前言:

        本人纯小白一个,有很多地方理解的没有各位大牛那么透彻,如有错误,请各位大牛指出斧正!小弟感激不尽。

        本篇文章为您分析一下原生JS写一个运动插件

基本功能:

HTML结构


    <div class="container"></div>
    <p>
        <button id="start">开始</button>
        <button id="stop">结束</button>
    </p>

html的结构只是为了调试运动插件而随意创建的。

CSS样式


    <style>
        .container {
            width: 100px;
            height: 100px;
            background-color: aqua;
            position: absolute;
            left: 0;
            top: 0;
        }

        p{
            position: absolute;
            top: 50px;
            left: 100px;
        }
    </style>

页面效果如下:

JS行为

需求分析:
  • 动画: 某些元素的某些CSS属性,在一段时间内,从一个值变化到另一个值

  • 动画插件: 某些数据(数字),在一段时间内,从一个值变化到另一个值

  • 不考虑DOM元素,DOM元素由用户传入

  1. 创建一个构造函数,让用户传入一些必须的参数(值、函数)

  2. 引入helper.js插件(自己封装的),使用对象混合

  3. 计算运动的总次数

  4. 获取当前的运动状态

  5. 计算所有属性每次运动的距离

  6. 为Animate函数添加方法

  7. 在原型上添加动画的开始函数

  8. 判断当前动画是否存在

  9. 开启一个定时器 ( 清空)

  10. 设置当前动画的状态(每次改变的距离)

页面代码如下:
页面JS:
        var div = document.querySelector(".container");
        var animate = new this.myPlugin.Animate({
            duration: 30,
            tatal: 1000,
            begin: {
                left: 0,
                top: 0
            },
            end: {
                left: 1000,
                top: 600
            },

        });



/**
 * 动画: 某些元素的某些CSS属性,在一段时间内,从一个值变化到另一个值
 * 动画插件: 某些数据,在一段时间内,从一个值变化到另一个值
 * 构造函数
 * @param {Object} option 配置对象
 */
this.myPlugin.Animate = function (option) {
    // 第一步: 默认配置
    var defalutOption = {
        duration: 16,   // 默认间隔时间,单位毫秒
        total: 1000,    // 默认总时间,单位毫秒
        begin: {},      // 需要变化的初始值
        end: {}         // 需要变化的结束值
    }
    // 第二步: 对象混合     参考helper.js中的对象混合插件
    this.option = myPlugin.mixin(defalutOption, option);
    // 计时器的id
    var timer = null;
    // 第三步: 运行总次数  变化的次数
    this.number = Math.ceil(this.option.total / this.option.duration);
    // 3.1 当前运动的次数
    this.curNumber = 0;
    // 第四步: 得到当前的运动状态    参考helper.js中的对象克隆插件  因为当前的运动属性肯定和你的初始值运动属性是一样的,所有我们要克隆他
    this.curData = myPlugin.clone(this.option.begin);
    // 第五步: 所有属性运动的总距离
    this.distance = {};
    // 5.1    所有属性每次运动的距离
    this.everyDistance = {};
    // 循环begin和end的所有属性
    for (var prop in this.option.begin) {
        // 5.2 所有属性运动的距离 = 结束值 - 初始值
        this.distance[prop] = this.option.end[prop] - this.option.begin[prop];
        // 5.3 所有属性每次运动的距离 = 运动总距离 / 运动次数
        this.everyDistance[prop] = this.distance[prop] / this.number;
    }
}

在原型上添加方法

/**
 * 在原型上添加动画方法,因为每创建一个Animate函数时都会创建一个start方法,这样代码比较冗余
 * 第七步: 7.1 开始动画
 */
this.myPlugin.Animate.prototype.start = function () {
    // 第八步: 如果这个定时器已经有动画了或者当前运动的次数达到了运动的总次数
    if (this.timer || this.curNumber === this.number) {
        // 不做任何处理   此处并非清空定时器
        return;
    }

    // 8.1 保存this
    var self = this;
    // 第九步: 开启一个定时器
    this.timer = setInterval(function () {
        // 9.1 每次运动次数加一
        self.curNumber++;
        // 9.2 如果当前运动的次数等于总次数
        if (self.curNumber === self.number) {
            // 9.3 停止动画
            self.stop();
        }
    }, this.option.duration)
}

/**
 * 第七步: 7.2 停止动画
 */
this.myPlugin.Animate.prototype.stop = function () {
    // 清空定时器
    clearInterval(this.timer);
    // 将定时器设置为null
    this.timer = null;
}

设置每次改变的距离

/**
 * 在原型上添加动画方法,因为每创建一个Animate函数时都会创建一个start方法,这样代码比较冗余
 * 第七步: 7.1 开始动画
 */
