本文采取逐步深入的方式讲解原生JS封装运动函数的过程,

封装结果适用于元素大部分属性的运动,

运动方式将根据需求持续更新,目前主要支持常用的两种:匀速运动和缓冲运动。

阶段一、仅适用单位带px属性的匀速运动

效果图:



封装思路:

  1. 传入需要运动的属性attr、运动的目标值target_value、运动速度speed
  2. 对速度进行简单处理speed = speed || 5,即不传入速度参数值时,默认速度为5;
  3. 使用getComputedStyle()获取元素该属性的当前值now_value,通过target_value > now_value ? Math.abs(speed) : -Math.abs(speed);获取运动的方向;
  4. 通过Math.abs(target_value - now_value) <= Math.abs(speed)获取终止条件,需要终止时关闭定时器并将运动元素送至终点box_ele.style[attr] = target_value + "px";

完整代码:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
*{
margin: 0;
padding: 0;
}
.box{
width: 200px;
height: 200px;
background: skyblue;
position: absolute;
}
</style>
</head>
<body>
<div class="box"></div>
<script>
var timer = null;
function animate(target_value, attr, speed){
var now_value = parseInt(getComputedStyle(box_ele)[attr]);
speed = speed || 5;
speed = target_value > now_value ? Math.abs(speed) : -Math.abs(speed);
clearInterval(timer);
timer = setInterval(function(){
if(Math.abs(target_value - now_value) <= Math.abs(speed)){
box_ele.style[attr] = target_value + "px";
clearInterval(timer);
}else{
now_value += speed;
box_ele.style[attr] = now_value + "px";
}
}, 30)
} box_ele = document.querySelector(".box");
box_ele.addEventListener("mouseenter", function(){
animate(400, "left");
})
</script>
</body>
</html>

阶段二、可适用单位不带px属性(如opacity)的匀速运动

效果图:



封装思路:

  1. 在阶段一的基础上添加对元素运动属性的判定,若运动属性为opacity,需要对相关值进行处理;
  2. 由于默认速度为5,而opacity的范围为0~1,故需要对目标值和当前值进行处理,即now_value = parseInt(getComputedStyle(box_ele)[attr] * 100); target_value *= 100;
  3. 渲染元素运动效果时由opacity属性不带单位px,故也需要进行处理,即运动时 box_ele.style[attr] = now_value / 100;,终止时box_ele.style[attr] = target_value / 100;

完整代码:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
*{
margin: 0;
padding: 0;
}
.box{
width: 200px;
height: 200px;
background: skyblue;
position: absolute;
}
</style>
</head>
<body>
<div class="box"></div>
<script>
var timer = null;
function animate(target_value, attr, speed){
if(attr === "opacity"){
var now_value = parseInt(getComputedStyle(box_ele)[attr] * 100);
target_value *= 100;
}else{
var now_value = parseInt(getComputedStyle(box_ele)[attr]);
}
speed = speed || 5;
speed = target_value > now_value ? Math.abs(speed) : -Math.abs(speed);
clearInterval(timer);
timer = setInterval(function(){
if(Math.abs(target_value - now_value) <= Math.abs(speed)){
if(attr === "opacity"){
box_ele.style[attr] = target_value / 100;
}else{
box_ele.style[attr] = target_value + "px";
}
clearInterval(timer);
}else{
now_value += speed;
if(attr === "opacity"){
box_ele.style[attr] = now_value / 100;
}else{
box_ele.style[attr] = now_value + "px";
}
}
}, 30)
} box_ele = document.querySelector(".box");
box_ele.addEventListener("mouseenter", function(){
animate(0, "opacity");
})
</script>
</body>
</html>

阶段三、适用于多元素单一属性的匀速运动

效果图:



