本文采取逐步深入的方式讲解原生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. linux记录每次登陆的历史命令

    编辑/etc/profile,增加如下代码 #Record history operation USER_IP=`>/dev/null |awk '{print $NF}' |sed -e 's ...

  2. OpenSSL 生成自定义证书

    前言 本文用来记录通过OpenSSL生成自定义证书并在浏览器设置可信任 准备 Linux CentOS7 系统 nginx 1.12.2 Windows 10 IE 11 chrome 71 Open ...

  3. Intellij IDEA创建 Web 项目

    快速构建 Web 项目 打开IDEA,新建Project,左边菜单栏选择 Maven,直接点 Next 选择GroupId和ArtifactId 选择项目名称,默认会填上工程位置.模块姓名等,直接点F ...

  4. Flutter Widgets 之 ListWheelScrollView

    注意:无特殊说明,Flutter版本及Dart版本如下: Flutter版本: 1.12.13+hotfix.5 Dart版本: 2.7.0 基础用法 在展示大量数据的时候我们第一会想到使用ListV ...

  5. Python学习笔记--gevent嵌套使用

    这篇主要是接着上篇的,实验gevent嵌套使用,看情况如何.还是先上代码. #!/usr/bin/env python # -*- coding: utf-8 -*- # @Date : 2020-0 ...

  6. 前端每日实战:4# 视频演示如何用纯 CSS 创作一个金属光泽 3D 按钮特效

    效果预览 按下右侧的"点击预览"按钮在当前页面预览,点击链接全屏预览. https://codepen.io/zhang-ou/full/MGeRRO 可交互视频教程 此视频是可以 ...

  7. Yuchuan_Linux_C编程之九目录操作相关函数

    一.整体大纲 二.相关函数 1. getcwd 函数作用:获取当前目录 头文件 #include <unistd.h> 函数原型 char *getcwd(char *buf, size_ ...

  8. ASP.NET Core身份认证服务框架IdentityServer4 介绍

    IdentityServer4是ASP.NET Core 2的OpenID Connect和OAuth 2.0框架.它可以在您的应用程序中提供以下功能: 它使你的应用程序具有如下特点: 认证即服务 适 ...

  9. [NodeJS] async 和 await 的本质

    ​    绝大多数nodejs程序员都会使用 async 和 await 关键字,但是极少有人能真正弄明白 async 和 await 的原理.这篇文章将从零“构建”出 async 和 await 关 ...

  10. 面试总被问分布式ID怎么办? 滴滴(Tinyid)甩给他

    整理了一些Java方面的架构.面试资料(微服务.集群.分布式.中间件等),有需要的小伙伴可以关注公众号[程序员内点事],无套路自行领取 一口气说出 9种 分布式ID生成方式,面试官有点懵了 面试总被问 ...