js 实现动画功能,完整解析插件版 可更改配置参数[animate.js]
前言:
本人纯小白一个,有很多地方理解的没有各位大牛那么透彻,如有错误,请各位大牛指出斧正!小弟感激不尽。
本篇文章为您分析一下原生JS写一个运动插件
基本功能:
补充: 本插件需要依赖heplers.js插件的(对象混合与克隆) https://www.cnblogs.com/qq4297751/p/12630364.html
补充中......
HTML结构
<div class="container"></div>
<p>
<button id="start">开始</button>
<button id="stop">结束</button>
</p>
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元素由用户传入
创建一个构造函数,让用户传入一些必须的参数(值、函数)
引入helper.js插件(自己封装的),使用对象混合
计算运动的总次数
获取当前的运动状态
计算所有属性每次运动的距离
为Animate函数添加方法
在原型上添加动画的开始函数
判断当前动画是否存在
开启一个定时器 ( 清空)
设置当前动画的状态(每次改变的距离)
页面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)
// 当每次发生变化时
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)
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]的更多相关文章
- js 控制Div循环显示 非插件版
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- js 实现文字滚动功能,可更改配置参数 带完整版解析代码。
前言: 本人纯小白一个,有很多地方理解的没有各位大牛那么透彻,如有错误,请各位大牛指出斧正!小弟感激不尽. 本篇文章为您分析一下原生JS写文字滚动效果 需求分析: 需要 ...
- js 实现淘宝放大镜功能,可更改配置参数 带完整版解析代码[magnifier.js]
前言: 本人纯小白一个,有很多地方理解的没有各位大牛那么透彻,如有错误,请各位大牛指出斧正!小弟感激不尽. 本篇文章为您分析一下原生JS写淘宝放大镜效果 基本功能: 运 ...
- js 实现图片瀑布流效果,可更改配置参数 带完整版解析代码[waterFall.js]
前言: 本人纯小白一个,有很多地方理解的没有各位大牛那么透彻,如有错误,请各位大牛指出斧正!小弟感激不尽. 本篇文章为您分析一下原生JS实现图片瀑布流效果 页面需求 1 ...
- js 实现淘宝无缝轮播图效果,可更改配置参数 带完整版解析代码[slider.js]
前言: 本人纯小白一个,有很多地方理解的没有各位大牛那么透彻,如有错误,请各位大牛指出斧正!小弟感激不尽. 本篇文章为您分析一下原生JS写淘宝无缝轮播图效果 需求分析: ...
- js 函数的防抖(debounce)与节流(throttle) 带 插件完整解析版 [helpers.js]
前言: 本人纯小白一个,有很多地方理解的没有各位大牛那么透彻,如有错误,请各位大牛指出斧正!小弟感激不尽. 函数防抖与节流是做什么的?下面进行通俗的讲解. 本文借鉴:h ...
- js 函数的多图片懒加载(lazy) 带插件版完整解析
前言: 本人纯小白一个,有很多地方理解的没有各位大牛那么透彻,如有错误,请各位大牛指出斧正!小弟感激不尽. 本篇文章为您分析一下原生JS实现图片懒加载效果 页面需求 1 ...
- 好用的jquery.animateNumber.js数字动画插件
在做公司的运营报告页面时,有一个数字累计增加的动画效果,一开始,毫无头绪,不知如何下手,于是上网查资料,发现大多都是用的插件来实现的,那么今天,我也来用插件jquery.animateNumber.j ...
- Knockout.js 数据验证之插件版和无插件版
本文我们将介绍使用 Knockout.js 实现一些基本的数据验证.就如我们在标题里提到的,我们会使用两种方法来创建数据验证方法. 使用自定义方法,不需要任何插件 最简单的方法是使用已有的插件 如果你 ...
随机推荐
- python的字符串、列表、字典和函数
一.字符串 在python中字符串无需通过像php中的explode或者javascript中的split进行分解即可完成切片,可以直接通过下标获取字符串中的每一个字符,下标从0开始,如果从厚望签署, ...
- 7L-双线链表实现
链表是基本的数据结构,尤其双向链表在应用中最为常见,LinkedList 就实现了双向链表.今天我们一起手写一个双向链表. 文中涉及的代码可访问 GitHub:https://github.com/U ...
- Tcl编程第三天,数学运算
1.tcl语言没有自己的数学计算,如果想要使用数学公式,必须得用C语言的库.使用方法如下. #!/usr/bin/tclsh set value [expr 8/5] puts $value set ...
- 2017蓝桥杯日期问题(C++B组)
标题:日期问题小明正在整理一批历史文献.这些历史文献中出现了很多日期.小明知道这些日期都在1960年1月1日至2059年12月31日.令小明头疼的是,这些日期采用的格式非常不统一,有采用年/月/日的, ...
- OpenWrite 编辑器如何配置七牛云图床
感谢用户 mutiantong.cn 的创作分享,原文链接:https://www.jianshu.com/p/29f33ca6e491 1. 配置七牛云 1.1 通过七牛云链接购买七牛云对象存储, ...
- Thinking in Java,Fourth Edition(Java 编程思想,第四版)学习笔记(三)之Everything Is an Object
---恢复内容开始--- Both C++ and Java are hybird languages. A hybird language allow multiple programming st ...
- C#——继承
在某基类中声明 virtual 并在一个或多个派生类中被重新定义的成员函数称为虚函数. 虚函数的作用就是实现多态性(Polymorphism),多态性是将接口与实现进行分离. C#作为完全面向对象语言 ...
- python这门语言为什么要起这个名字
我只是一只可爱的小虫 前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:Liz喵 PS:如有需要Python学习资料的小 ...
- I. 蚂蚁上树
蚂蚁上树(Sauteed Vermicelli with minced Pork),又名肉末粉条,是四川省及重庆市的特色传统名菜之一.因肉末贴在粉丝上,形似蚂蚁爬在树枝上而得名.这道菜具体的历史,已不 ...
- java多线程3:原子性,可见性,有序性
概念 在了解线程安全问题之前,必须先知道为什么需要并发,并发给我们带来什么问题. 为什么需要并发,多线程? 时代的召唤,为了更充分的利用多核CPU的计算能力,多个线程程序可通过提高处理器的资源利用率来 ...