1.offset家族补充
◆offset取值返回时一个number类型的数值,但是它不会返回带小数的整数,内部做了四舍五入的处理,如style.left=9.4px,offsetLeft获取到的值是9,然而style.left=9.5px,offsetLeft获取到的值是10。
2.缓速动画与匀速动画的原理
◆匀速动画的原理:this.style.left=this.offsetLeft+步长(也就是每次递增多少递减多少),步长的递增和递减取决于 指定的位置是大于当前位置还是小于当前位置。
◆缓速动画的原理:this.style.left=this.offsetLeft+步长,这个步长是动态的,speed=(指定的位置-this.offsetLeft)/10,因为只有这样 速度才会从快的到慢,但是可能会出现 当步长过小时,this.offsetLeft从this.style.left哪里获取到的值被四舍五入了,然后造成永远达不到指定的位置并且定时器也无法清除,解决的方法是,每次都对步长进行向上或者向下取舍。
//每次移动的步长
element.spend = (target - element.offsetLeft) / 10
//对步长进行取整,避免offsetLeft底层的四舍五入的影响
element.spend = element.spend > 0 ? Math.ceil(element.spend) : Math.floor(element.spend);
这样就能够达到,当步长大于0就向上取整,就不担心offsetLeft四舍五入掉0.5以下的值了,如果步长为负数时,也能够做到-0.5直接向下取整变成-1,于是就解决了指定的位置小于当前位置而造成的不移动并且定时器不停的问题,判断当前的位置和指定的位置是否符合停止定时器的核心代码
//目标位置减去当前位置的绝对值 如果小于步长的绝对值 那么就说明也就一步之遥了,
// 那么直接移动到指定的位置清除计时器算了。
if (Math.abs(target - element.offsetLeft) <= Math.abs(element.spend)) {
element.style.left = target + "px";
clearInterval(element.timer);
return;
}
都是判断左右步长是否小于或者等于当前位置距离指定位置的长度,如果符合条件那么可以直接停止计时器了,这一点无论是匀速还是缓速动画都是一样的。
3.使用offsetLeft和style.left来DIY动画补充
◆封装基础的缓速动画简单版
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用offset封装缓速动画</title>
<style type="text/css">
#box {
height: 200px;
background-color: #eee;
position: relative;
}
#box1 {
position: absolute;
top: 50px;
width: 100px;
height: 100px;
background-color: #f09;
}
</style>
</head>
<body>
<div id="box">
<button id="btn1">移动到200</button>
<button id="btn2">移动到600</button>
<div id="box1"></div>
</div>
<script>
//需求:当点击移动到200时 盒子移动到200 当点击移动到600时 盒子移动到600(要求是缓速而不是匀速或闪速)
//思路: 匀速:动画原理=盒子原来的位置+步长
// 缓速:动画原理=盒子原来的位置+(指定位置-盒子原来的位置)/10
// 缓速的步长=(指定位置-盒子原来的位置)/10;
//步骤:
// 1.获取事件源及相关元素对象
// 2.绑定事件
// 3.书写事件驱动程序
// 1.获取事件源及相关元素对象
var btn1 = document.getElementById("btn1");
var btn2 = document.getElementById("btn2");
var box1 = document.getElementById("box1");
// 2.绑定事件
btn1.onclick = function () {
// 3.书写事件驱动程序
animate(box1, 200);
//
// //绑定计时器之前先清除定时器
// clearInterval(box1.timer);
// box1.timer = setInterval(function () {
//
// //bug:offsetLeft取值会进行四舍五入,所以只要spend为0.5以下
// //就会导致 box1.style.left属性转换为box1.offsetLeft时永远都是向下取整
// //如box1.style.left=9.4px,那么box1.offsetLeft获取到的值永远都是9
// //这样就会导致(200-box1.offsetLeft<spend)取值永远都为false,盒子也不会移动,
// //定时器也不会被清除掉
//
//
// //每次移动的步长
// var spend = (200 - box1.offsetLeft) / 10
//
// //对步长进行取整,避免offsetLeft底层的四舍五入的影响
// spend = spend > 0 ? Math.ceil(spend) : Math.floor(spend);
//
// if (Math.abs(200 - box1.offsetLeft) <= Math.abs(spend)) {
// box1.style.left = "200px";
// clearInterval(box1.timer);
// return;
// }
//
// //重新设置距离左边的位置
// box1.style.left = box1.offsetLeft + spend + "px";
// }, 30);
}
btn2.onclick = function () {
// 3.书写事件驱动程序
animate(box1, 600);
}
//缓素动画
function animate(element, target) {
//绑定计时器之前先清除定时器
clearInterval(element.timer);
element.timer = setInterval(function () {
//bug:offsetLeft取值会进行四舍五入,所以只要spend为0.5以下
//就会导致 box1.style.left属性转换为box1.offsetLeft时永远都是向下取整
//如box1.style.left=9.4px,那么box1.offsetLeft获取到的值永远都是9
//这样就会导致(200-box1.offsetLeft<spend)取值永远都为false,盒子也不会移动,
//定时器也不会被清除掉
//每次移动的步长
element.spend = (target - element.offsetLeft) / 10
//对步长进行取整,避免offsetLeft底层的四舍五入的影响
element.spend = element.spend > 0 ? Math.ceil(element.spend) : Math.floor(element.spend);
//目标位置减去当前位置的绝对值 如果小于步长的绝对值 那么就说明也就一步之遥了,
// 那么直接移动到指定的位置清除计时器算了。
if (Math.abs(target - element.offsetLeft) <= Math.abs(element.spend)) {
element.style.left = target + "px";
console.log(element.style.left)
clearInterval(element.timer);
return;
}
//重新设置距离左边的位置
element.style.left = element.offsetLeft + element.spend + "px";
console.log(element.style.left)
}, 30);
}
</script>
</body>
</html>
◆封装基础的缓速动画正常版
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用offset封装缓速动画</title>
<style type="text/css">
#box {
height: 200px;
background-color: #eee;
position: relative;
}
#box1 {
position: absolute;
top: 50px;
width: 100px;
height: 100px;
background-color: #f09;
}
</style>
</head>
<body>
<div id="box">
<button id="btn1">移动到200</button>
<button id="btn2">移动到600</button>
<div id="box1"></div>
</div>
<script>
//需求:当点击移动到200时 盒子移动到200
// 当点击移动到600时 盒子移动到600(要求是缓速而不是匀速或闪速)
//思路: 匀速:动画原理=盒子原来的位置+步长
// 缓速:动画原理=盒子原来的位置+(指定位置-盒子原来的位置)/10
// 缓速的步长=(指定位置-盒子原来的位置)/10;
// 判断的时候可能步长为正数或者负数,
// 所以判断距离是否合适时都要使用绝对值来进行判断
//步骤:
// 1.获取事件源及相关元素对象
// 2.绑定事件
// 3.书写事件驱动程序
// 1.获取事件源及相关元素对象
var btn1 = document.getElementById("btn1");
var btn2 = document.getElementById("btn2");
var box1 = document.getElementById("box1");
// 2.绑定事件
btn1.onclick = function () {
// 3.书写事件驱动程序
animate(box1, 200);
}
btn2.onclick = function () {
// 3.书写事件驱动程序
animate(box1, 600);
}
//缓速动画
function animate(element, target) {
//绑定计时器之前先清除定时器
clearInterval(element.timer);
element.timer = setInterval(function () {
//注意:this.offsetLeft最终获取的是number类型的值,并且它还会对小数进行四舍五入,
//如果this.style.left中有小数,并且还低于0.5,那么就会导致offsetLeft每次获取到的值,
//都是一样,然后就会造成 盒子不懂,定时器不停的现象发生,所以缓速动画时要记得给步长取整
// 如果步长>0就向上取整,避免步长小于0.5时出现bug,
// 如果步长<0,就向下取整,避免步长下雨-0.5时出现bug。
//每次移动的步长
element.spend = (target - element.offsetLeft) / 10
//对步长进行取整,避免offsetLeft底层的四舍五入的影响
element.spend = element.spend > 0 ? Math.ceil(element.spend) : Math.floor(element.spend);
//目标位置减去当前位置的绝对值 如果小于步长的绝对值 那么就说明也就一步之遥了,
// 那么直接移动到指定的位置清除计时器算了。
if (Math.abs(target - element.offsetLeft) <= Math.abs(element.spend)) {
element.style.left = target + "px";
console.log(element.style.left)
clearInterval(element.timer);
return;
}
//重新设置距离左边的位置
element.style.left = element.offsetLeft + element.spend + "px";
console.log(element.style.left)
}, 30);
}
</script>
</body>
</html>
4.使用缓速动画制作,导航背景动画-绿色的筋斗云效果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>导航背景动画-绿色的筋斗云</title>
<style type="text/css">
body, ul, li, a, i {
padding: 0;
margin: 0;
}
body {
background-color: rgba(0, 0, 0, 0.8);
}
.box {
width: 800px;
height: 42px;
border-radius: 8px;
position: relative;
background: #fff url(data:image/png;base64,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) right center no-repeat;
margin: 150px auto;
}
ul {
list-style: none;
position: relative;
}
li {
float: left;
width: 83px;
height: 42px;
font: 400 16px/42px "simsum";
text-align: center;
}
i {
position: absolute;
width: 83px;
height: 42px;
background: url(data:image/gif;base64,R0lGODlhUwAqAMQfAMLghfL45aHQRNHoouXyy7ncdM3mmqXSSur11e32273ee67WXfj78fT66p3OO9brreHwxMXii9rttarUVbXaa8nklN7vvf3++vv9+L/fgKfTT+Dvwe/34P///5nMMyH5BAEAAB8ALAAAAABTACoAAAX/4CeOZGmeZKeubOt2aCzPdOoGyPMYQA8Ui4XCBxg8IIiAq8ZsiliNDW+h8Viv2Kz2KqxIlCuneMRKDAqCrXrN9ggKA0J4TFNhNpl0e8/XCgAQc3Q2HQQKDn2JilgCEQ0qgx8qFw9Vi5eXDhkJkGIqDweYoqIADDBMKgQTo6yYAoGnMh0YEa22mAOdKB0BC7e/i7kxHQl6wMd8cifExsjOawemJbOrz9ZrCrFPGdfdaoFkCd7jWRoXpx0F5OtWDzAdDOzsE+8S8uxyHQD36wAqFPzIaVDhK+A4UwUNdkPQAaDCboFqPbwW6MHEawwDXLQWQFLCjb8EvIMA8lgBdOpArQpYkKFHhAEAFCyoxscCugbN5GkoYMACgnMvWBAAkDOLgHNkOBS1dkBBHFMt6qgwsEZYCg40nTX9EpWOCgVbDiCddmEAol8TAHAVFOnJBUtYwC3r0KDCUj4H4BDAwKLtsAFZ/M1YgWCAgqxGgwCosOFRX7+DEWBZwBcVCxwIMv8MCtkJrysHHg160dlvB8luOJVebdqiANWsY3ulcICDNtm46/QSnbt3nb23SYQAADs=) 0 0 no-repeat;
left: 0;
top: 0;
}
a {
text-decoration: none;
color: #000;
}
</style>
<script>
//需求:当鼠标移动到任何一个li中时,绿色的筋斗云就随之移动到li中,(使用缓速动画)
// 移出时就回到原理指定的li中去,鼠标点击li就是指定绿色的筋斗云停在那个li那里
//思路:给每一个li绑定事件,根据li的实际宽度和当前li的索引来确定筋斗云移动的位置
//步骤:
//1.获取事件源及相关对象
//2.绑定事件
//3.书写事件驱动程序
window.onload = function () {
//1.获取事件源及相关对象
var box = document.getElementById("box");
var cloud = box.firstElementChild || box.firstChild;
var liArr = box.children[1].children;
var target=0;//定义一个鼠标点击后的指定距离
//2.绑定事件
for (var i = 0; i < liArr.length; i++) {
//给每一个li添加index属性
liArr[i].index=i;
liArr[i].οnmοuseοver=function(){
//随着鼠标的移动到li上时 同时移动筋斗云
animate(cloud,this.index*this.offsetWidth);
}
liArr[i].οnmοuseοut=function(){
//鼠标移出时 筋斗云移动到默认指定的位置
animate(cloud,target);
}
liArr[i].οnclick=function(){
//设置默认指定的位置
target=this.index*this.offsetWidth;
//鼠标点击那个li,那个li的位置就是默认指定的位置,筋斗云就默认待在哪个位置
animate(cloud,target);
}
}
//3.书写事件驱动程序
}
/**
* 缓速动画
* @param element
* @param target
*/
function animate(element, target) {
console.log(target);
//绑定计时器之前先清除定时器
clearInterval(element.timer);
element.timer = setInterval(function () {
//注意:this.offsetLeft最终获取的是number类型的值,并且它还会对小数进行四舍五入,
//如果this.style.left中有小数,并且还低于0.5,那么就会导致offsetLeft每次获取到的值,
//都是一样,然后就会造成 盒子不懂,定时器不停的现象发生,所以缓速动画时要记得给步长取整
// 如果步长>0就向上取整,避免步长小于0.5时出现bug,
// 如果步长<0,就向下取整,避免步长下雨-0.5时出现bug。
//每次移动的步长
element.spend = (target - element.offsetLeft) / 10
//对步长进行取整,避免offsetLeft底层的四舍五入的影响
element.spend = element.spend > 0 ? Math.ceil(element.spend) : Math.floor(element.spend);
//目标位置减去当前位置的绝对值 如果小于步长的绝对值 那么就说明也就一步之遥了,
// 那么直接移动到指定的位置清除计时器算了。
if (Math.abs(target - element.offsetLeft) <= Math.abs(element.spend)) {
element.style.left = target + "px";
console.log(element.style.left)
clearInterval(element.timer);
return;
}
//重新设置距离左边的位置
element.style.left = element.offsetLeft + element.spend + "px";
console.log(element.style.left)
}, 15);
}
</script>
</head>
<body>
<div class="box" id="box">
<i></i>
<ul>
<li><a href="javascript:;">首页新闻</a></li>
<li><a href="javascript:;">活动策划</a></li>
<li><a href="javascript:;">师资力量</a></li>
<li><a href="javascript:;">企业文化</a></li>
<li><a href="javascript:;">招聘信息</a></li>
<li><a href="javascript:;">公司简介</a></li>
<li><a href="javascript:;">北京上海</a></li>
<li><a href="javascript:;">广州深圳</a></li>
</ul>
</div>
</body>
</html>