Skip to content

Latest commit

 

History

History
105 lines (70 loc) · 7.71 KB

08.md

File metadata and controls

105 lines (70 loc) · 7.71 KB

八、制作 knockout 动画

knockout. js 是不是一个动画库。每当底层数据发生变化时,所有 knockout. js 的自动更新都会立即应用于*。为了动画化它的任何变化,我们需要深入到敲除. js 的内部,并使用另一个像 jQuery 或 MooTools 这样的 JavaScript 框架手动创建动画化的转换。本章坚持使用 jQuery 的动画例程,但是所展示的概念也适用于其他动画库。*

*## 购物车的返回

在本章中,我们将回到购物车示例的简化版本。用以下内容创建一个新的 HTML 文件。我们不会发出任何 AJAX 请求,所以请随意将它放在您计算机的任何地方。然而,我们将使用 jQuery 的动画例程,所以一定要包含一个到 jQuery 库副本的链接。

    <html lang='en'> <head>   <title>Animating Knockout.js</title>   <meta charset='utf-8' />   <link rel='stylesheet' href='style.css' /> </head> <body>
      <h1>Animating Knockout.js</h1>
      <table>
        <thead><tr>
          <th>Product</th>
          <th>Price</th>
          <th></th>
        </tr></thead>
        <tbody data-bind='foreach: items'>
          <tr>
            <td data-bind='text: name'></td>
            <td data-bind='text: price'></td>
            <td><button data-bind='click: $root.removeProduct'>Remove</button></td>
          </tr>
        </tbody>
      </table>

      <button data-bind='click: addProduct'>Add Beer</button>

      <script type='text/javascript' src='knockout-2.1.0.js'></script>
      <script src='jquery-1.7.2.js'></script>
      <script type='text/javascript'>     function Product(name, price, tags, discount, details) {       this.name = ko.observable(name);       this.price = ko.observable(price);     }     function ShoppingCart() {       var self = this;       this.instructions = ko.observable("");       this.hasInstructions = ko.observable(false);              this.items = ko.observableArray([         new Product("Beer", 10.99),         new Product("Brats", 7.99),         new Product("Buns", 1.49)       ]);              this.addProduct = function() {         this.items.push(new Product("More Beer", 10.99));       };
           this.removeProduct = function(product) {         self.items.destroy(product);       };            };     ko.applyBindings(new ShoppingCart());   </script> </body> </html>

希望现在这些都是回顾。我们有一个包含一堆产品的可观测数组,一个显示每个产品的foreach绑定,以及一个向购物车添加更多商品的按钮。

列表回调

gentle . js 本身是一个强大的用户界面库,但是一旦您将其与 jQuery 或 MooTools 等框架的动画功能相结合,您就可以用最少的标记创建真正令人惊叹的 ui。首先,我们将看一下动画列表,然后下一节介绍一种更通用的动画视图组件的方法。

foreach绑定有两个名为beforeRemoveafterAdd的回调。这些功能分别在项目从列表中移除之前或添加到列表之后执行。这给了我们一个机会,在敲除操作 DOM 之前制作每个项目的动画。向<tbody>元素添加回调,如下所示:

        <tbody data-bind='foreach: {data: items,       beforeRemove: hideProduct,       afterAdd: showProduct}'>

现在,我们的foreach绑定不再是一个属性,而是将一个对象文字作为其参数。参数的data属性指向您想要渲染的数组,beforeRemoveafterAdd属性指向所需的回调函数。接下来,我们应该在ShoppingCart视图模型上定义这些回调:

          this.showProduct = function(element) {         if (element.nodeType === 1) {           $(element).hide().fadeIn();         }       };
           this.hideProduct = function(element) {         if (element.nodeType === 1) {          $(element).fadeOut(function() { $(element).remove(); });         }       };

showProduct()回调使用 jQuery 让新列表项逐渐淡入,hideProduct()回调淡出,然后从 DOM 中移除。这两个函数都将受影响的 DOM 元素作为它们的第一个参数(在本例中,它是一个<tr>元素)。条件语句确保我们使用的是完整的元素,而不仅仅是文本节点。

最终的结果应该是列出那些可以平稳地进出列表的项目。当然,您可以自由使用 jQuery 的任何其他转换,或者在任一回调中执行自定义后处理。

自定义绑定

foreach回调对于动画列表非常有用,但是不幸的是其他绑定不提供这个功能。因此,如果我们想要动画用户界面的其他部分,我们必须创建自定义绑定,将动画嵌入其中。

自定义绑定的工作方式就像敲除. js 的默认绑定一样。例如,考虑以下表单域:

      <div>
        <p>
          <input data-bind='checked: hasInstructions'              type='checkbox' />
          Requires special handling instructions
        </p>
        <div>
          <textarea data-bind='visible: hasInstructions,                            value: instructions'>
          </textarea>
        </div>
      </div>

该复选框充当了<textarea>的切换开关,但是由于我们使用的是visible绑定,knockout. js 会突然在 DOM 中添加或删除它。为了给<textarea>提供一个平稳的过渡,我们将创建一个名为visibleFade的自定义绑定:

          <textarea data-bind='visibleFade: hasInstructions,                            value: instructions'>

当然,在我们将自定义绑定添加到 knockout. js 之前,这是行不通的,我们可以通过将定义绑定的对象添加到ko.bindingHandlers来实现,如下面的代码示例所示。这也恰好是定义所有内置绑定的地方。

        ko.bindingHandlers.visibleFade = {       init: function(element, valueAccessor) {         var value = valueAccessor();         $(element).toggle(value());       },       update: function(element, valueAccessor) {         var value = valueAccessor();         value() ? $(element).fadeIn() : $(element).fadeOut();       }     }

init属性指定了第一次遇到绑定时要调用的函数。这个回调应该定义视图组件的初始状态,并执行必要的设置操作(例如,注册事件侦听器)。对于visibleFade,我们所要做的就是根据视图模型的状态显示或隐藏元素。我们使用 jQuery 的toggle()方法实现了这一点。

element参数是被绑定的 DOM 元素,valueAccessor是将返回所讨论的视图模型属性的函数。在我们的示例中,element引用了<textarea>,而valueAccessor()返回了对hasInstructions的引用。

update属性指定了一个函数,每当相关的可观测到的变化时执行,我们的回调使用hasInstructions的值将<textarea>转换到适当的方向。请记住,您需要调用可观测值来获取其当前值(即value(),而不是value)。然而,如果hasInstructions是一个普通的 JavaScript 属性而不是一个可观测的属性,情况就不是这样了。

摘要

在这一章中,我们发现了两种制作 Quicken . js 视图组件动画的方法。首先,我们向foreach绑定添加了回调方法,这让我们可以将项目的添加和移除委托给用户定义的函数。这让我们有机会将 jQuery 的动画转换集成到我们的 knockout. js 模板中。然后,我们探索了自定义绑定作为动画任意元素的手段。

本章介绍了自定义绑定的一个常见用例,但它们绝不仅限于动画用户界面组件。自定义绑定还可以用来过滤收集的数据,监听自定义事件,或者创建可重用的小部件,如网格和分页内容。如果你能把一个行为封装成一个init和一个update函数,你就能把它变成一个自定义绑定。*