动画转换 beforeRemove/afterAdd when foreach with computed observable
Animated transition beforeRemove/afterAdd when foreach with computed observable
我在尝试使用 jquery 动画时面临一些挑战,例如 fadeIn() fadeOut() with knockout。
实例,无动画:http://jsfiddle.net/LkqTU/23801/
我使用一个计算的可观察对象来过滤我原来的慈善机构数组。计算的数据与 foreach 绑定,我想让整个容器(使用 class .tab)在任何更改之前淡出,在更改之后淡入。
我已经尝试使用内置的 beforeRemove 和 afterAdd 属性,但是在计算我的数组时这似乎不起作用。正如下面的实例所示,容器被一些慈善机构的多个实例填满,即使底层计算数组只包含正确的数组。
现场示例,带有(失败的)动画:http://jsfiddle.net/fy7au6x6/1/
关于如何控制动画计算的更改时间的任何建议?
这是两个数组,"All charities"和"Charities filtered by category":
self.allCharities = ko.observableArray([
new Charity(0, "Amnesty International", ",466", "HUMANITARIAN"),
new Charity(1, "Richard Dawkins Foundation", "[=11=]", "EDUCATION"),
new Charity(2, "Khaaaan Academy", "13,859", "EDUCATION"),
new Charity(4, "Wikipedia", ",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);
与您问题的评论中给出的解决方案相反,我建议您不要在 viewmodel 方法中混合使用 DOM-handling 和 ViewModel 功能。通常,我建议避免做任何使视图模型依赖于 DOM.
的事情
当谈到 foreach 绑定的动画时,我首先建议创建 一个自定义绑定处理程序,它实际上会使用 foreach 绑定并添加您想要的动画。这样,您可以将与 DOM 相关的代码保留在视图或 bindingHandlers 中,它们应该在的位置。
在某些情况下,您可能不想为其创建自定义绑定,而只是希望动画方法可用于您的 foreach 绑定。在这些情况下,将这些方法放在视图模型上可能是一种更实用的方法。但是,如果您这样做,我建议您完全避免让视图模型功能依赖于这些方法,只保留它们以执行 DOM 动画逻辑。
鉴于这种方法,您的视图模型可能看起来类似于(复制 fiddle 中的视图模型,然后添加动画方法):
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", ",466", "HUMANITARIAN"),
new Charity(1, "Richard Dawkins Foundation", "[=10=]", "EDUCATION"),
new Charity(2, "Khaaaan Academy", "13,859", "EDUCATION"),
new Charity(4, "Wikipedia", ",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>
我已经用上面的代码更新了你的fiddle,你可以在http://jsfiddle.net/LkqTU/23825/找到它。
如果您希望创建多个实例,那么将这些方法添加到视图模型构造函数原型中也是一个好主意(以及更多 "correct")。
这里有一个使用自定义绑定处理程序的更 干净 的答案。
诀窍是使用一个布尔值,本质上说,"I am about to change"...当我们将其设置为 true 时,我们使用简单的绑定处理程序淡出。
过滤器处理完毕并准备就绪后,我们将相同的布尔值设置为 false,本质上说,"I am done"...我们的小处理程序会在发生这种情况时淡出。
诀窍是使用订阅和第二个可观察数组而不是计算数组。这允许您将布尔值设置为真,填充辅助可观察对象,然后将该可观察对象设置为假...这可以驱动淡入淡出行为,而不必担心绑定行为的时间。
Fiddle:
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", ",466", "HUMANITARIAN"),
new Charity(1, "Richard Dawkins Foundation", "[=11=]", "EDUCATION"),
new Charity(2, "Khaaaan Academy", "13,859", "EDUCATION"),
new Charity(4, "Wikipedia", ",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();
我在尝试使用 jquery 动画时面临一些挑战,例如 fadeIn() fadeOut() with knockout。
实例,无动画:http://jsfiddle.net/LkqTU/23801/
我使用一个计算的可观察对象来过滤我原来的慈善机构数组。计算的数据与 foreach 绑定,我想让整个容器(使用 class .tab)在任何更改之前淡出,在更改之后淡入。
我已经尝试使用内置的 beforeRemove 和 afterAdd 属性,但是在计算我的数组时这似乎不起作用。正如下面的实例所示,容器被一些慈善机构的多个实例填满,即使底层计算数组只包含正确的数组。
现场示例,带有(失败的)动画:http://jsfiddle.net/fy7au6x6/1/
关于如何控制动画计算的更改时间的任何建议?
这是两个数组,"All charities"和"Charities filtered by category":
self.allCharities = ko.observableArray([
new Charity(0, "Amnesty International", ",466", "HUMANITARIAN"),
new Charity(1, "Richard Dawkins Foundation", "[=11=]", "EDUCATION"),
new Charity(2, "Khaaaan Academy", "13,859", "EDUCATION"),
new Charity(4, "Wikipedia", ",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);
与您问题的评论中给出的解决方案相反,我建议您不要在 viewmodel 方法中混合使用 DOM-handling 和 ViewModel 功能。通常,我建议避免做任何使视图模型依赖于 DOM.
的事情当谈到 foreach 绑定的动画时,我首先建议创建 一个自定义绑定处理程序,它实际上会使用 foreach 绑定并添加您想要的动画。这样,您可以将与 DOM 相关的代码保留在视图或 bindingHandlers 中,它们应该在的位置。
在某些情况下,您可能不想为其创建自定义绑定,而只是希望动画方法可用于您的 foreach 绑定。在这些情况下,将这些方法放在视图模型上可能是一种更实用的方法。但是,如果您这样做,我建议您完全避免让视图模型功能依赖于这些方法,只保留它们以执行 DOM 动画逻辑。
鉴于这种方法,您的视图模型可能看起来类似于(复制 fiddle 中的视图模型,然后添加动画方法):
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", ",466", "HUMANITARIAN"),
new Charity(1, "Richard Dawkins Foundation", "[=10=]", "EDUCATION"),
new Charity(2, "Khaaaan Academy", "13,859", "EDUCATION"),
new Charity(4, "Wikipedia", ",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>
我已经用上面的代码更新了你的fiddle,你可以在http://jsfiddle.net/LkqTU/23825/找到它。
如果您希望创建多个实例,那么将这些方法添加到视图模型构造函数原型中也是一个好主意(以及更多 "correct")。
这里有一个使用自定义绑定处理程序的更 干净 的答案。
诀窍是使用一个布尔值,本质上说,"I am about to change"...当我们将其设置为 true 时,我们使用简单的绑定处理程序淡出。
过滤器处理完毕并准备就绪后,我们将相同的布尔值设置为 false,本质上说,"I am done"...我们的小处理程序会在发生这种情况时淡出。
诀窍是使用订阅和第二个可观察数组而不是计算数组。这允许您将布尔值设置为真,填充辅助可观察对象,然后将该可观察对象设置为假...这可以驱动淡入淡出行为,而不必担心绑定行为的时间。
Fiddle:
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", ",466", "HUMANITARIAN"),
new Charity(1, "Richard Dawkins Foundation", "[=11=]", "EDUCATION"),
new Charity(2, "Khaaaan Academy", "13,859", "EDUCATION"),
new Charity(4, "Wikipedia", ",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();