我正面临一些尝试使用jquery动画的挑战,例如fadeIn()fadeOut()with knockout。
实例,没有动画:http://jsfiddle.net/LkqTU/23801/
我使用计算的observable来过滤我原来的慈善数组。计算是与foreach数据绑定的,我想让整个容器(带有类.tab)在任何更改之前淡出,并在更改之后淡出。
我尝试过使用内置的beforeRemove和afterAdd属性,但是在计算数组时这似乎不起作用。如下面的实例中所示,容器被一些慈善机构的几个实例填满,即使底层的计算数组只包含正确的数组。
实例,包含(失败)动画:http://jsfiddle.net/fy7au6x6/1/
有关如何控制使用动画计算的更改时间的任何建议吗?
这是两个阵列,"所有慈善机构"和"按类别"过滤的慈善机构:
self.allCharities = ko.observableArray([
new Charity(0, "Amnesty International", "$2,466", "HUMANITARIAN"),
new Charity(1, "Richard Dawkins Foundation", "$0", "EDUCATION"),
new Charity(2, "Khaaaan Academy", "13,859", "EDUCATION"),
new Charity(4, "Wikipedia", "$7,239", "EDUCATION")
]);
self.filteredCharities = ko.computed(function () {
// If no category is selected, return all charities
if (!self.selectedCategory())
return self.allCharities();
// Return charities in the selected category
return ko.utils.arrayFilter(self.allCharities(), function (c) {
return (c.Category() == self.selectedCategory());
});
}, this);
答案 0 :(得分:2)
与您对问题的评论中提供的解决方案相反,我建议您不要在viewmodel方法中混合使用DOM处理和ViewModel功能。一般来说,我建议避免做任何使视图模型依赖于DOM的事情。
当谈到foreach-binding的动画时,我首先会建议创建一个自定义bindingHandler,它实际上会使用foreach-binding并添加你想要的动画。这样,您可以将DOM相关代码保留在视图中,或者绑定到它们应该的bindingHandlers中。
在某些情况下,您可能不希望为其创建自定义绑定,但只希望您的foreach绑定可以使用动画方法。在这些情况下,将这些方法放在viewmodel上可能是一种更实用的方法。但是,如果你这样做,我建议你根据这些方法避免使用你的viewmodel功能,只保留它们来做DOM动画逻辑。
鉴于这种方法,您的viewmodel可能看起来类似于(复制小提琴中的那个,然后添加动画方法):
function ViewModel() {
var self = this;
self.selectedCategory = ko.observable("");
self.setCategory = function (newCat) {
self.selectedCategory(newCat);
};
self.allCharities = ko.observableArray([
new Charity(0, "Amnesty International", "$2,466", "HUMANITARIAN"),
new Charity(1, "Richard Dawkins Foundation", "$0", "EDUCATION"),
new Charity(2, "Khaaaan Academy", "13,859", "EDUCATION"),
new Charity(4, "Wikipedia", "$7,239", "EDUCATION")
]);
self.filteredCharities = ko.computed(function () {
// If no category is selected, return all charities
if (!self.selectedCategory())
return self.allCharities();
// Return charities in the selected category
return ko.utils.arrayFilter(self.allCharities(), function (c) {
return (c.Category() == self.selectedCategory());
});
}, this);
var fadeAnimationDuration = 500;
self.animationAfterAddingCharityElementsToDom = function(element){
//Since this method will be depending on the DOM, avoid having
//the viewmodel functionality depending on this method
//First hide the new element
var $categoryDomElement = $(element);
$categoryDomElement.hide();
var $tabDomElement = $categoryDomElement.parent();
$tabDomElement.fadeOut(fadeAnimationDuration, function(){
//When the tab has faded out, show the new element and then fade the tab back in
$categoryDomElement.show();
$tabDomElement.fadeIn(fadeAnimationDuration);
});
};
self.animationBeforeRemovingCharityElementsFromDom = function(element){
//Since this method will be depending on the DOM, avoid having
//the viewmodel functionality depending on this method
var $categoryDomElement = $(element);
var $tabDomElement = $categoryDomElement.parent();
$tabDomElement.fadeOut(fadeAnimationDuration, function(){
//When the tab has faded out, remove the element and then fade the tab back in
$categoryDomElement.remove();
$tabDomElement.fadeIn(fadeAnimationDuration);
});
};
};
然后你的绑定就是:
<div class="tab" data-bind="foreach: { data: filteredCharities, afterAdd: animationAfterAddingCharityElementsToDom, beforeRemove: animationBeforeRemovingCharityElementsFromDom }">
<div class="tab-tile mb21" data-bind="css:{'mr21':$index()%3 < 2}">
<a href="#" class="amount" data-bind="text: Amount"></a>
<a href="#" class="title" data-bind="text: Name"></a>
<a href="#" class="category" data-bind="text: Category"></a>
</div>
</div>
我已使用上述代码更新了您的小提琴,您可以在http://jsfiddle.net/LkqTU/23825/找到它。
如果您希望自己创建多个实例,那么将这些方法添加到viewmodel构造函数原型中也是一个好主意(以及更多&#34;正确&#34;)。
答案 1 :(得分:1)
这是使用自定义绑定处理程序的更多 clean 答案。
诀窍是使用一个布尔表示,基本上,“我即将改变”......当我们将其设置为true时,我们会使用简单的绑定处理程序淡出。
一旦过滤器处理完毕并准备就绪,我们将相同的布尔值设置为false,这实际上就是说“我已经完成了......”当发生这种情况时,我们的小处理程序会淡入。
诀窍是使用订阅和第二个可观察数组而不是计算数组。这允许你将boolean设置为true,填充辅助observable,然后将observable设置为false ...这可以驱动淡入淡出行为,而不必担心绑定行为的时间。
<强>小提琴:强>
http://jsfiddle.net/brettwgreen/h9m5wb8k/
<强> HTML:强>
<div class="side-bar">
<a href="#" class="category" data-bind="click: function(){ setCategory('')}">All</a>
<a href="#" class="category" data-bind="click: function(){ setCategory('EDUCATION')}">Education</a>
<a href="#" class="category" data-bind="click: function(){ setCategory('HUMANITARIAN')}">Humanitarian</a>
</div>
<div class="tab" data-bind="fader: filtering, foreach: filteredCharities">
<div class="tab-tile mb21" data-bind="css:{'mr21':$index()%3 < 2}">
<a href="#" class="amount" data-bind="text: Amount"></a>
<a href="#" class="title" data-bind="text: Name"></a>
<a href="#" class="category" data-bind="text: Category"></a>
</div>
</div>
<强> JS:强>
ko.bindingHandlers.fader = {
update: function(element, valueAccessor) {
var obs = valueAccessor();
var val = ko.unwrap(obs);
if (val) {
$(element).fadeOut(500);
}
else
{
$(element).fadeIn(500);
}
}
};
function Charity(id, name, amount, category) {
var self = this;
self.Id = ko.observable(id);
self.Name = ko.observable(name);
self.Amount = ko.observable(amount);
self.Category = ko.observable(category);
}
// ----------------------------------------------------------
// VIEWMODEL ------------------------------------------------
// ----------------------------------------------------------
function ViewModel() {
var self = this;
self.selectedCategory = ko.observable("");
self.filtering = ko.observable(false);
self.setCategory = function (newCat) {
self.filtering(true);
window.setTimeout(function() {self.selectedCategory(newCat);}, 500);
};
self.allCharities = ko.observableArray([
new Charity(0, "Amnesty International", "$2,466", "HUMANITARIAN"),
new Charity(1, "Richard Dawkins Foundation", "$0", "EDUCATION"),
new Charity(2, "Khaaaan Academy", "13,859", "EDUCATION"),
new Charity(4, "Wikipedia", "$7,239", "EDUCATION")
]);
self.filteredCharities = ko.observableArray(self.allCharities());
self.selectedCategory.subscribe(function(newValue) {
self.filtering(true);
console.log(newValue);
if (!newValue)
self.filteredCharities(self.allCharities());
else {
var fChars = ko.utils.arrayFilter(self.allCharities(), function (c) {
return (c.Category() === newValue);
});
self.filteredCharities(fChars);
};
self.filtering(false);
});
};
// ----------------------------------------------------------
// DOCUMENT READY FUNCTION ----------------------------------
// ----------------------------------------------------------
$(document).ready(function () {
ko.applyBindings(vm);
});
var vm = new ViewModel();