move ---- 编写一个包含几种常用动画的js库

在操作dom元素的时候为了让网站显得更有活力或者某些想让人注意到, 经常需要用到一些小动画, 但常用的 jquery 库只有一种ease(先加速后减速的动画)运动方式的动画, 虽然这是很常用的动画, 但有时也会用到其他的, 最近写了一个集成几种常用动画的库, move.js , 如果不是走动画队列的话, 通常的动画库在进行一个动画的时候, 在对元素施加另一个动画就会马上停止当前动画, 马上执行新添加的动画, move动画库稍微修改了一下, 在新动画添加之后, 老动画还会继续跑, 两个动画会进行叠加. 你可以在demo演示中疯狂的点击start, demo演示

四种常见动画:

  • ease       ---- 先加速后减速动画, 初速度较小开始加速, 过了中点就减速, 这也是jquery默认动画
  • easeOut  ---- 初速度较大, 一直做减速运动
  • collision  ---- 碰撞动画, 初速度较小或为0, 一直加速, 碰撞终点的时候反弹, 就像篮球落地
  • elastic    ---- 弹性动画, 初速度较大, 一直做加速度减小的加速运动, 到达终点位置加速度为0(因为设置了阻力,所以加速度为0的地方在终点之前), 来回摆动

动画曲线:

  有了动画曲线, 这几种动画其实只是一道简单的高一物理题, 只需要特别注意一下终点位置像素偏差如何处理.

终点位置处理

  因为使用的定时器做动画, 时间间隔是13ms, 因为jquery也是13ms, 我测试了13ms也可以得到比较平滑的效果, 但因为浏览器刷新时间间隔是16.7ms, 我的理解是需要3ms多进行dom渲染, 这点不太确定, 忘知道的朋友指点.    因为每隔13ms里目标值接近几个像素, 那么如果在接近终点的时候, 如果定时器上一次设置得值离目标1px, 那么下一次可能跳过目标, 因此会做位置判断, 如果速度>0, 并且当前位置已经>=目标值, 或者速度<0,当前位置<=目标值, 就让当前位置在目标位置.

代码如下:

