7.7 动画插件
整个tile插件基本上就算完成了,但是还差一个动画部分。Win8上经常有一些这样的效果:磁贴片里的内容有规律地滚动,比如向上逐条显示新闻或邮件。本节,我们就将一步一步实现这些效果。
先分析一下该插件都需要设置什么内容。由于该插件和旋转轮播效果类似,所以通过回顾轮播插件,可以分析到,需要以下几个方面的内容:
❑可以在上下左右4个方向进行秩序滚动,如果能有左右(或上下)弹球式滚动就更好了,要求能在声明式用法里设置滚动效果,比如slideLeft、slideUp等。
❑要求能设置滚动时间、滚动以后的暂停时间。
❑是否能支持更炫的动画特效。
经过考虑,我们发现前两点比较容易实现,第3点的更炫特效由于已经偏离了本书的内容,所以只能利用第三方的jQuery的easing插件进行调用,也就是暴露一个接口,让开发人员自行选择。
首先要考虑一下,多个滚动的内容如何定义。前面我们在容器内定义了一个主区域内容tile-content,可以在里面连续放置多个充满tile-content元素的子元素用于滚动。可是,再设置一个层级的子元素,又要处理很多相关的CSS样式,这里,就大胆使用多个tile-content元素试试,此时只需要显示一个tile-content,其他的tile-content都默认隐藏即可,因为其他的tile-content可以通过tile动画插件来显示。具体结构如下:
- <div class="tile double bg-amber" data-toggle="tile" data-direction="slideLeftRight"
- data-period="700" data-duration="3000" data-easing="doubleSqrt">
- <div class="tile-content bg-red">a区</div>
- <div class="tile-content bg-green">b区</div>
- <div class="tile-content bg-orange ">c 区</div>
- <div class="tile-content bg-grayDark">d区 </div>
- </div>
这里,除了在tile元素放置多个tile-content子容器以外,还定义了5个参数,分别表示:插件标识、滚动方向、单个tile-content滚动时间、滚动结束后的停留时间、动画过渡效果。
此时,我们需要解决一个问题,那就是在一个tile里,默认只能显示一个tile-content容器,这个内容可以使用CSS来控制,默认设置只显示第一个tile-content容器(此时,也不影响原先定义的CSS样式)。具体代码如下:
- .tile .tile-content { display: none;}
- .tile .tile-content:first-child { display: block;}
声明式布局定义好以后,就开始定义插件内容。
步骤1 立即调用的函数。
- // 1.定义立即调用的函数
- +function ($) {
- "use strict"; // 使用严格模式 ES5支持
- // 后续步骤
- // 2.tile插件类及原型方法的定义
- // 3.在jQuery上定义tile插件,并重设插件构造器
- // 并重设插件构造器,可以通过该属性获取插件的真实类函数
- // 4. 防冲突处理
- // 5. 绑定触发事件
- }(window.jQuery);
步骤2 插件核心代码。定义基础的Tile类函数和相关原型方法,Tile类函数主要是接收options值、设置各种默认值等。
Tile类函数的定义代码如下:
- // Tile类函数定义
- var Tile = function (element, options) {
- this.$element = $(element); // 容器元素
- this.options = options;
- // 插件运行参数,根据初始化代码,优先级最高的是所单击元素上的data-属性,
- // 然后是容器上的data-属性,最后才是默认值
- this.frames = this.$element.children(".tile-content");
- // 查找有多少个主区域需要滚动
- this.currentIndex = 0; // 当前所显示主区域的索引
- this.interval = 0; // 触发设置
- this.size = { // 获取当前磁贴的高度和宽度
- 'width': this.$element.width(),
- 'height': this.$element.height()
- };
- // 确保默认的参数都是正常传值,如果是undefined,就使用默认值
- if (this.options.direction == undefined) { this.options.direction =
- Tile.DEFAULTS.direction; }
- if (this.options.period == undefined) { this.options.period =
- Tile.DEFAULTS.period; }
- if (this.options.duration == undefined) { this.options.duration =
- Tile.DEFAULTS.duration; }
- if (this.options.easing == undefined) { this.options.easing =
- Tile.DEFAULTS.easing; }
- // 定义一个默认的动画过渡效果,可以使用jQuery的easing插件
- $.easing.doubleSqrt = function (t) { return Math.sqrt(Math.sqrt(t)); };
- }
- // 默认值定义
- Tile.DEFAULTS = {
- direction: 'slideLeft', // 默认滚动方向
- period: 3000, // 默认暂停间隔
- duration: 700, // 默认滚动时间
- easing: 'doubleSqrt' // 默认动画过渡效果
- }
关于原型方法,能公开进行调用的,我们只暴露start和pause,其他的一些原型方法都是用于处理滚动效果的,先来看一下核心摘要代码:
- // 启动执行动画
- Tile.prototype.start = function () {
- var that = this;
- this.interval = setInterval(function () {
- that.animate();
- }, that.options.period);
- }
- // 暂停动画
- Tile.prototype.pause = function () {
- var that = this;
- clearInterval(that.interval);
- }
- // 动画处理入口,再分别调用各自方向的动画处理效果
- Tile.prototype.animate = function () {
- var that = this;
- var currentFrame = this.frames[this.currentIndex], nextFrame;
- this.currentIndex += 1;
- if (this.currentIndex >= this.frames.length) this.currentIndex = 0;
- nextFrame = this.frames[this.currentIndex];
- // 根据滚动方向,分别调用相应的内部方法,参数是:
- // 当前要滚动的tile-content、下一个要滚动的tile-content
- switch (this.options.direction) {
- case 'slideLeft': this.slideLeft(currentFrame, nextFrame); break;
- case 'slideRight': this.slideRight(currentFrame, nextFrame); break;
- case 'slideDown': this.slideDown(currentFrame, nextFrame); break;
- case 'slideUpDown': this.slideUpDown(currentFrame, nextFrame); break;
- case 'slideLeftRight': this.slideLeftRight(currentFrame, nextFrame); break;
- default: this.slideUp(currentFrame, nextFrame);
- }
- }
- // 左右来回滚动效果
- Tile.prototype.slideLeftRight = function (currentFrame, nextFrame) {…}
- // 上下来回滚动效果
- Tile.prototype.slideUpDown = function (currentFrame, nextFrame) {…}
- // 一直向上滚动效果
- Tile.prototype.slideUp = function (currentFrame, nextFrame) {…}
- // 一直向下滚动效果
- Tile.prototype.slideDown = function (currentFrame, nextFrame) {…}
- // 一直向左滚动效果
- Tile.prototype.slideLeft = function (currentFrame, nextFrame) {…}
- // 一直向右滚动效果
- Tile.prototype.slideRight = function (currentFrame, nextFrame) {…}
这里,在start里使用了setInterval函数来触发动画执行函数,而在pause里使用了clearInterval函数清除动画执行函数。其他方面都很好理解了,看注释即可明白。
而关于4个方向的滚动效果则更简单,主要就是利用jQuery的animate函数将原有的tile-content移出后,再将下一个tile-content移入并显示。代码如下:
- // 左右来回滚动效果
- Tile.prototype.slideLeftRight = function (currentFrame, nextFrame) {
- if (this.currentIndex % 2 == 1) // 用奇偶数来决定滚动方向
- this.slideLeft(currentFrame, nextFrame);
- else
- this.slideRight(currentFrame, nextFrame);
- }
- // 上下来回滚动效果
- Tile.prototype.slideUpDown = function (currentFrame, nextFrame) {
- if (this.currentIndex % 2 == 1) // 用奇偶数来决定滚动方向
- this.slideUp(currentFrame, nextFrame);
- else
- this.slideDown(currentFrame, nextFrame);
- }
- // 一直向上滚动效果
- Tile.prototype.slideUp = function (currentFrame, nextFrame) {
- var move = this.size.height;
- var options = {
- 'duration': this.options.duration,
- 'easing': this.options.easing
- };
- $(currentFrame).animate({ top: -move }, options);
- $(nextFrame)
- .css({ top: move })
- .show()
- .animate({ top: 0 }, options);
- }
- // 一直向下滚动效果
- Tile.prototype.slideDown = function (currentFrame, nextFrame) {
- var move = this.size.height;
- var options = {
- 'duration': this.options.duration,
- 'easing': this.options.easing
- };
- $(currentFrame).animate({ top: move }, options);
- $(nextFrame)
- .css({ top: -move })
- .show()
- .animate({ top: 0 }, options);
- }
- // 一直向左滚动效果
- Tile.prototype.slideLeft = function (currentFrame, nextFrame) {
- var move = this.size.width;
- var options = {
- 'duration': this.options.duration,
- 'easing': this.options.easing
- };
- $(currentFrame).animate({ left: -move }, options);
- $(nextFrame)
- .css({ left: move })
- .show()
- .animate({ left: 0 }, options);
- }
- // 一直向右滚动效果
- Tile.prototype.slideRight = function (currentFrame, nextFrame) {
- var move = this.size.width;
- var options = {
- 'duration': this.options.duration,
- 'easing': this.options.easing
- };
- $(currentFrame).animate({ left: move }, options);
- $(nextFrame)
- .css({ left: -move })
- .show()
- .animate({ left: 0 }, options);
- }
步骤3 jQuery插件定义。在jQuery上定义$.fn.tile插件,有点特殊的代码是options参数的收集和合并,主要收集了3部分:插件的默认参数Defaults、modal元素上的data-属性、执行插件时候传入的option对象。三者的优先级依次升高。源码如下:
- // Tile插件定义
- var old = $.fn.tile;
- // 保留其他库的$.fn.tile代码(如果定义的话),以便在noConflict之后,可以继续使用该老代码
- $.fn.tile = function (option) {
- return this.each(function () { // 遍历所有符合规则的元素
- var $this = $(this) // 当前触发元素的jQuery对象
- var data = $this.data('bs.tile')
- // 获取自定义属性data-bs.tile的值(其实是tile实例)
- var options = $.extend({}, Tile.DEFAULTS, $this.data(),
- typeof option == 'object' && option)
- // 合并参数,优先级依次递增
- if (!data) $this.data('bs.tile', (data = new Tile(this, options)))
- // 如果没有tile实例,就初始化一个,并传入this和参数
- option === 'pause' ? data.pause() : data.start();
- })
- }
步骤4 防冲突处理。防冲突处理的源码如下:
- $.fn.tile.Constructor = Tile; // 并重设插件构造器,可以通过该属性获取插件的真实类函数
- // Tile 防冲突
- $.fn.tile.noConflict = function () {
- $.fn.tile = old
- return this
- }
上述代码非常简单了,可以像其他插件一样接收option参数(参数里可以设置滚动方向、时间等)。唯一一个需要注意的就是,默认的执行方式只提供了start和pause,而且,只有在option参数是pause字符串的时候才执行pause方法。否则一律执行start方法。大家在进行JavaScript调用的时候需要注意一下。
步骤5 绑定触发事件。DATA-API绑定触发事件则更简单了,由于不需要绑定相关的click事件,只需要在data-toggle="tile"属性的tile元素上执行tile插件即可。
- // Tile DATA-API
- // 由于不需要click相关的时间,所以这里只是简单触发tile插件
- $(window).on('load', function () {
- $('[data-toggle="tile"]').each(function () { // 遍历所有符合规则的元素
- $(this).tile(); // 实例化插件以便自动运行
- })
- })
一般情况下,该插件使用声明式用法即可。关于插件的JavaScript用法,我们来举两个例子。代码如下:
- // 触发所有的tile磁贴
- $(function () {
- $(".tile").tile();
- });
- // 对特定磁贴启用特殊参数
- $(function () {
- $("#firstTile").tile({
- direction: "slideRight",
- duration: 700
- });
- });
如果需要在其他元素上作用单击事件,来手动触发tile插件的滚动效果,则利用现有插件的实例,在停止的时候传入pause参数即可。代码如下:
- // 通过开始和暂停按钮手动触发磁贴动画
- $(function () {
- $("#btnStart").click(function () {
- $("#firstTile").tile({
- direction: "slideRight",
- duration: 700
- });
- });
- $("#btnPause").click(function () {
- $("#firstTile").tile('pause');
- });
- });