js运动原理
1. 运动
运动原理:使用定时器达到状态的改变
运动速度:由每次改变的属性值决定
运动停止:达到某一特定值,清除定时器
注意: 实现运动时始终遵循两条原则
1.每次运动之前先清除上一次的定时器
2.运动和停止分开进行判断
2.匀速运动
每次运动的速度都是定值
<!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: 0px; padding: 0px;}
#div1{width: 100px; height: 100px; background-color: red; position: absolute; left: 0px; top: 200px;}
</style>
<script>
window.onload = function(){
var oBtn = document.getElementById("btn1");
var oDiv = document.getElementById("div1");
var timer = null;//定义一个全局变量用来存储定时器
oBtn.onclick = function(){
var speed = 20;
clearInterval(timer);//每次点击启动定时器后,都将前一次的定时器清除
timer = setInterval(function(){
if(oDiv.offsetLeft >= 500){//运动停止条件
clearInterval(timer);
}else{
oDiv.style.left = oDiv.offsetLeft + speed + 'px';
}
}, 300);
}
}
</script>
</head>
<body>
<button id = 'btn1'>开始运动</button>
<div id = 'div1'></div>
</body>
</html>
注意:点击过后红色小方块会向右运动500像素,加入每次不清楚上一次的定时器,连续点击开始运动会发生加速的情况,这是因为开启了多个定时器,清除上一次的定时器过后就能解决这个问题
3.缓冲运动
在我们日常生活中,刹车就是缓冲运动。
缓冲运动的速度和距离成正相关。
var speed =(iTarget - node.offsetLeft) / 8;
【注】经过长期的实验,8这个数字,呈现出来的缓冲效果最好。
<!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: 0px;
padding: 0px;
}
#div1 {
width: 100px;
height: 100px;
background-color: red;
position: absolute;
left: 0px;
top: 200px;
}
span {
width: 1px;
height: 500px;
background-color: black;
position: absolute;
left: 500px;
top: 0px;
display: block;
}
</style>
<script>
window.onload = function () {
var oBtn = document.getElementById("btn1");
oBtn.onclick = function () {
startMove(500);
}
}
var timer = null;
function startMove(iTarget) {
var oDiv = document.getElementById("div1");
var oTxt = document.getElementById("txt1");
clearInterval(timer);
timer = setInterval(function () {
//计算速度
var speed = (iTarget - oDiv.offsetLeft) / 8;
//判断速度的方向,同时取整防止速度取不到一个可以到达目的值的速度
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
if (oDiv.offsetLeft == iTarget) {
clearInterval(timer);
} else {
oDiv.style.left = oDiv.offsetLeft + speed + 'px';
}
}, 30);
}
</script>
</head>
<body>
<button id='btn1'>开始运动</button>
<div id='div1'></div>
<span></span>
</body>
</html>
注意:此处需要注意的是速度的取值,假如不对数据进行取整操作,物体可能将永远到达不了目的值,这不是我们想要的,取整的同时要注意速度的正负,正的速度应该是向上取整,负的速度是向下取整。
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>
#img1{opacity: 0.3; filter: alpha(opacity=30);}
body{background-color: black;}
</style>
<script>
window.onload = function(){
var oImg = document.getElementById("img1");
oImg.onmouseover = function(){
startMove(100);
}
oImg.onmouseout = function(){
startMove(30);
}
}
//计算透明度的时候,如果遇到这种有不兼容的问题
//直接通过中间变量完成计算
var alpha = 30;
var timer = null;
function startMove(iTarget){
var oImg = document.getElementById("img1");
var speed = 2;
//1、判断速度的正负
if(alpha < iTarget){
speed = Math.abs(speed);
}else{
speed = -Math.abs(speed);
}
//2、每次启动定时器之前,现将上一次定时器关闭掉
clearInterval(timer);
timer = setInterval(function(){
//3、运动和停止要分开
if(alpha == iTarget){
clearInterval(timer);
}else{
alpha += speed;
//设置透明度
oImg.style.opacity = alpha / 100;
oImg.style.filter = `alpha(opacity=${alpha})`;
}
}, 30);
}
</script>
</head>
<body>
<img id = 'img1' src="timg.jpeg" alt="">
</body>
</html>
注意:此处需要注意的就是透明度的兼容,通知alpha变量达到兼容的效果。
5.多物体的缓冲运动
让每一个物体都拥有缓冲运动的效果
不能公用一个定时器
<!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>
div{width: 100px; height: 50px; background-color: red; margin: 50px;}
</style>
<script>
/*
移入 宽 100 => 300
移出 宽 300 => 100
*/
window.onload = function(){
var aDivs = document.getElementsByTagName("div");
for(var i = 0; i < aDivs.length; i++){
aDivs[i].onmouseover = function(){
//100 => 300
startMove(this, 300);
}
aDivs[i].onmouseout = function(){
//300 => 100
startMove(this, 100);
}
}
}
/*
让每一个运动的物体,都拥有一个自己独享的定时器。
node.index = i;
node.timer = 定时器
*/
// var timer = null;
function startMove(node, iTarget){
clearInterval(node.timer);
node.timer = setInterval(function(){
//计算速度
var speed = (iTarget - node.offsetWidth) / 8;
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
if(node.offsetWidth == iTarget){
clearInterval(node.timer);
}else{
node.style.width = node.offsetWidth + speed + 'px';
}
}, 30);
}
</script>
</head>
<body>
<div></div>
<div></div>
<div></div>
<div></div>
</body>
</html>
注意:假如都共用一个定时器的话,鼠标划入第一个方块,随即立刻划入第二个的话,第一个的缓冲效果会被停止,因为每次划入的时候都清除了上一个定时器,为了解决这个问题,就引入参数node,让每一个物体都拥有自己的定时器。
6.多物体的淡入淡出
和实现多物体的缓冲运动一样,只不过是改变透明度
<!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>
div{width: 100px; height: 100px; background-color: red;
margin: 50px; opacity: 0.3; filter: alpha(opacity=30);}
</style>
<script>
/*
移入 30 => 100
移出 100 => 30
*/
window.onload = function(){
var aDivs = document.getElementsByTagName("div");
for(var i = 0; i < aDivs.length; i++){
aDivs[i].alpha = 30;
aDivs[i].onmouseover = function(){
startMove(this, 100);
}
aDivs[i].onmouseout = function(){
startMove(this, 30);
}
}
}
/*
多物体运动里,所有的变量都不能共用。
多物体运动里,所有的属性和变量都要独立使用。
*/
// var alpha = 30; //中间变量透明度的变化
function startMove(node, iTarget){
clearInterval(node.timer);
node.timer = setInterval(function(){
var speed = (iTarget - node.alpha) / 8;
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
if(node.alpha == iTarget){
clearInterval(node.timer);
}else{
node.alpha += speed;
node.style.opacity = node.alpha / 100;
node.style.filter = `alpha(opacity=${node.alpha})`;
}
}, 30);
}
</script>
</head>
<body>
<div></div>
<div></div>
<div></div>
<div></div>
</body>
</html>
7.多物体多样式的缓冲效果
<!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>
div{width: 100px; height: 50px; background-color: red; font-size: 20px; margin: 50px;}
div.box{opacity: 0.3; filter: alpha(opacity=30);}
</style>
<script>
window.onload = function(){
var aDivs = document.getElementsByTagName("div");
//点击第一个div,让宽100 => 300
aDivs[0].onclick = function(){
startMove(this, "width", 300);
}
//高变成100 => 300
aDivs[1].onclick = function(){
startMove(this, "height", 300);
}
//marginLeft 100 => 300
aDivs[2].onclick = function(){
startMove(this, "marginLeft", 300);
}
//fontSize 20 => 100
aDivs[3].onclick = function(){
startMove(this, "fontSize", 100);
}
//透明度的变化
aDivs[4].onmouseover = function(){
startMove(this, "opacity", 100);
}
aDivs[4].onmouseout = function(){
startMove(this, "opacity", 30);
}
}
//多物体多样式的运动 startMove(this, "width", 300);
function startMove(node, attr, iTarget){
clearInterval(node.timer);
node.timer = setInterval(function(){
//1、获取当前值
var iCur = null;
if(attr == "opacity"){
iCur = parseInt(parseFloat(getStyle(node, "opacity")) * 100);
}else{
iCur = parseInt(getStyle(node, attr))
}
//2、计算速度
var speed = (iTarget - iCur) / 8;
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
//3、运动和停止分开
if(iCur == iTarget){
clearInterval(node.timer);
}else{
if(attr == "opacity"){
iCur += speed;
node.style.opacity = iCur / 100;
node.style.filter = `alpha(opacity=${iCur})`;
}else{
node.style[attr] = iCur + speed + 'px';
}
}
}, 30);
}
/*
node 元素节点
cssStyle 获取css样式类型
*/
function getStyle(node, cssStyle){
if(node.currentStyle){
return node.currentStyle[cssStyle];
}else{
return getComputedStyle(node)[cssStyle];
}
}
</script>
</head>
<body>
<div></div>
<div></div>
<div></div>
<div>div文本</div>
<div class = 'box'></div>
</body>
</html>
8.链式运动
上一次动画结束之后在继续执行下一个动画。
<!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>
#div1{width: 100px; height: 100px; background-color: red;}
</style>
<script>
window.onload = function(){
var oDiv = document.getElementById("div1");
/*
【注】每一个动画都开始在,上一个动画结束的时候。
*/
//链式运动 宽100=>300 然后高100=>300 透明度100=>30
oDiv.onmouseover = function(){
startMove(this, "width", 300, function(){
startMove(this, "height", 300, function(){
startMove(this, "opacity", 30);
})
});
}
//链式运动 透明度30=>100 然后高300=>100 宽300=100
oDiv.onmouseout = function(){
startMove(this, "opacity", 100, function(){
startMove(this, "height", 100, function(){
startMove(this, "width", 100);
})
});
}
}
</script>
<script>
//多物体多样式的运动 startMove(this, "width", 300);
function startMove(node, attr, iTarget, complete){ //complete = show;
clearInterval(node.timer);
node.timer = setInterval(function(){
//1、获取当前值
var iCur = null;
if(attr == "opacity"){
iCur = parseInt(parseFloat(getStyle(node, "opacity")) * 100);
}else{
iCur = parseInt(getStyle(node, attr))
}
//2、计算速度
var speed = (iTarget - iCur) / 8;
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
//3、运动和停止分开
if(iCur == iTarget){
clearInterval(node.timer);
//这里是上一个动画结束的时候,但是动画结束以后,我们要干什么是不确定的
//一般情况下我们把函数中的不确定值声明成形参,一大段代码不确定,也可以声明成形参
//回调函数:这种直接将函数当做参数的写法,叫做回调函数。
if(complete){
complete.call(node); //执行函数
}
}else{
if(attr == "opacity"){
iCur += speed;
node.style.opacity = iCur / 100;
node.style.filter = `alpha(opacity=${iCur})`;
}else{
node.style[attr] = iCur + speed + 'px';
}
}
}, 30);
}
/*
node 元素节点
cssStyle 获取css样式类型
*/
function getStyle(node, cssStyle){
if(node.currentStyle){
return node.currentStyle[cssStyle];
}else{
return getComputedStyle(node)[cssStyle];
}
}
</script>
</head>
<body>
<div id = 'div1'></div>
</body>
</html>
注意:执行下一个动画的函数应该写在停止上一个动画之后,也就是清除定时器之后。此处第四个参数是函数作为参数调用,属于回调函数。
9.完美运动
一个物体实现多种样式的动画,参数通过对象传入
<!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>
#div1{width: 100px; height: 100px; background-color: black; color: white;}
</style>
<script>
/*
需求:想要实现多个css的变化
【注】问题:当其中有一个动画到达目的值,整体就必须关闭掉定时器。
保证:定时器,一定是,等所有的动画都到达目的值以后才能关闭定时器。
*/
window.onload = function(){
var oDiv = document.getElementById("div1");
oDiv.onmouseover = function(){
startMove(this, {
width: 300,
height: 102,
opacity: 30
}, function(){
this.innerHTML = "移入动画结束"
});
}
oDiv.onmouseout = function(){
startMove(this, {
width: 100,
height: 100,
opacity: 100
}, function(){
this.innerHTML = "移出动画结束"
});
}
}
</script>
<script>
//多物体多样式的运动 startMove(this, "width", 300);
function startMove(node, cssObj, complete){ //complete = show;
clearInterval(node.timer);
node.timer = setInterval(function(){
var isEnd = true; //假设所有动画都都到达目的值
for(var attr in cssObj){
//取出当前css样式的目的值
var iTarget = cssObj[attr];
//1、获取当前值
var iCur = null;
if(attr == "opacity"){
iCur = parseInt(parseFloat(getStyle(node, "opacity")) * 100);
}else{
iCur = parseInt(getStyle(node, attr))
}
//2、计算速度
var speed = (iTarget - iCur) / 8;
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
if(attr == "opacity"){
iCur += speed;
node.style.opacity = iCur / 100;
node.style.filter = `alpha(opacity=${iCur})`;
}else{
node.style[attr] = iCur + speed + 'px';
}
//当前值是否瞪目目的值
if(iCur != iTarget){
isEnd = false;
}
}
if(isEnd){
//说明都到达目的值
clearInterval(node.timer);
if(complete){
complete.call(node);
}
}
}, 30);
}
/*
node 元素节点
cssStyle 获取css样式类型
*/
function getStyle(node, cssStyle){
if(node.currentStyle){
return node.currentStyle[cssStyle];
}else{
return getComputedStyle(node)[cssStyle];
}
}
</script>
</head>
<body>
<div id = 'div1'>11</div>
</body>
</html>
注意:当某一属性到达目的值之后会清除定时器,这样可能别的属性并没有到达目的值,为了解决这个问题,我们必须增加一个判断,是否所有的动画都到达了目的值。