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 实现一些基本的数据验证.就如我们在标题里提到的,我们会使用两种方法来创建数据验证方法. 使用自定义方法,不需要任何插件 最简单的方法是使用已有的插件 如果你 ...
随机推荐
- 高性能/并发的保证-Netty在Redisson的应用
前言 Redisson Github: https://github.com/redisson/redisson Redisson 官网:https://redisson.pro/ Redis ...
- codeblocks 的安装与初体验
下载 链接 安装 点击默认安装即可! 配置 自带编译器还报错!! 大多数自带编译器的codeblocks安装后无法找到编译器的解决方案 点击Settings->Compiler,找到Toolc ...
- Taro Next H5 跨框架组件库实践
作者:凹凸曼 - JJ Taro 是一款多端开发框架.开发者只需编写一份代码,即可生成各小程序端.H5 以及 React Native 的应用. Taro Next 近期已发布 beta 版本,全面完 ...
- flask-script的基本使用
Flask-Script flask-script的作用是可以通过命令行的形式来操作Flask.例如通过命令跑一个开发的服务器.设置数据库等. 命令的添加方式 1 .使用manage.command: ...
- C++语言实现双向链表
这篇文章是关于利用C++模板的方式实现的双向链表以及双向链表的基本操作,在之前的博文C语言实现双向链表中,已经给大家分析了双向链表的结构,并以图示的方式给大家解释了双向链表的基本操作.本篇文章利用C+ ...
- Golang源码分析之目录详解
开源项目「go home」聚焦Go语言技术栈与面试题,以协助Gopher登上更大的舞台,欢迎go home~ 导读 学习Go语言源码的第一步就是了解先了解它的目录结构,你对它的源码目录了解多少呢? 目 ...
- VirtualBox的四种网络连接方式【转】
VirtualBox中有4中网络连接方式: NAT Bridged Adapter Internal Host-only Adapter VMWare中有三种,其实他跟VMWare 的网络连接方式都是 ...
- java消除 list重复值及交集,并集,差集
消除 list重复值 Java代码 public void removeDuplicate(List list) { HashSet h = new HashSet(list); list.clea ...
- AJ学IOS 之ipad开发Popover的调色板应用_popover显示后其他控件仍然能进行交互
AJ分享,必须精品 一:效果 后面的是xcode的控制台 二:代码 ViewController #import "ViewController.h" #import " ...
- C# 基础知识系列- 10 反射和泛型(二)
0. 前言 这篇文章延续<C# 基础知识系列- 5 反射和泛型>,继续介绍C#在反射所开发的功能和做的努力.上一篇文章大概介绍了一下泛型和反射的一些基本内容,主要是通过获取对象的类型,然后 ...