封装思路:

  1. 在阶段二的基础上添加参数ele,从而可以控制不同元素的运动;
  2. 将定时器放入运动元素对象中,即ele.timer = setInterval(function(){},避免相互之间造成干扰

完整代码:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
*{
margin: 0;
padding: 0;
}
.box{
width: 200px;
height: 200px;
background: skyblue;
margin-bottom: 10px;
}
</style>
</head>
<body>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<script> function animate(ele, target_value, attr, speed){
if(attr === "opacity"){
var now_value = parseInt(getComputedStyle(ele)[attr] * 100);
target_value *= 100;
}else{
var now_value = parseInt(getComputedStyle(ele)[attr]);
}
speed = speed || 5;
speed = target_value > now_value ? Math.abs(speed) : -Math.abs(speed);
// 定时器放入ele对象中,保证每个元素使用自己的定时器互不干扰
clearInterval(ele.timer);
ele.timer = setInterval(function(){
if(Math.abs(target_value - now_value) <= Math.abs(speed)){
clearInterval(ele.timer);
if(attr === "opacity"){
ele.style[attr] = target_value / 100;
}else{
ele.style[attr] = target_value + "px";
}
}else{
now_value += speed;
if(attr === "opacity"){
ele.style[attr] = now_value / 100;
}else{
ele.style[attr] = now_value + "px";
}
}
}, 30)
} var box_eles = document.querySelectorAll(".box");
document.body.onclick = function(){
animate(box_eles[0], 500, "width");
animate(box_eles[1], 200, "margin-left");
animate(box_eles[2], 0.2, "opacity");
}
</script>
</body>
</html>

阶段四、适用于多元素单一属性的匀速或缓冲运动

效果图:



封装思路:

  1. 在阶段三的基础上添加参数animate_mode,并设置animate_mode = "uniform_motion",即默认为匀速运动;
  2. 根据传入的运动方式计算速度,若传入的参数为"butter_motion",速度计算方法为speed = (target_value - now_value) / 10,即根据距离目标值的距离不断调整速度,越靠近目标点速度越慢;
  3. 注意速度计算需要放入定时器中,因为只有定时器中的now_value在不断变化。

完整代码:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
*{
margin: 0;
padding: 0;
}
.box{
width: 200px;
height: 200px;
background: skyblue;
margin-bottom: 10px;
}
</style>
</head>
<body>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<script> function animate(ele, target_value, attr, animate_mode = "uniform_motion", speed){
if(attr === "opacity"){
var now_value = parseInt(getComputedStyle(ele)[attr] * 100);
target_value *= 100;
}else{
var now_value = parseInt(getComputedStyle(ele)[attr]);
}
// 匀速运动模式下的速度
if(animate_mode === "uniform_motion"){
speed = speed || 5;
speed = target_value > now_value ? Math.abs(speed) : -Math.abs(speed);
}
clearInterval(ele.timer);
ele.timer = setInterval(function(){
// 缓冲运动模式下的速度
if(animate_mode === "butter_motion"){
// 根据距离目标值的距离不断调整速度,越靠近目标点速度越慢
speed = (target_value - now_value) / 10;
speed = target_value > now_value ? Math.abs(speed) : -Math.abs(speed);
}
if(Math.abs(target_value - now_value) <= Math.abs(speed)){
clearInterval(ele.timer);
if(attr === "opacity"){
ele.style[attr] = target_value / 100;
}else{
ele.style[attr] = target_value + "px";
}
}else{
now_value += speed;
if(attr === "opacity"){
ele.style[attr] = now_value / 100;
}else{
ele.style[attr] = now_value + "px";
}
}
}, 30)
} var box_eles = document.querySelectorAll(".box");
document.body.onclick = function(){
animate(box_eles[0], 500, "width", "butter_motion");
animate(box_eles[1], 200, "margin-left", "butter_motion");
animate(box_eles[2], 0.2, "opacity");
}
</script>
</body>
</html>

阶段五、适用于多元素多属性的匀速或缓冲运动

效果图:



封装思路:

  1. 在阶段四的基础上将属性参数attr、目标值参数target_value删除,替换为attr_obj
  2. 遍历传入的属性对象,对每个属性值进行处理,分别设置属性的目标值target_value和当前值now_value
  3. 在定时器中遍历传入的属性对象,逐个属性进行运动;
  4. 由于运动目标的不一致会让运动执行次数不同,有可能提前关闭定时器,故某条属性运动完成时删除该条属性数据,直到对象里没有属性,关闭定时器。

完整代码:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
*{
margin: 0;
padding: 0;
}
.box{
width: 200px;
height: 200px;
background: skyblue;
margin-bottom: 10px;
}
</style>
</head>
<body>
<div class="box"></div>
<script> function animate(ele, attr_obj, animate_mode = "butter_motion", speed){ // 默认运动方式为缓冲运动
// 遍历传入的属性对象,对每个属性值进行处理,分别设置属性的目标值和当前值
for(var attr in attr_obj){
attr_obj[attr] = {
// 考虑属性为“opacity”的特殊情况
target_value : attr === "opacity" ? attr_obj[attr] * 100 : attr_obj[attr],
now_value : attr === "opacity" ? parseInt(getComputedStyle(ele)[attr]) * 100 : parseInt(getComputedStyle(ele)[attr])
}
}
// 定时器都放入ele的对象中,保证每个元素使用自己的定时器互不干扰
clearInterval(ele.timer);
ele.timer = setInterval(function(){
// 遍历传入的属性对象,逐个属性进行运动
for(var attr in attr_obj){
// 匀速运动下的速度设置
if(animate_mode === "uniform_motion"){
// 匀速运动模式可以传入速度参数,不传入时默认为5
speed = speed || 5;
// 判断运动方向,即speed的正负
speed = attr_obj[attr].target_value > attr_obj[attr].now_value ? Math.abs(speed) : -Math.abs(speed);
}
// 缓冲运动下的速度设置
if(animate_mode === "butter_motion"){
// 根据距离目标值的距离不断调整速度,越靠近目标点速度越慢,且能判断运动方向
speed = (attr_obj[attr].target_value - attr_obj[attr].now_value) / 10;
// 速度的精确处理
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed)
}
// 终止条件
if(Math.abs(attr_obj[attr].target_value - attr_obj[attr].now_value) <= Math.abs(speed)){
ele.style[attr] = attr === "opacity" ? attr_obj[attr].target_value / 100 : attr_obj[attr].target_value + "px";
// 目标的不一致会让运动执行次数不同,有可能提前关闭定时器,故某条属性运动完成则删除对象里的属性数据
delete attr_obj[attr];
// 若对象里还存在属性,则继续运动(不关闭定时器)
for(var num in attr_obj){
return false;
}
// 直到对象里没有属性,关闭定时器
clearInterval(ele.timer);
// 运动条件
}else{
attr_obj[attr].now_value += speed;
ele.style[attr] = attr === "opacity" ? attr_obj[attr].target_value / 100 : attr_obj[attr].now_value + "px";
}
}
}, 30)
} var box_ele = document.querySelector(".box");
document.body.onclick = function(){
animate(box_ele, {
"width" : 103,
"height" : 402,
"opacity" : 0.3,
"margin-left" : 200
});
}
</script>
</body>
</html>

