前言:

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

        本篇文章为您分析一下原生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;
}

结语

整完!!!

js 实现动画功能,完整解析插件版 可更改配置参数[animate.js]的更多相关文章

  1. js 控制Div循环显示 非插件版

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  2. js 实现文字滚动功能,可更改配置参数 带完整版解析代码。

    前言:         本人纯小白一个,有很多地方理解的没有各位大牛那么透彻,如有错误,请各位大牛指出斧正!小弟感激不尽.         本篇文章为您分析一下原生JS写文字滚动效果 需求分析: 需要 ...

  3. js 实现淘宝放大镜功能,可更改配置参数 带完整版解析代码[magnifier.js]

    前言:         本人纯小白一个,有很多地方理解的没有各位大牛那么透彻,如有错误,请各位大牛指出斧正!小弟感激不尽.         本篇文章为您分析一下原生JS写淘宝放大镜效果 基本功能: 运 ...

  4. js 实现图片瀑布流效果,可更改配置参数 带完整版解析代码[waterFall.js]

    前言:         本人纯小白一个,有很多地方理解的没有各位大牛那么透彻,如有错误,请各位大牛指出斧正!小弟感激不尽.         本篇文章为您分析一下原生JS实现图片瀑布流效果 页面需求 1 ...

  5. js 实现淘宝无缝轮播图效果,可更改配置参数 带完整版解析代码[slider.js]

    前言:         本人纯小白一个,有很多地方理解的没有各位大牛那么透彻,如有错误,请各位大牛指出斧正!小弟感激不尽.         本篇文章为您分析一下原生JS写淘宝无缝轮播图效果 需求分析: ...

  6. js 函数的防抖(debounce)与节流(throttle) 带 插件完整解析版 [helpers.js]

    前言:         本人纯小白一个,有很多地方理解的没有各位大牛那么透彻,如有错误,请各位大牛指出斧正!小弟感激不尽.         函数防抖与节流是做什么的?下面进行通俗的讲解. 本文借鉴:h ...

  7. js 函数的多图片懒加载(lazy) 带插件版完整解析

    前言:         本人纯小白一个,有很多地方理解的没有各位大牛那么透彻,如有错误,请各位大牛指出斧正!小弟感激不尽.         本篇文章为您分析一下原生JS实现图片懒加载效果 页面需求 1 ...

  8. 好用的jquery.animateNumber.js数字动画插件

    在做公司的运营报告页面时,有一个数字累计增加的动画效果,一开始,毫无头绪,不知如何下手,于是上网查资料,发现大多都是用的插件来实现的,那么今天,我也来用插件jquery.animateNumber.j ...

  9. Knockout.js 数据验证之插件版和无插件版

    本文我们将介绍使用 Knockout.js 实现一些基本的数据验证.就如我们在标题里提到的,我们会使用两种方法来创建数据验证方法. 使用自定义方法,不需要任何插件 最简单的方法是使用已有的插件 如果你 ...

随机推荐

  1. Keil5新建STM32工程(库函数版本)

    1.下载keil5教程参考:https://blog.csdn.net/qq_36854651/article/details/82632931 2.安装完keil5后,创建一个空的目录存放工程 3. ...

  2. python函数的传参模式

    python里的变量更像是一个名字.标签.而Python中一切又皆为对象. 当函数传参时,函数参数作为一个标签,指向某个对象,因此更贴切的说是"call by object". 但 ...

  3. fstream

    fstream file("b.txt", ios::in|ios::out|ios::app); mode ate - seek to the end of stream imm ...

  4. thinkphp后端开发ajax接口开发测试

    数据自动填充,Firefox的网络查看JSON数据,查错. 同时,用getLastSql函数查询,并且开启日志文件记录网页执行的全过程,还可以用thinkPHP内置的trace的跟踪. 谁有更好地方法 ...

  5. 使用css动画实现领积分效果

    最近项目中要做一个领积分的效果,根据老板的描述,这个效果类似于支付宝蚂蚁森林里的领取能量.整体效果是就是在树周围飘着几个积分元素,上下滑动,类似星星闪烁,点击领取后,沿着树中心的位置滑动并消失,树上的 ...

  6. 【PHP】函数

    一.      数学中的函数: a)    sin().cos().tan().log() 这些都是数学函数,但是都已经忘了啥意思了!没关系,在php当中的函数和这些函数的概念还是有一定的区别的 二. ...

  7. 百度找不到,但高手都知道(感觉他们都知道)的一个小细节--BUG调试报告

    语言 Batch 前言 以前我一直不明白为什么那么多应用程序在读取"文件路径"作为参数时为什么总是在正式的"文件路径"前要加上个"-f".& ...

  8. 《深入理解 Java 虚拟机》读书笔记:垃圾收集器与内存分配策略

    正文 垃圾收集器关注的是 Java 堆和方法区,因为这部分内存的分配和回收是动态的.只有在程序处于运行期间时才能知道会创建哪些对象,也才能知道需要多少内存. 虚拟机栈和本地方法栈则不需要过多考虑回收的 ...

  9. 【Selenium04篇】python+selenium实现Web自动化:文件上传,Cookie操作,调用 JavaScript,窗口截图

    一.前言 最近问我自动化的人确实有点多,个人突发奇想:想从0开始讲解python+selenium实现Web自动化测试,请关注博客持续更新! 这是python+selenium实现Web自动化第四篇博 ...

  10. 15.ASP.NET Core 应用程序中的静态文件中间件

    在这篇文章中,我将向大家介绍,如何使用中间件组件来处理静态文件.这篇文章中,我们讨论下面几个问题: 在ASP.NET Core中,我们需要把静态文件存放在哪里? 在ASP.NET Core中 wwwr ...