this.myPlugin.Animate.prototype.start = function () {
    // 第八步: 如果这个定时器已经有动画了或者当前运动的次数达到了运动的总次数
    if (this.timer || this.curNumber === this.number) {
        // 不做任何处理   此处并非清空定时器
        return;
    }

    // 8.1 保存this
    var self = this;
    // 第九步: 开启一个定时器
    this.timer = setInterval(function () {
        // 9.1 每次运动次数加一
        self.curNumber++;
        // 第十步: 改变self.curData的每一项属性
        for (var prop in self.curData) {
            // 10.1 这里进行的是小数的运算。
            if (self.curNumber === self.number) {
                // 10.2 处理小数不精确 最后一次运动
                self.curData[prop] = self.option.end[prop];
            } else {
                // 10.3 每次运动都加上当前的距离
                self.curData[prop] += self.everyDistance[prop];
            }
        }
        // 9.2 如果当前运动的次数等于总次数
        if (self.curNumber === self.number) {
            // 9.3 停止动画
            self.stop();
        }
    }, this.option.duration)
}

/**
 * 第七步: 7.2 停止动画
 */
this.myPlugin.Animate.prototype.stop = function () {
    // 清空定时器
    clearInterval(this.timer);
    // 将定时器设置为null
    this.timer = null;
}

下面进行调用
在页面的JS中添加:
        var div = document.querySelector(".container");
        var animate = new this.myPlugin.Animate({
            duration: 30,
            tatal: 1000,
            begin: {
                left: 0,
                top: 0
            },
            end: {
                left: 1000,
                top: 600
            },
            onstart: function () {
                console.log("开始");
            },
        });

        start.onclick = function () {
            animate.start();
        }
第十一步: 在动画插件中添加开始函数:


    // 第八步: 如果这个定时器已经有动画了或者当前运动的次数达到了运动的总次数
    if (this.timer || this.curNumber === this.number) {
        // 不做任何处理   此处并非清空定时器
        return;
    }
    // 保存this。
    var self = this;
    // 第十一步: 如果在配置里有这个函数,我们就在开始的时候调用
    if (this.option.onstart) {
        // 调用函数,通过call来传入他的this指向
        this.option.onstart.call(self)
    }
    // 第九步: 开启一个定时器
    this.timer = setInterval(function () {
        // console.log(self.curData);
        // 9.1 每次运动次数加一
        self.curNumber++;
        // 第十步: 改变self.curData的每一项属性
        for (var prop in self.curData) {
            // 10.1 这里进行的是小数的运算。
            if (self.curNumber === self.number) {
                // 10.2 处理小数不精确 最后一次运动
                self.curData[prop] = self.option.end[prop];
            } else {
                // 10.3 每次运动都加上当前的距离
                self.curData[prop] += self.everyDistance[prop];
            }
        }
        // 第十一步: 如果在配置里有这个函数,我们就在开始的时候调用
        if (self.option.onmove) {
            // 调用函数,通过call来传入他的this指向
            self.option.onmove.call(self);
        }
        // 9.2 如果当前运动的次数等于总次数
        if (self.curNumber === self.number) {
            self.stop();
            // 第十一步: 如果在配置里有这个函数,我们就在开始的时候调用
            if (self.option.onover) {
                // 调用函数,通过call来传入他的this指向
                self.option.onover.call(self);
            }
        }
    }, this.option.duration)

在页面的JS中添加移动函数:

            // 当每次发生变化时
            onmove: function () {
                // console.log(this.curData);
                div.style.left = this.curData.left + "px";
                div.style.top = this.curData.top + "px";
                div.style.background = `rgb(${this.curData.r},${this.curData.g},${this.curData.b})`;
            },

第十二步: 在动画插件中添加移动函数:


    // 第八步: 如果这个定时器已经有动画了或者当前运动的次数达到了运动的总次数
    if (this.timer || this.curNumber === this.number) {
        // 不做任何处理   此处并非清空定时器
        return;
    }
    // 保存this。
    var self = this;
    // 第十一步: 如果在配置里有这个函数,我们就在开始的时候调用
    if (this.option.onstart) {
        // 调用函数,通过call来传入他的this指向
        this.option.onstart.call(self)
    }
    // 第九步: 开启一个定时器
    this.timer = setInterval(function () {
        // console.log(self.curData);
        // 9.1 每次运动次数加一
        self.curNumber++;
        // 第十步: 改变self.curData的每一项属性
        for (var prop in self.curData) {
            // 10.1 这里进行的是小数的运算。
            if (self.curNumber === self.number) {
                // 10.2 处理小数不精确 最后一次运动
                self.curData[prop] = self.option.end[prop];
            } else {
                // 10.3 每次运动都加上当前的距离
                self.curData[prop] += self.everyDistance[prop];
            }
        }
        // 第十二步: 如果在配置里有这个函数,我们就在开始的时候调用
        if (self.option.onmove) {
            // 调用函数,通过call来传入他的this指向
            self.option.onmove.call(self);
        }
        // 9.2 如果当前运动的次数等于总次数
        if (self.curNumber === self.number) {
            self.stop();
        }
    }, this.option.duration)