var move = {
    css: function(obj, attr){
        if( typeof attr === "string" ){
            return obj.currentStyle ?obj.currentStyle[attr] : window.getComputedStyle(obj, false)[attr];
        }
        else if( typeof attr === "object" ){
            var a;
            for(a in attr){
                switch(a){
                    case "width":
                    case "height":
                    case "left":
                    case "top":
                    case "right":
                    case "padding":
                    case "paddingLeft":
                    case "paddingRight":
                    case "paddingTop":
                    case "paddingBottom":
                    case "margin":
                    case "marginLeft":
                    case "marginRight":
                    case "marginTop":
                    case "marginBottom":
                    case "borderRadius":
                    case "borderWidth":
                        if( typeof attr[a] === "number" )    obj.style[a] = attr[a] + ‘px‘;
                        else    obj.style[a] = attr[a];
                        break;
                    case "opacity":
                        if( +attr[a] < 0 ) attr[a] = 0;
                        obj.style.filter = "alpha(opacity="+ attr[a]*100 +")";
                        obj.style.opacity = attr[a];
                        break;
                    default:
                        obj.style[a] = attr[a];
                }
            }
        }
    },
    init: function(obj, json, time){
        if( !obj.ani ){
            obj.ani = {};                //动画对象
            obj.ani.s0 = {},             //当前值
            obj.ani.st = {},             //目标值
            obj.ani.dis = {},            //目标值和起始值距离
            obj.ani.va = {},             //平均速度
            obj.ani.v = {},              //初始速度,当前速度
            obj.ani.a = {},              //加速度
            obj.ani.d = {},              //t时间段内的位移
            obj.ani.res = {};            //此刻的结果
        }

        obj.aniOver = false;
        obj.ani.time = time || 500;
        obj.ani.interval = 13;
        obj.ani.total = Math.ceil( obj.ani.time/obj.ani.interval );        //定时器总次数
        obj.ani.t = 0;                    //当前次数

        //如果第一次动画还没结束第二次就开始了, 就将第二次的json属性传入obj.ani.st(第一次的还在)
        //并且上一次动画的目标值不受影响
        var attr;
        for( attr in json) obj.ani.st[attr] = parseFloat(json[attr], 10);
        for( attr in obj.ani.st ){
            obj.ani.s0[attr] = parseFloat(move.css(obj, attr), 10);
        //    obj.ani.st[attr] = obj.ani.st[attr];
            obj.ani.dis[attr] = obj.ani.st[attr] - obj.ani.s0[attr];
            obj.ani.va[attr] = obj.ani.dis[attr]/obj.ani.total;
            obj.ani.d[attr] = 0;
        }
    },

    ease: function(obj, json, time, fn){
        if( obj.aniOver === false ) clearInterval(obj.ani.timer);
        this.init(obj, json, time);

        var attr, This = this;

        //因为每一种动画的初始速度, 最大速度, 加速度不同, 所以这三个单独设置
        for( attr in obj.ani.st ){
            obj.ani.v[attr] = 0.5*obj.ani.va[attr];
            //假设最大速度是3倍平均速度,初速度是0.5倍, 因此是3-0.5
            obj.ani.a[attr] = (3-0.5)*obj.ani.va[attr]/(0.5*obj.ani.total);
        }
        obj.ani.timer = setInterval(function(){
            obj.ani.t++;
            for( attr in obj.ani.st ){
                if( Math.abs(obj.ani.d[attr]) < Math.abs(obj.ani.dis[attr]/2) ){
                    obj.ani.v[attr] += obj.ani.a[attr];
                    obj.ani.d[attr] += obj.ani.v[attr];
                }
                else if( Math.abs(obj.ani.d[attr])>=Math.abs(obj.ani.dis[attr]/2) && Math.abs(obj.ani.d[attr])<=Math.abs(obj.ani.dis[attr]) ){
                    obj.ani.v[attr] -= obj.ani.a[attr];
                    obj.ani.d[attr] += obj.ani.v[attr];
                }
                obj.ani.res[attr] = obj.ani.s0[attr] + obj.ani.d[attr];
                if( (obj.ani.v[attr] > 0 && obj.ani.res[attr] > obj.ani.st[attr]) || (obj.ani.v[attr] < 0 && obj.ani.res[attr] < obj.ani.st[attr]) ) obj.ani.res[attr] = obj.ani.st[attr];
                if( obj.ani.t > obj.ani.total ){
                    clearInterval(obj.ani.timer);
                    obj.aniOver = true;
                    break;
                }
            }
            move.css(obj, obj.ani.res);
            if( obj.aniOver ){
                obj.ani.st = {};
                obj.ani.res = {};
                if( typeof fn === "function" ) fn.call(obj);
            }
        }, obj.ani.interval);
    },

    easeOut: function(obj, json, time, fn){
        if( obj.aniOver === false ) clearInterval(obj.ani.timer);
        this.init(obj, json, time);

        var attr, This = this;
        //因为每一种动画的初始速度, 最大速度, 加速度不同, 所以这三个单独设置
        for( attr in obj.ani.st ){
            obj.ani.v[attr] = 5*obj.ani.va[attr];
            obj.ani.a[attr] = -6*obj.ani.va[attr]/(0.5*obj.ani.total);
        }
        obj.ani.timer = setInterval(function(){
            obj.ani.t++;
            for( attr in obj.ani.st ){
                obj.ani.v[attr] += obj.ani.a[attr];
                obj.ani.d[attr] += obj.ani.v[attr];
                obj.ani.res[attr] = obj.ani.s0[attr] + obj.ani.d[attr];
                if( (obj.ani.v[attr] > 0 && obj.ani.res[attr] > obj.ani.st[attr]) || (obj.ani.v[attr] < 0 && obj.ani.res[attr] < obj.ani.st[attr]) ){
                    for( attr in obj.ani.res )    obj.ani.res[attr] = obj.ani.st[attr];
                    clearInterval(obj.ani.timer);
                    obj.aniOver = true;
                    break;
                }
            }
            move.css(obj, obj.ani.res);
            if( obj.aniOver && typeof fn === "function" ) fn.call(obj);
        }, obj.ani.interval);
    },

    collision: function(obj, json, time, fn){
        if( obj.aniOver === false ) clearInterval(obj.ani.timer);
        this.init(obj, json, time);
        var attr, This = this, temp;
        //因为每一种动画的初始速度, 最大速度, 加速度不同, 所以这三个单独设置
        for( attr in obj.ani.st ){
            obj.ani.v[attr] = 2*obj.ani.va[attr];
            obj.ani.a[attr] = 6*obj.ani.va[attr]/(0.5*obj.ani.total);
        }

        obj.ani.timer = setInterval(function(){
            obj.ani.t++;

            for( attr in obj.ani.st ){
                console.log(obj.ani.st)
                if( obj.ani.d[attr] === obj.ani.dis[attr] ) obj.ani.v[attr]*=-0.5;
                obj.ani.v[attr] += obj.ani.a[attr];
                obj.ani.v[attr] *= 0.999;
                temp = obj.ani.dis[attr] - obj.ani.d[attr];
                if( temp*obj.ani.v[attr] > 0 && Math.abs(temp) < Math.abs(obj.ani.v[attr]) ){
                    obj.ani.d[attr] += temp;
                }
                else{
                    obj.ani.d[attr] += obj.ani.v[attr];
                }
                obj.ani.res[attr] = obj.ani.s0[attr] + obj.ani.d[attr];

                if( obj.ani.t > obj.ani.total ){
                    for( attr in obj.ani.res )    obj.ani.res[attr] = obj.ani.st[attr];
                    clearInterval(obj.ani.timer);
                    obj.aniOver = true;
                    break;
                }
            }
            move.css(obj, obj.ani.res);
            if( obj.aniOver ){
                obj.ani.st = {};
                obj.ani.res = {};
                if( typeof fn === "function" ) fn.call(obj);
            }
        }, obj.ani.interval);
    },

    elastic: function(obj, json, fn){
        if( obj.aniOver === false ) clearInterval(obj.ani.timer);
        this.init(obj, json);

        var attr, This = this, factor={};
        //因为每一种动画的初始速度, 最大速度, 加速度不同, 所以这三个单独设置
        for( attr in obj.ani.st ){
            obj.ani.v[attr] = 0*obj.ani.va[attr];
            factor[attr] = 0.06;
        }
        obj.ani.timer = setInterval(function(){
            obj.ani.t++;
            for( attr in obj.ani.st ){
                obj.ani.a[attr] = (obj.ani.dis[attr] - obj.ani.d[attr])*factor[attr];
                obj.ani.v[attr] += obj.ani.a[attr];
                obj.ani.v[attr] *= 0.8;
            //    obj.ani.v[attr] = obj.ani.v[attr] > 0 ? Math.ceil(obj.ani.v[attr]) : Math.floor(obj.ani.v[attr]);
                obj.ani.d[attr] += obj.ani.v[attr];
                obj.ani.res[attr] = obj.ani.s0[attr] + obj.ani.d[attr];

                if( Math.abs(obj.ani.v[attr]) <= 2 && Math.abs(obj.ani.dis[attr] - obj.ani.d[attr]) <= 2  ){
                    factor[attr] = 0;
                    obj.ani.v[attr]=0;
                    obj.ani.res[attr] = obj.ani.st[attr];
                }
                if( obj.ani.t > obj.ani.total ){
                    for( attr in obj.ani.res )    obj.ani.res[attr] = obj.ani.st[attr];
                    clearInterval(obj.ani.timer);
                    obj.aniOver = true;
                    break;
                }
            }
            move.css(obj, obj.ani.res);
            if( obj.aniOver ){
                obj.ani.st = {};
                obj.ani.res = {};
                if( typeof fn === "function" ) fn.call(obj);
            }
        }, obj.ani.interval);
    }
}

      

  为了少传参数, 所以我用了命名空间来管理运动库, 如果想调用碰撞动画, 只需这样实现:

