本文采取逐步深入的方式讲解原生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. H5新增特性之语义化标签

    H5新增特性之语义化标签 语义化标签顾名思义标签有自己的含义,浏览器或者程序员一看就知道是什么.在HTML 5出来之前,我们用div来表示页面章节,但是这些div都没有实际意义.(即使我们用css样式 ...

  2. HttpClientFactory的套路,你知多少?

    背景 ASP.NET Core 在 2.1 之后推出了具有弹性 HTTP 请求能力的 HttpClient 工厂类 HttpClientFactory. 替换的初衷还是简单摆一下: ① using(v ...

  3. 小程序开发技巧(三)-- 云开发时效数据刷新和存储 (access_token等)

    小程序云开发时效数据刷新和存储 (access_token等) 1.问题描述 小程序中经常有需要进行OCR识别,或者使用外部api例如百度AI识别等接口,请求调用这些接口需要令牌,即一些具有时效性的数 ...

  4. Flutter01-学习准备

    1. 简介: Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面. Flutter可以与现有的代码一起工作.在全世界,Flutter正在被越来越多的开发者和 ...

  5. Nginx之反向代理配置(二)

    前文我们聊了Nginx的防盗链.反向代理以及开启nginx代理缓存,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/12417130.html:今天我们继续说ng ...

  6. 【开发技巧】再见,BLE的那些坑!

    蓝牙,平常你用的多吗?上班路上戴着蓝牙耳机听音乐.看视频打开蓝牙分享个人热点给小伙伴们解锁共享单车时,打开蓝牙就能迅速解锁...... BLE-蓝牙低能耗技术,方便了我们的生活,但是开发者在开发过程中 ...

  7. CSS+JS相应式导航菜单

    响应式导航菜单 响应式导航菜单就是当网页在其他不同视口的样式,不同的设备需要不同的样式 需要掌握的知识 - 掌握媒体查询,如果你不是很懂那就看我写的CSS响应式布局 掌握CSS重的display:no ...

  8. JZOJ 3929. 【NOIP2014模拟11.6】创世纪

    3929. [NOIP2014模拟11.6]创世纪 (Standard IO) Time Limits: 1000 ms Memory Limits: 65536 KB Description 上帝手 ...

  9. Nuxt简单使用Google/Baidu Analyze

    博客地址: https://www.seyana.life/post/17 具体账号注册方法和绑定方法可以去到官网下,都有相应的指南, 一般设置也比较简单,只需要把对应js代码添加到head中即可, ...

  10. LVM简介及CentOS7 LVM操作实战

    LVM简介LVM是逻辑盘卷管理(LogicalVolumeManager)的简称,它是Linux环境下对磁盘分区进行管理的一种机制,LVM是建立在硬盘和 分区之上的一个逻辑层,来提高磁盘分区管理的灵活 ...