在页面的JS中绑定暂停事件函数:

     btnStop.onclick = function () {
         animate.stop();
     }

第十三步: 在动画插件中添加结束函数:


 // 第八步: 如果这个定时器已经有动画了或者当前运动的次数达到了运动的总次数
 if (this.timer || this.curNumber === this.number) {
     // 不做任何处理   此处并非清空定时器
     return;
 }
 // 保存this。
 var self = this;
 // 第十一步: 如果在配置里有这个函数,我们就在开始的时候调用
 if (this.option.onstart) {
     // 调用函数,通过call来传入他的this指向
     this.option.onstart.call(self)
 }
 // 第九步: 开启一个定时器
 this.timer = setInterval(function () {
     // console.log(self.curData);
     // 9.1 每次运动次数加一
     self.curNumber++;
     // 第十步: 改变self.curData的每一项属性
     for (var prop in self.curData) {
         // 10.1 这里进行的是小数的运算。
         if (self.curNumber === self.number) {
             // 10.2 处理小数不精确 最后一次运动
             self.curData[prop] = self.option.end[prop];
         } else {
             // 10.3 每次运动都加上当前的距离
             self.curData[prop] += self.everyDistance[prop];
         }
     }
     // 第十二步: 如果在配置里有这个函数,我们就在开始的时候调用
     if (self.option.onmove) {
         // 调用函数,通过call来传入他的this指向
         self.option.onmove.call(self);
     }
     // 9.2 如果当前运动的次数等于总次数
     if (self.curNumber === self.number) {
         self.stop();
         // 第十三步: 如果在配置里有这个函数,我们就在开始的时候调用
         if (self.option.onover) {
             // 调用函数,通过call来传入他的this指向
             self.option.onover.call(self);
         }
     }
 }, this.option.duration)

页面效果如下

下面附上完整代码

HTML结构


    <div class="container">

    </div>
    <p>
        <button id="btnBegin">开始</button>
        <button id="btnStop">停止</button>
    </p>

CSS样式


<style>
        .container {
            position: absolute;
            left: 0;
            top: 0;
            width: 100px;
            height: 100px;
            background-color: aqua;
        }

        p {
            position: absolute;
            left: 200px;
            top: 100px;
            width: 100px;
            height: 100px;
        }
    </style>

JS行为

页面JS行为


    <script src="./plugin/helpers.js"></script>  <!--引入插件-->
    <script src="./anmate.js"></script>          <!--引入插件-->
    <script>
        var div = document.querySelector(".container");
        var animate = new this.myPlugin.Animate({
            duration: 30,
            tatal: 1000,
            begin: {
                left: 0,
                top: 0
            },
            end: {
                left: 1000,
                top: 400
            },
            onstart: function () {
                console.log("开始");
            },
            // 当每次发生变化时
            onmove: function () {
                console.log(this.curData);

                div.style.left = this.curData.left + "px";
                div.style.top = this.curData.top + "px";
                div.style.background = `rgb(${this.curData.r},${this.curData.g},${this.curData.b})`;
            },
            onover: function() {
                console.log("结束");
            }
        });

        btnBegin.onclick = function () {
            animate.start();
        }
        btnStop.onclick = function () {
            animate.stop();
        }
        // console.log(animate);
    </script>

插件JS行为


if (!this.myPlugin) {
    this.myPlugin = {};
}

/**
 * 动画: 某些元素的某些CSS属性,在一段时间内,从一个值变化到另一个值
 * 动画插件: 某些数据,在一段时间内,从一个值变化到另一个值
 * 构造函数
 * @param {Object} option 配置对象
 */
