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 实现一些基本的数据验证.就如我们在标题里提到的,我们会使用两种方法来创建数据验证方法. 使用自定义方法,不需要任何插件 最简单的方法是使用已有的插件 如果你 ...
随机推荐
- 新安装的eclipse配置好了环境变量后,打开还是出现A Java runtime environment错误
新安装的eclipse配置好了环境变量后,打开还是出现如下图的A Java runtime environment错误; 解决方法: 第一步: Windows环境下:把C:\Users\你的用户名 目 ...
- raphael.js 使用指南
RaphaelJS是一个用JavaScript实现的强大的矢量图形库. (1)使用前准备,下载RaphaelJS,到官网下载. (2)在相应的HTML页面引入RaphaelJS,如下示例代码: < ...
- find的基本查询命令《二》
Linux find命令详解 由于find具有强大的功能,所以它的选项也很多,其中大部分选项都值得我们花时间来了解一下.即使系统中含有网络文件系统( NFS),find命令在该文件系统中同样有效,只你 ...
- 打造一款 刷Java 知识的小程序(二)
学习Java的神器已上线,面向广大Java爱好者! 之前写的一篇:打造一款 刷Java 知识的小程序(一) 一.第二版做了什么? 第一版小程序只具有初级展示功能,知识点都是hardcode在代码里面的 ...
- Linux网络架设篇,虚拟机l系统中网卡设备名与配置文件不符如何处理?
很多情况下,当我们在虚拟机中安装好linux系统后,并不能成功连上网.当我们配置好相关IP地址后同样不能成功连接网络.并且会体会网卡名与配置名不符,这时候应该怎么办呢? 1.清空下面文件 /etc/u ...
- Golang Web入门(1):自顶向下理解Http服务器
摘要 由于Golang优秀的并发处理,很多公司使用Golang编写微服务.对于Golang来说,只需要短短几行代码就可以实现一个简单的Http服务器.加上Golang的协程,这个服务器可以拥有极高的性 ...
- MySQL学习之路3-MySQL中常用数据类型
MySQL中常用数据类型 字符型 存储字符型数据.例如姓名,地址,电话号码等.使用引号括起来,一般使用单引号. 常用类型: char(255) 定长字符串,最大长度255个字符. varchar(25 ...
- 接口请求:get、post (requests方法)
https://www.cnblogs.com/lanyinhao/p/9634742.html 比较全面 1.模块说明 requests是使用Apache2 licensed 许可证的HTTP库. ...
- 23 抽象类 abstract
/*概念 * abstract:关键字,用于修饰方法和类 * 抽象方法:不同类的方法是相似,但是具体内容又不太一样,所以我们只能抽取他的声明,没有具体的方法体,没有具体方法体的方法就是抽象方法 * 抽 ...
- "文本加粗"组件:<b> —— 快应用组件库H-UI
 <import name="b" src="../Common/ui/h-ui/text/c_tag_b"></import> &l ...