• 帧动画插件


    动画插件封装

    最近这段时间一直都在研究关于动画方法的知识,说实话确实不容易,主要还是动画算法这方面比较难,毕竟没学过。当然也有所收获,明白了基本动画的原理是什么,所以自己也封装了一个简单的动画插件来巩固自己所学。

    动画插件的实现方式

    对于前端来说,主要实现动画的方式就是css(transition , animation),js(setTimeout , setInterval , requestAnimationFrame),canvas,svg等方式,在这里我主要是通过requestAnimationFrame来实现动画效果的。

    插件说明

    该插件接受5个参数:

    1. 第一个参数是需要动画的目标元素。
    2. 第二个参数是需要动画的属性,是一个对象。
    3. 第三个参数是动画的总时长。
    4. 第四个参数是动画的效果。目前支持三种动画效果,linear,easeIn,easeOut。
    5. 第五个参数是动画结束之后的回调函数。

    该插件可以实现多个属性一起动画效果,也可以实现单个属性动画效果,也可以实现一个属性接着一个属性动画效果。由于使用的是回调函数,所以当一个接着一个属性来实现动画效果的时候,会产生回调函数嵌套。

    插件代码

    function (element , props , duration , easing , callback) {
    if (typeof element !== 'object' && element.nodeType !== 1) {
    return;
    };
    if (typeof props !== 'object' && props.toString() !== '[object Object]') {
    return;
    };
    var noop = function () {};
    this.element = element;
    this.props = props;
    this.duration = duration || 600;
    this.easing = easing || 'linear';
    this.callback = callback || noop;
    this.tickID = 0;
    this.styles = this.getStyle();
    this.animate();
    };
    Animator.prototype = {
    getStyle : function () {
    return window.getComputedStyle ? window.getComputedStyle(this.element) : this.element.currentStyle();
    },
    animate : function () {
    for (var prop in this.props) {
    this.step.call(this , prop);
    }
    },
    step : function (prop) {
    var self = this;
    var initialValue = 0;
    var beginTime = new Date();
    var endValue = parseFloat(this.props[prop]);
    var beginValue = parseFloat(this.styles[prop]);
    var changeValue = parseFloat(endValue - beginValue);
    var distance = 0;
    var move = function () {
    var p = (new Date() - beginTime) / self.duration;
    if (p > 1) {
    self.element.style[prop] = (prop === 'opacity') ? endValue : endValue + 'px';
    cancelAnimationFrame(self.tickID);
    self.tickID = null;
    self.callback.call(self);
    } else {
    if (self.easing === 'linear') {
    distance = changeValue * p;
    } else if (self.easing === 'easeIn') {
    distance = changeValue * p * p;
    } else if (self.easing === 'easeOut') {
    distance = changeValue * (2 * p - p * p);
    };
    self.element.style[prop] = (prop === 'opacity') ? (beginValue + distance) : (beginValue + distance + 'px');
    this.tickID = requestAnimationFrame(move);
    }
    };
    move();
    }
    };

    实例代码

    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
    *{
    margin: 0;
    padding: 0;
    list-style: none;
    }
    .box{
    width:100px;
    height:100px;
    background:#f60;
    position:absolute;
    top:50;
    left:0;
    border: 1px solid #000;大专栏  帧动画插件iv>
    }
    </style>
    </head>
    <body>
    <button id="btn">click</button>
    <div id="box" class="box"></div>
    <script>
    function (element , props , duration , easing , callback) {
    if (typeof element !== 'object' && element.nodeType !== 1) {
    return;
    };
    if (typeof props !== 'object' && props.toString() !== '[object Object]') {
    return;
    };
    var noop = function () {};
    this.element = element;
    this.props = props;
    this.duration = duration || 600;
    this.easing = easing || 'linear';
    this.callback = callback || noop;
    this.tickID = 0;
    this.styles = this.getStyle();
    this.animate();
    };
    Animator.prototype = {
    getStyle : function () {
    return window.getComputedStyle ? window.getComputedStyle(this.element) : this.element.currentStyle();
    },
    animate : function () {
    for (var prop in this.props) {
    this.step.call(this , prop);
    }
    },
    step : function (prop) {
    var self = this;
    var initialValue = 0;
    var beginTime = new Date();
    var endValue = parseFloat(this.props[prop]);
    var beginValue = parseFloat(this.styles[prop]);
    var changeValue = parseFloat(endValue - beginValue);
    var distance = 0;
    var move = function () {
    var p = (new Date() - beginTime) / self.duration;
    if (p > 1) {
    self.element.style[prop] = (prop === 'opacity') ? endValue : endValue + 'px';
    cancelAnimationFrame(self.tickID);
    self.tickID = null;
    self.callback.call(self);
    } else {
    if (self.easing === 'linear') {
    distance = changeValue * p;
    } else if (self.easing === 'easeIn') {
    distance = changeValue * p * p;
    } else if (self.easing === 'easeOut') {
    distance = changeValue * (2 * p - p * p);
    };
    self.element.style[prop] = (prop === 'opacity') ? (beginValue + distance) : (beginValue + distance + 'px');
    this.tickID = requestAnimationFrame(move);
    }
    };
    move();
    }
    };
    var box = document.querySelector('#box');
    var btn = document.querySelector('#btn');
    btn.addEventListener('click' , function () {
    new Animator(box , {
    width : 300,
    height : 300,
    top : 200,
    left : 100,
    opacity : 0.5,
    borderWidth : 20
    });
    });
    //效果二
    btn.addEventListener('click' , function () {
    new Animator(box , {
    width : 500
    } , 1000 , 'easeOut' , function () {
    new Animator(box , {
    height : 300,
    left : 100,
    borderWidth : 50
    } , 1000 , 'easeIn' , function () {
    new Animator(box , {
    opacity : 0.6
    })
    });
    });
    });
    </script>
  • 相关阅读:
    AOP动态代理两种方式
    Spring AOP的两种代理方式
    面试中关于Redis的问题看这篇就够了
    关于redis,学会这8点就够了
    什么是MVCC
    JAVA 中BIO,NIO,AIO的理解
    JAVA异常体系结构详解
    Java多线程:由浅入深看synchronized的底层实现原理
    为什么wait,notify和notifyAll要与synchronized一起使用?
    Java并发之AQS详解
  • 原文地址:https://www.cnblogs.com/lijianming180/p/12026664.html
Copyright © 2020-2023  润新知