本文采取逐步深入的方式讲解原生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. 听起来很美,用起来很累!停车类APP软肋在哪

    据数据显示,全国现有汽车已达1亿7千万辆,停车位缺于6800万个.而在北京,汽车保有量和车位的配比大约是1:0.5,而国际上一般是1:1.2,结构严重失衡.正所谓哪里有需求,哪里就有市场.停车位的走俏 ...

  2. YOLO 论文阅读

    YOLO(You Only Look Once)是一个流行的目标检测方法,和Faster RCNN等state of the art方法比起来,主打检测速度快.截止到目前为止(2017年2月初),YO ...

  3. Quartz Tutorial 11 - Miscellaneous Features of Quartz

    文章目录 Plug-Ins Quartz提供了一个接口(org.quartz.spi.SchedulerPlugin) 用于插入附加的功能. 与Quartz一同发布的,提供了各种实用功能的插件可以在o ...

  4. textarea 实现高度自动增长

    有时候希望textarea 能够自动调整高度来适应输入的内容 网上看到了很多解决方案,比如动态创建一个隐藏的div,当用户输入的时候将textarea的内容绑定到div,由于div的高度会自动撑开,因 ...

  5. 百度地图API:使用百度定位

    准备工作: 1.申请百度地图API 2.下载百度地图的SDK 3.将SDK包中的BaiduLBS_Android.jar文件放到,项目里的app/libs里面 4.在src/main目录下创建一个名为 ...

  6. Spring编译后没有xml配置文件解决方法

    问题描述 在使用Maven来构建Spring项目的时候,使用下面代码来读取Spring配置文件. ClassPathXmlApplicationContext context = new ClassP ...

  7. .Net Core调用oracle存储过程

    一 前言 实战踩坑系列,调用第三方Oracle存储,各种血泪史,现记录如下. 二 入坑 首先,调用Oracle需要安装客户端驱动才行,但是在程序开发中下载客户端驱动是一个不明智的选择.于是,不管是微软 ...

  8. Typora[MarkDown编辑器]+(PicGo+Github+JsDelivr)[个人图床] ,开启你的高效创作

    使用Typora搭配Picgo开启你的高效创作 0x00 一切都要从MarkDown说起 富文本语言的弊端 平常我们最常用的写作工具,无非是富文本编辑器中的代表--微软家的Office Word.这种 ...

  9. linux构建DHCP服务器

    1.DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)是一个局域网的网络协议,使用UDP协议工作,主要用途:给内部网络或网络服务供应商自动分配IP地址 ...

  10. Eureka在有虚拟网卡的情况下获取正确的IP

    发现问题 最近项目在Eureka注册时,发现一个问题:注册的IP地址不是 192.168.0.XXX 的网络IP,而是另外一个网段的地址,如图 通过 ipconfig 命令查看本机的IP地址发现,该I ...