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 实现一些基本的数据验证.就如我们在标题里提到的,我们会使用两种方法来创建数据验证方法. 使用自定义方法,不需要任何插件 最简单的方法是使用已有的插件 如果你 ...
随机推荐
- Win32程序:与"LPCWSTR"类型的形参不兼容
出现该问题的原因是通常手动输入的字符串都是LPCSTR类型的, 解决办法如下: 在工程处右键,属性-常规-字符集,将Unicode字符集改为为多字节字符集,应用并确认即可. 字符串常量报错: 在常 ...
- MVC分层设计
MVC分层设计 什么是MVC? MVC 是一种软件架构模式,利用分层的思想来设计交互式应用程序,由以下3层组成: Model 业务模型层. View 展示层. Controller 控制层. MVC包 ...
- zabbix模板的自动发现规则(ldd)实现被监控项自动发现
zabbix模板的自动发现规则(ldd)实现被监控项自动发现 自动发现规则(ldd)用途说明 在zabbix自带的linux模板的自动发现规则中,有一个Mounted filesystem disco ...
- Adb adb push (remote write failed: No space left on device)
修改完成程序后, mm 后, 准备要push 进到公司测试手机里面的.之前还真的没有遇到过这个问题,查了一下, 应该是手机没空间了的 sudo adb root sudo adb remount su ...
- Java第十八天,可变参数
可变参数 1.使用前提 当一个方法的参数需要多个参数,并且这些参数的类型一致时,可以使用可变参数. 2.使用方法 定义方法时使用 3.定义格式 修饰符 返回值类型 方法名(参数类型...变量名){ } ...
- 在SpringBoot中使用SpringSecurity
@ 目录 提出一个需求 解决方案: 使用SpringSecurity进行解决 SpringSecurity和SpringBoot结合 1. 首先在pom.xml中引入依赖: 2. 配置用户角色和接口的 ...
- [转] [知乎] 浅谈Roguelike
浅谈Roguelike 从柏林诠释说起 在2008年召开的国际Roguelike开发会议上,众多的Roguelike开发者与爱好者共同制定了<柏林诠释>,规定了Roguelike游戏需要具 ...
- 004-流程控制-C语言笔记
004-流程控制-C语言笔记 学习目标 1.[掌握]关系运算符和关系表达式 2.[掌握]逻辑运算符和逻辑表达式 3.[掌握]运算符的优先级和结合性 4.[掌握]if-else if-else结构的使用 ...
- Nginx+uWSGI+Python+Django构建必应高清壁纸站
写在前面 做这个网站的初衷是因为,每次打开必应搜索搜东西的时候都会被上面的背景图片吸引,我想必应的壁纸应该是经过专业人员精选出来的,我甚至会翻看以前的历史图片,唯一美中不足的是必应的首页只能查看最多7 ...
- Linux 提高操作效率之 tab 命令补全
最近在使用阿里云 ECS 时,发现 Centos 无法进行 tab 补全,特别影响操作效率,本文简单记录下 Linux 下的 tab 命令补全功能,希望对 Linux 初学者有所帮助. 安装 Linu ...