前言:

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

        本篇文章为您分析一下原生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. 判断移动端还是PC端JS

    if(/Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent)) {         // alert("手机&qu ...

  2. 1021 Deepest Root (25 分)

    A graph which is connected and acyclic can be considered a tree. The height of the tree depends on t ...

  3. Socket探索1-两种Socket服务端实现

    介绍 一次简单的Socket探索之旅,分别对Socket服务端的两种方式进行了测试和解析. CommonSocket 代码实现 实现一个简单的Socket服务,基本功能就是接收消息然后加上结束消息时间 ...

  4. 一分钟搞定Java高频面试题

    一分钟搞定Java高频面试题 一.变量赋值和计算 题目: public static void main(String[] args) { int i = 1; i = i++; int j = i+ ...

  5. MTK Android 设置-选择日期格式 [管理和组织首选项,ListPreference,CheckBoxPreference,EditTextPreference,RingtonePreference]

    ###android.preference.ListPreference的一些特性 android:key  选项的名称或键 android:title  选项的标题 android:summary  ...

  6. MySQL入门,第五部分,表结构的修改

    ALTER TABLE <基本表名> [ ADD <新列名> <列数据类型> [列完整性约束] DROP COLUMN <列名> MODIFY < ...

  7. 推荐一款超实用的GitHub可视化代码树插件:Octotree

    前言 大家在GitHub查看代码的时候,是不是会经常跳转搜索代码!过一段时间就不知道自己跑到哪里了!有了这款工具,妈妈再也不用担心我找不到代码位置了! 直接上效果图 插件名称 : octotree 作 ...

  8. 非PDC角色DC强制NTP

    前一阵,公司其他部门员工告诉我,他们的系统无法通过LDAP搜索账户了 经过检查,发现该服务器的时间居然比我们的时间服务器PDC快了将近20分钟,而且该问题机器的 时间源并非PDC,而是另外一台普通DC ...

  9. 多线程高并发编程(3) -- ReentrantLock源码分析AQS

    背景: AbstractQueuedSynchronizer(AQS) public abstract class AbstractQueuedSynchronizer extends Abstrac ...

  10. three.js中让模型自动居中的代码如下:

    //load_Model为需要居中的3D模型 //原理是通过boundingBoxHelper 来计算模型的大小范围 var hex = 0xff0000; var MD_Length,MD_Widt ...