运动函数
封装运动函数
在事件位置调用就可以让位置,长宽,透明度等批量运动
- html代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 给div添加内部样式 -->
<style>
*{
margin: 0;
padding: 0;
}
div{
width: 100px;
height: 100px;
background: springgreen;
position: absolute;
top: 100px;
left: 100px;
cursor: pointer;
opacity: 1;
}
</style>
</head>
<body>
<div></div>
</body>
</html>
- js代码
// 拿到div标签并且赋值给常量box
const box = document.querySelector("div")
// 给div标签添加一个点击事件
box.onclick = function(){
// 调用封装的运动函数
move(this,"left",300)
move(this,"top",500)
move(this,"opacity",1)
}
/* 创建一个运动函数,该函数有三个参数为ele(不同的元素),
type(标签的样式类型),target(目标,即运动最后达到的值) */
function move(ele,type,target){
// 判断样式类型是否为opacity(透明度)
if(type === "opacity"){
// 是则让目标放大100倍
target *= 100
}
// 设置一个每16毫秒运行一次的定时器并且储存在变量timer
let timer = setInterval(() => {
// 定义变量current用来储存运动的样式现在所在的位置,或者值
let current
if(type === "opacity"){
// 是则将div现在的透明度样式的值放大100倍赋值给current
current = window.getComputedStyle(ele)[type] * 100
}else{
// 不是将转为判定div其他样式现在的值赋给current
current = parseInt(window.getComputedStyle(ele)[type])
}
// 定义变量step储存每一次变换的大小,整体意为每次改变剩余未变换的百分之10
let step = (target - current) / 10
/* 因为上面一句导致在判定运动最后是否到达目标的时候是无
限接近相等,即最后的step无限小,因此需要将正小数判定时
候向上取整,负小数向下取整。不理解的可以在控制台打印step
观察一下step值的变化。 */
step = step > 0?Math.ceil(step):Math.floor(step)
if(current == target){
// 到达目标就可以清空定时器,停止运动
clearInterval(timer)
timer = null
}else{
if(type === "opacity"){
// 因为样式不带px所以要单独判断给样式赋值
ele.style[type] = (current + step) / 100
}else{
ele.style[type] = current + step + "px"
}
}
}, 16);
}
- 最后效果在网页相对浏览器可视区域左上角的向右100px,向下100px的位置看到鼠标箭头变成小手,点击一下左键,方块会由完全透明变的不透明,最终移动到相对浏览器可视区域左上角的向右300px,向下500px。
升级版封装运动函数
上面的运动函数每让一个属性发生改变,即运动,就要调用一次封装函数,非常的麻烦且不银杏化,因此我们对此进行了一点小升级。
- html方面没有变化,可以继续沿用上面的
- js代码
const box = document.querySelector("div")
box.onclick = function () {
/* 多属性移动 => 一次可以移动跟多个属性 */
moveFn(this, {
"opacity": 1,
'width': 600,
'height': 400,
'top': 400,
'left': 600
})
}
/* 这里参数的元素不变,但是我们发现属性,和属性值
对应的都是同一个元素,那我们为什么不能把一个元素(标签)
的属性以及属性值用对象以key跟value的方式收集起来,
这样传参数的时可以少传一个,调用的时候也可以一次调用多属性 */
function moveFn(ele, moveObj) {
//定义一个变量
let count = 0;
/* 遍历moveObj对象,拿出收集的属性及属性值,
在对象中key对应属性,value对应属性值 */
for (let key in moveObj) {
count ++;
// 如同上一条注释,一一对应就一一赋值,封装的运动函数其他不变
let type = key
let target = moveObj[key]
if (type == 'opacity') {
target *= 100;
}
let timer = setInterval(() => {
let current
if (type == 'opacity') {
current = window.getComputedStyle(ele)[type] * 100;
} else {
current = parseInt(window.getComputedStyle(ele)[type])
}
let step = (target - current) / 10
step = step > 0 ? Math.ceil(step) : Math.floor(step)
if (current == target) {
clearInterval(timer)
timer = null;
count --
if(count === 0){
console.log("代码执行完毕!");
}
} else {
if (type === 'opacity') {
ele.style[type] = (current + step) / 100;
} else {
ele.style[type] = current + step + "px";
}
}
}, 16);
}
}
小总结
运动函数实现的方法跟思路有很多,这只是其中一种。
在进化版中可能有细心的朋友发现了还增添一个变量 count
,这个变量是用来判定一个元素的运动什么时候执行完毕的。
正常情况下一个元素有几个属性要同时变化,那么在清除定时器的后面添加 console.log("代码执行完毕")
会发现在控制台,这句话有几条属性,就被执行了几次,无法判断元素的一次执行完毕。
因此,在遍历对象前定义一个变量 count
并且赋值为0,当遍历一次对象,让 count
的值加 1 ,当对象中的属性及属性值全部拿出来进入定时器,这时候,每清除一次定时器,再让 count
的值减 1 ,这时候就可以用 count
是否为零来判断一个元素的一次执行完毕。
为什么要加这个东西捏,因为这是在为轮播图的编写做准备。