2

我在尝试使用 jquery 动画时遇到了一些挑战,例如 fadeIn() fadeOut() 和淘汰赛。

现场示例,没有动画: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);
4

2 回答 2

2

与对您的问题的评论中给出的解决方案相反,我建议您不要在 viewmodel 方法中混合 DOM 处理和 ViewModel 功能。一般来说,我建议避免做任何使视​​图模型依赖于 DOM 的事情。

当谈到 foreach-binding 的动画时,我首先建议创建一个自定义 bindingHandler ,它实际上会使用 foreach-binding 并添加你想要的动画。这样,您可以将与 DOM 相关的代码保留在视图或 bindingHandlers 中,它们应该在哪里。

在某些情况下,您可能不想为其创建自定义绑定,而只是希望动画方法可用于您的 foreach 绑定。在这些情况下,将这些方法放在视图模型上可能是一种更实用的方法。但是,如果您这样做,我建议您完全避免让您的视图模型功能依赖于这些方法,只保留它们用于执行 DOM 动画逻辑。

鉴于这种方法,您的视图模型可能看起来类似于(复制小提琴中的视图模型,然后添加动画方法):

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 构造函数原型中也可能是一个好主意(并且更“正确”)。

于 2015-04-20T09:03:44.240 回答
1

这是使用自定义绑定处理程序的更清晰的答案。

诀窍是使用一个布尔值,本质上说,“我即将改变”......当我们将其设置为 true 时,我们会使用简单的绑定处理程序淡出。

一旦过滤器处理完毕并准备就绪,我们将相同的布尔值设置为 false,这实际上表示“我完成了”......当这种情况发生时,我们的小处理程序会淡入。

诀窍是使用订阅和第二个可观察数组而不是计算数组。这允许您将布尔值设置为 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();
于 2015-04-20T19:09:01.427 回答