敲除绑定中的同步动画

时间:2015-02-14 01:47:10

标签: javascript knockout.js jquery-animate

我希望在某些属性随我的挖空模型(特别是移动)而改变时应用动画。我需要这些动画是同步的,如果有多个动画会让用户感到非常困惑。

我想使用敲除自定义绑定来执行此操作,因为它应该使我的代码更容易理解,但如果我这样做,我无法为jquery动画提供回调函数。我知道由于javascript的限制,我不能拥有真正的同步行为,但我无法弄清楚如何伪造它。

我想要的行为: http://jsfiddle.net/3fLvpxLc/2/

$("#e1").animate({left: 50}, "slow", function() {
    // more animations
}

具有同步问题的版本: http://jsfiddle.net/hrwsd1z3/1/

ko.bindingHandlers.position = {
    update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        var value = valueAccessor();
        var valueUnwrapped = ko.unwrap(value);
        $(element).animate({left: valueUnwrapped}, "slow");
    }
}

1 个答案:

答案 0 :(得分:2)

jQuery queues是你的朋友。使用它们,您可以序列化异步动画。

通常它们会隐式用于您对一个元素执行的所有动画效果,即绑定到动画元素本身

$("element").show("slow").animate({left: 25});

但你也可以明确地使用它们。 queue向队列添加动画,next回调将下一个动画队列出来(您可以方便地将其作为complete处理程序传递)。这样你可以将动画绑定到不同的元素而不是动画元素:

$("#container").queue(function (next) {
    $("element").show("slow", next);
}
$("#container").queue(function (next) {
    $("element").animate({left: 25}, next);
}

凭借这些知识,任务变得简单:



ko.bindingHandlers.syncPosition = {
    init: function(element, valueAccessor) {
        var newPosition = ko.toJS(valueAccessor());

        // set element to its initial position
        $(element).css(newPosition);
    },
    update: function(element, valueAccessor) {
        var newPosition = ko.toJS(valueAccessor());

        // queue position update as animation to a common element, e.g. the body
        $(document.body).queue(function( next ) {
            $(element).animate(newPosition, "slow", next);
        });
    }
};

function Item(id, top, left) {
    this.id = ko.observable(id);
    this.position = {
         top: ko.observable(top),
         left: ko.observable(left)
    };
}
function VM(params) {
    var self = this;
    
    self.elements = ko.observableArray([
        new Item("e1"),
        new Item("e2"),
        new Item("e3")
    ]);
}

var vm = new VM();
ko.applyBindings(vm);

vm.elements()[0].position.left(50);
vm.elements()[1].position.left(75);
vm.elements()[2].position.left(25);
vm.elements()[1].position.left(125);
vm.elements()[2].position.top(10);
vm.elements()[1].position.top(20);
vm.elements()[0].position.top(30);
vm.elements()[0].position.left(0);
vm.elements()[1].position.left(0);
vm.elements()[2].position.left(0);

div.container {
    position: relative;
}
div.container > div {
    width: 20px;
    height: 20px;
    position: absolute;
}
#e1 {
    background-color: blue;
}
#e2 {
    background-color: red;
}
#e3 {
    background-color: green;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<div class="container" data-bind="foreach: elements">
    <div data-bind="syncPosition: position, attr: {id: id}"></div>
</div>
&#13;
&#13;
&#13;