前言:

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

        本篇文章为您分析一下原生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. vs code 关闭保存自动格式化 formatonsave - [vscode] - [html]

    有时候Ctrl+s保存,html代码哥格式会紊乱. 造成这个原因一般是我们基本都在用的一个插件: ![](https://img2018.cnblogs.com/blog/1735896/201912 ...

  2. vue中的js引入图片,使用require相关问题

    vue中的js引入图片,必须require进来 或者引用网络地址 <template> <div class="home"> <img alt=&qu ...

  3. 浅谈Python导入外部文件

    import 和 from ... import * 有什么区别? 例如我写了个模块hello.py def print_func(args): print("Hello " + ...

  4. Linux搜索工具

                                                                Linux搜索工具 Search搜索工具 yum search all vim  ...

  5. Linux/Unix下pid文件的作用

    主要有三点: (1) pid文件的内容:pid文件为文本文件,内容只有一行, 记录了该进程的ID. 用cat命令可以看到. (2) pid文件的作用:防止进程启动多个副本.只有获得pid文件(固定路径 ...

  6. 微信小程序wx.setStorage(OBJECT)

    关于微信小程序的:wx.setStorage(OBJECT)在官网API介绍到:

  7. 计算机网络篇,基于UDP、TCP的应用层及其端口

  8. 33.1 File 获取目录下的所有文件及子目录

    重要获取功能 String[] list() 返回当前路径下所有的文件和文件夹名称 //注意:只有指向文件夹的File对象才可以调用该方法(指向文件的file对象使用list会报错npe) File[ ...

  9. JS 浏览器BOM-->open() 方法

    1.定义和用法 open() 方法用于打开一个新的浏览器窗口或查找一个已命名的窗口. 语法: window.open(URL,name,specs,replace) 参数: URL:打开指定的页面的U ...

  10. MySQL REPLACE INTO 的使用

    前段时间写游戏合服工具时出现过一个问题,源DB和目标DB角色表中主键全部都不相同,从源DB取出玩家数据再使用 replace into 写入目标DB中,结果总有几条数据插入时会导致目标DB中原有的角色 ...