this.myPlugin.Animate = function (option) {
    // 第一步: 默认配置
    var defalutOption = {
        duration: 16,   // 默认间隔时间,单位毫秒
        total: 1000,    // 默认总时间,单位毫秒
        begin: {},      // 需要变化的初始值
        end: {}         // 需要变化的结束值
    }
    // 第二步: 对象混合     参考helper.js中的对象混合插件
    this.option = myPlugin.mixin(defalutOption, option);
    // 第三步: 计时器的id
    var timer = null;
    // 3.1 运行总次数  变化的次数
    this.number = Math.ceil(this.option.total / this.option.duration);
    // 3.2 当前运动的次数
    this.curNumber = 0;
    // 第四步: 得到当前的运动状态    参考helper.js中的对象克隆插件  因为当前的运动属性肯定和你的初始值运动属性是一样的,所有我们要克隆他
    this.curData = myPlugin.clone(this.option.begin);
    // 第五步: 所有属性运动的总距离
    this.distance = {};
    // 5.1    所有属性每次运动的距离
    this.everyDistance = {};
    // 第六步: 循环begin和end的所有属性
    for (var prop in this.option.begin) {
        // 6.1 所有属性运动的距离 = 结束值 - 初始值
        this.distance[prop] = this.option.end[prop] - this.option.begin[prop];
        // 6.2 所有属性每次运动的距离 = 运动总距离 / 运动次数
        this.everyDistance[prop] = this.distance[prop] / this.number;
    }
}

/**
 * 在原型上添加动画方法,因为每创建一个Animate函数时都会创建一个start方法,这样代码比较冗余
 * 第七步: 7.1 开始动画
 */
this.myPlugin.Animate.prototype.start = function () {
    // 第八步: 如果这个定时器已经有动画了或者当前运动的次数达到了运动的总次数
    if (this.timer || this.curNumber === this.number) {
        // 不做任何处理   此处并非清空定时器
        return;
    }
    // 保存this。
    var self = this;
    // 第十一步: 如果在配置里有这个函数,我们就在开始的时候调用
    if (this.option.onstart) {
        // 调用函数,通过call来传入他的this指向
        this.option.onstart.call(self)
    }
    // 第九步: 开启一个定时器
    this.timer = setInterval(function () {
        // console.log(self.curData);
        // 9.1 每次运动次数加一
        self.curNumber++;
        // 第十步: 改变self.curData的每一项属性
        for (var prop in self.curData) {
            // 10.1 这里进行的是小数的运算。
            if (self.curNumber === self.number) {
                // 10.2 处理小数不精确 最后一次运动
                self.curData[prop] = self.option.end[prop];
            } else {
                // 10.3 每次运动都加上当前的距离
                self.curData[prop] += self.everyDistance[prop];
            }
        }
        // 第十二步: 如果在配置里有这个函数,我们就在开始的时候调用
        if (self.option.onmove) {
            // 调用函数,通过call来传入他的this指向
            self.option.onmove.call(self);
        }
        // 9.2 如果当前运动的次数等于总次数
        if (self.curNumber === self.number) {
            self.stop();
            // 第十三步: 如果在配置里有这个函数,我们就在开始的时候调用
            if (self.option.onover) {
                // 调用函数,通过call来传入他的this指向
                self.option.onover.call(self);
            }
        }
    }, this.option.duration)
}

/**
 * 第七步: 7.2 停止动画
 */
this.myPlugin.Animate.prototype.stop = function () {
    // 清空定时器
    clearInterval(this.timer);
    // 将定时器设置为null
    this.timer = null;
}

结语

整完!!!

最新文章

  1. mybatis-缓存1
  2. Bootstrap学习笔记(一)
  3. Synchronized 个人深解
  4. 我对java反射机制的理解
  5. Java I/O解读与使用实例
  6. 关闭oom killer
  7. NBUT 1120 Reimu&#39;s Teleport (线段树)
  8. MySQL几个注意点
  9. jmp指令
  10. rsyslog 走tcp通讯配置
  11. Java线程状态及Thread类中的主要方法
  12. windows+nginx负载测试
  13. cnblogs latex公式
  14. loadView
  15. 给斐讯K1刷机并拨号e信(湖北地区测试无问题)
  16. 题解——洛谷P2294 [HNOI2005]狡猾的商人(差分约束)
  17. UIActivityIndicatorView 的使用
  18. BugPhobia准备篇章:Scrum Meeting工作分析篇
  19. C++ pbds 库平衡树(tree)
  20. iOS开发-iOS7禁用手势返回

热门文章

  1. Mysql数据库错误代码大全
  2. Linux搜索工具
  3. Ubuntu16.04默认Python3.5升级Python3.6踩坑
  4. javascript入门 之 ztree(四 自定义Icon)
  5. json.dumps() 和 json.loads()
  6. SQL基础系列(1)-基本语法--转载w3school
  7. java添加对象成功后想知道当前添加对象的id
  8. RNN循环神经网络(Recurrent Neural Network)学习
  9. vue单页应用和和多页应用的区别
  10. B - Bash and a Tough Math Puzzle CodeForces - 914D (线段树的巧妙应用)