move.collision(obj, {left:500, top:300}, 1000, function(){
	alert("动画完成");
});

  其他几种动画调用方法同理, 但注意elastic不需要传入时间

为什么要在obj下声明一个ani对象?

  由于不是走的动画队列, 所以动画正在进行时如果施加新动画, 之前动画就会停止, 比如设置left从100px跑到600px, 在右移动的过程中如果马上施加一个top从当前值跑到500px的动画, 如果所有参数(初速度, 目标值, 时间等)放到函数中作为局部变量, 每次都会设置新的, 之前的目标值则就消失了,  因此将这些属性放到dom节点对象下面的ani对象中, 方便管理.

  这个动画库原理还是非常简单的, 有问题或不足的地方欢迎指正, 源码下载: move.js,   demo演示

时间: 2024-10-19 08:48:22

move ---- 编写一个包含几种常用动画的js库的相关文章

Numeral.js 是一个用于格式化和数字四则运算的js 库

1.Numeral.js 是一个用于格式化和数字四则运算的js 库. 2.支持多种语言,包含中文在内的17种语言. 在浏览器中引用js文件: <script src="numeral.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/numeral.js/1.4.5/numeral.min.js"></script> 在nodejs开发引用开

可视化CSS3动画代码生成js库插件-Bounce.js

