本文采取逐步深入的方式讲解原生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. 查漏补缺:socket编程:TCP粘包问题和常用解决方案(上)

    1.TCP粘包问题的产生(发送端) 由于TCP协议是基于字节流并且无边界的传输协议,因此很容易产生粘包问题.TCP的粘包可能发生在发送端,也可能发生在接收端.发送端的粘包是TCP协议本身引起的,TCP ...

  2. 开始使用Github

     Gather ye rosebuds while ye may 我自己也是刚开始使用github没几天,写得不好我就写自己常用的吧 2015年9月20日下午3:19更新知乎上这个答案写得好多了

  3. qt creator源码全方面分析(3)

    目录 项目文件分析 qtcreator.pro 包含qtcreator.pri Qt版本判断 包含doc.pri 源码组织架构 指定dist文件列表 qbs配置 指定架构和平台 指定基础名 指定lin ...

  4. Spring,SpringMVC,MyBatis,SSM配置文件比较

    Spring配置文件: applicationContext.xml applicationContext.xml是Spring的核心配置文件 IOC/DI,AOP相关配置都是在这个文件中 Sprin ...

  5. mysql in与exists区别

    1.exists是对外表做loop循环,每次loop循环再对内表(子查询)进行查询,那么因为对内表的查询使用的索引(内表效率高,故可用大表),而外表有多大都需要遍历,不可避免(尽量用小表),故内表大的 ...

  6. React Native 在 Airbnb(译文)

    在Android,iOS,Web和跨平台框架的横向对比中,React Native本身是一个相对较新且快速开发移动的平台.两年后,我们可以肯定地说React Native在很多方面都是革命性的.这是移 ...

  7. 关于Html+css阶段学习总结

    一.学习经历 进入大学不久,就加入了社团,从而对前端有了一个初步的了解,之后也做过一些学校的官网,积累了一些微小的经验. 到了大二的时候,学校开设了专门的html+css课程,从中也学到许多新的htm ...

  8. 一步步去阅读koa源码,整体架构分析

    阅读好的框架的源码有很多好处,从大神的视角去理解整个框架的设计思想.大到架构设计,小到可取的命名风格,还有设计模式.实现某类功能使用到的数据结构和算法等等. 使用koa 其实某个框架阅读源码的时候,首 ...

  9. Service Mesh - gRPC 本地联调远程服务

    Description Service Mesh 架构下,服务间调用会通过服务名(Service Name)互相调用,比如在 Kubernetes .Docker Swarm 集群中,服务 IP 均由 ...

  10. Xmind快速入门(基本操作够用了)

    先选择结构--再选择风格 快捷键: 1.tab (产生子主题)2.enter (在下方产生并列主题) shift+enter (在上方产生并列主题)3.Alt+Enter (给某个主题添加标注)4.按 ...