总结

至此运动函数已封装完成,该功能适用于大多数情况下元素的运动。

可以实现轮播图、萤火虫、放烟花、商品放大镜等多种效果。

Javascript之封装运动函数的更多相关文章

  1. Javascript作业—封装type函数,返回较详细的数据类型

    Javascript作业—封装type函数,返回较详细的数据类型 思路: 1 取typeof的值,如果是数字.函数等非对象类型,直接取类型 2 如果是object类型,则调用Object.protot ...

  2. 原生javascript封装的函数

    1.javascript 加载的函数 window.onload = function(){} 2.封装的id函数 function $(id) { return document.getElemen ...

  3. 第一百四十二节,JavaScript,封装库--运动动画和透明度动画

    JavaScript,封装库--运动动画和透明度动画 /** yi_dong_tou_ming()方法,说明 * * yi_dong_tou_ming()方法,将一个元素,进行一下动画操作 * 1,x ...

  4. JavaScript封装一个函数效果类似内置方法concat()

    JavaScript封装一个函数效果类似内置方法concat() 首先回忆concat()的作用: concat() 方法用于连接两个或多个数组.该方法不会改变现有的数组,而仅仅会返回被连接数组的一个 ...

  5. 原生JS封装时间运动函数

    /*讲时间运动之前先给大家复习一下运动函数 通常大家都会写运动框架,一个定时器(Timer),一个步长(step 就是每次运动的距离),一个当前位置(current)一个目标位置(target),然后 ...

  6. 运动函数封装(js)

    // 运动函数 function starMove(obj,json,fnEnd){ clearInterval(obj.timer); obj.timer  = setInterval(functi ...

  7. JavaScript--封装好的运动函数+旋转木马例子

    封装好的运动函数: 1.能控制目标元素的多种属性 2.能自动获取元素的样式表: 3.获取样式函数兼容 4.能对于元素的多属性进行动画(缓动动画)修改 5.能区分透明度等没单位的属性和px属性的变化 a ...

  8. jQuery编写插件--封装全局函数的插件(一些常用的js验证表达式)

    上一篇写到了jQuery插件的3种类型,介绍了第一种类型的写法--封装jQuery对象的方法插件.这一篇要介绍第二种插件类型:封装全局函数的插件:这类插件就是在jQuery命名空间内部添加函数:这类插 ...

  9. 第一百三十五节,JavaScript,封装库--拖拽

    JavaScript,封装库--拖拽 封装库新增1个拖拽方法 /** tuo_zhuai()方法,将一个弹窗元素实现拖拽功能 * 注意:一般需要在css文件将元素里的某一个区块光标设置成提示可以拖拽, ...

随机推荐

  1. X因素 开启它就能让你成为超级明星

    开启它就能让你成为超级明星" title="X因素 开启它就能让你成为超级明星"> "只要努力就能成为明星!"记得电影学院的不少老师都这样告诫学 ...

  2. Sed 实记 · laoless's Blog

    sed编辑命令 p 打印匹配行 = 打印文件行号 a 在定位行之后追加文本 i 在定位行之前插入文本 d 删除定位行 c 用新文本替换定位文本 s 使用替换模式替换相应模式 r 从另一个文件读取文本 ...

  3. IPFS初窥

    虽然区块链有很多令人兴奋的特性,但是也有其固有的缺点.比如,文件或者长度较长的文本信息就不适合存储在链上.那么如何解决这个缺点呢?一个解决方案就是IPFS(Interplanetary File Sy ...

  4. 先搞清楚这些问题,简历上再写你熟悉Java!

    原创声明 本文作者:黄小斜 转载请务必在文章开头注明出处和作者. 系列文章介绍 本文是<五分钟学Java>系列文章的一篇 本系列文章主要围绕Java程序员必须掌握的核心技能,结合我个人三年 ...

  5. Android Base64图片无法长按保存 问题解决

    踩了一个巨坑. 目前微信ios/android 均能长按保存src=base64的图片  (微信android x5 专门解决了这个问题); 但是android其他App没有针对解决这个系统问题(姑且 ...

  6. 如何给女朋友讲SSM和springBoot的区别?(简单描述一下)

  7. js中所有函数的参数(按值和按引用)都是按值传递的,怎么理解?

    我觉着我可能对这块有点误解,所以单独开个博说下自己的理解,当然是研究后的正解了. 1,参数传递是基本类型,看个例子: function addTen(num){ num += 10; return n ...

  8. Markdown使用说明

    # Markdown 使用说明 Markdown 是一种**轻量级标记语言** 使用规则: 1. 标题   2. 列表 3. 引用 4. 图片与链接 5. 粗体与斜体 6.表格 7. 代码框 8. 分 ...

  9. 数学-Matrix Tree定理证明

    老久没更了,冬令营也延期了(延期后岂不是志愿者得上学了?) 最近把之前欠了好久的债,诸如FFT和Matrix-Tree等的搞清楚了(啊我承认之前只会用,没有理解证明--),FFT老多人写,而Matri ...

  10. git回滚到任意一个版本

    1.首先查找提交的记录(-3表示显示最近的3条) git log -3 2.强制回滚到制定版本 git reset --hard 制定版本commitId 如:git reset --hard 4ba ...