简要教程 Bounce.js是一款功能非常强大的可视化CSS3动画代码生成js库插件.该js库插件提供了一个在线APP,通过该APP可以在可视化的条件下编辑CSS3的各种动画效果,如移动.旋转.倾斜.easing等效果,编辑完成后可以直接获取该CSS3帧动画的代码,复制代码到你的页面中即可在你的页面上获得与该动画一样的效果.此外,你也可以单独使用Bounce.js,通过js代码来完成各种CSS3动画效果.Bounce.js能与jQuery完美结合. 查看演示     下载插件 安装 可以通过Bo

html5轻量级操纵和制作SVG动画的js库-svg.js

svg.js是一个轻量级的操纵和制作SVG动画的js插件库.svg.js可以生成SVG图形.图像.文字和路径等等.svg.js还可以用于制作svg动画和互动拖拽等效果. svg.js不依赖与jQuery等外部插件库,它遵循麻省理工学院的许可( MIT License)下许可证的条款. 在线演示:http://www.htmleaf.com/Demo/201501301302.html 下载地址:http://www.htmleaf.com/html5/SVG/201501301301.html

如何比较好的编写一个包含业务逻辑的方法体

具体做法:先写出方法体的主体流程,细节部分先只抽象出方法暂不实现,等主体框架完成后再实现具体的细节部分. 缘由:之前的做法是建一个方法,然后把一坨业务逻辑的代码塞到方法里.这样做的问题是如果业务很复杂可能导致思路混乱,而且后期代码也不容易让别人理解.

一个简单的加载动画,js实现

简单效果图: html: <div class="box"> <ul> <li></li> <li></li> <li></li> <li></li> <li></li> </ul> </div> css: .box{ width: 200px; height: 80px; margin: 200px auto; bor

Dom Animator – 提供 Dom 注释动画的 JS 库

DOM 动画是一个极好的 JavaScript 库,用来在页面的 DOM 注释中显示小的 ASCII 动画.这对于那些检查你的代码的人是一个小彩蛋,仅此而已.它是一个独立的库,不依赖 jQuery 或者其它库,所以使用是非常简单的.您不需要任何 CSS 或 HTML,只是 JavaScript 而已. 在线演示      源码下载 您可能感兴趣的相关文章 网站开发中很有用的 jQuery 效果[附源码] 分享35个让人惊讶的 CSS3 动画效果演示 十分惊艳的8个 HTML5 & JavaScr

为Node.js编写组件的几种方式

本文主要备忘为Node.js编写组件的三种实现:纯js实现.v8 API实现(同步&异步).借助swig框架实现. 关键字:Node.js.C++.v8.swig.异步.回调. 简介 首先介绍使用v8 API跟使用swig框架的不同: (1)v8 API方式为官方提供的原生方法,功能强大而完善,缺点是需要熟悉v8 API,编写起来比较麻烦,是js强相关的,不容易支持其它脚本语言. (2)swig为第三方支持,一个强大的组件开发工具,支持为python.lua.js等多种常见脚本语言生成C++组件

Bounce.js-可视化CSS3动画代码生成js插件

Bounce.js是一款功能非常强大的可视化CSS3动画代码生成js库插件.该js库插件提供了一个在线APP,通过该APP可以在可视化的条件下编辑CSS3的各种动画效果.此外,你也可以单独使用Bounce.js,通过js代码来完成各种CSS3动画效果. Bounce.js能与jQuery完美结合. 在线演示:http://www.htmleaf.com/Demo/201502231412.html 下载地址:http://www.htmleaf.com/css3/css3donghua/2015

一个老师程序员说:这是学Java 必知必会的 20 种常用类库和 API

一个有经验的Java开发人员特征之一就是善于使用已有的轮子来造车.<Effective Java>的作者Joshua Bloch曾经说过:"建议使用现有的API来开发,而不是重复造轮子".在本文中,我将分享一些Java开发人员应该熟悉的最有用的和必要的库和API.顺便说一句,这里不包括框架,如Spring和Hibernate因为他们非常有名,都有特定的功能.最后,如果大家如果在自学遇到困难,想找一个java的学习环境,可以加入我们的java学习圈,点击我加入吧,会节约很多时