一、学习运动框架作用
- web页面开发的过程中,如何与用户进行友好,有趣的交互,是我们必须考虑的问题,比如:
导航条中滑动的动画特效
点击加入购物车按钮通过抛物线
加入右侧购物车的动画特效
网页游戏的开发:微信打飞机,打砖块等
二、运动原理
- Js运动:
- 就是让 web 上 DOM 元素动起来,改变其自身的位置属性,比如高宽,左边距,上边距,透明度等。
- 动画的原理就是把不同状态的物体,串成连续的样子。
- js动画也一样,不同状态的DOM,用定时器控制,就能得到动画效果。
人眼能够识别的最小的时间间隔是18帧。
【注】电影院电影24帧。
- 如何实现运动:
- 1.运动的物体使用绝对定位
- 2.通过改变定位物体的属性(left、right、top、bottom)值来使物体移动。例如
- 向右或左移动可以使用offsetLeft(offsetRight)来控制左右移动。
- 步骤:
- 开始运动前,先清除已有定时器 (因为:是连续点击按钮,物体会运动越来越快,造成运动混乱)
- 开启定时器,计算速度
- 把运动和停止隔开(if/else),判断停止条件,执行运动
三、定时器
- 在javascritp中,有两个关于定时器的专用函数,它们是倒计定时器、循环定时器。
3.1. 倒计定时器:timer=setTimeout(函数名,delaytime);
- 倒计时定时器就是在指定时间后触发事件,只是作用一次。
- 一般用于页面上只需要触发一次的的情况,比如点击某按钮后页面在一定时间后跳转到相应的站点,也可以用于判断一个浏览者是不是你的站点上的“老客”,如果不是,你就可以在5秒或者10秒后跳转到相应的站点,然后告诉他以后再来可以在某个地方按某一个按钮就可以快速进入。
3.2. 循环定时器:timer=setInterval(函数名,delaytime);
-
循环定时器就是在间隔时间到来时反复触发事件,不停地作用。
-
一般用于站点上需要从复执行的效果,比如一个javascript的滚动条或者状态栏,也可以用于将页面的背景用飞雪的图片来表示。这些事件需要隔一段时间运行一次。
-
function()是定时器触发时要执行的是事件的函数,可以是一个函数,也可以是几个函数,或者javascript的语句也可以,单要用;隔开;
-
delaytime则是间隔的时间,以毫秒为单位。
3.3 删除定时器 clearTimeout(timename) clearInterval(timename)
- clearTimeout(timename)关闭倒计时定时器
- clearInterval(timename)来关闭循环定时器
四、运动研究
4.1. 运动:匀速运动(让物体动起来)
- 对定时器的使用
- 给DIV加绝对定位
- offsetLeft
window.onload = function(){
var oMenu = document.getElementById("menu");
oMenu.onmouseenter = function(){
//-100 => 0
startMove(0);
}
oMenu.onmouseleave = function(){
//0 => -100
startMove(-100);
}
var timer = null;
function startMove(iTarget){
var oMenu = document.getElementById("menu");
var speed = 10;
//1、每次启动定时器将上一次定时器关闭
clearInterval(timer);
timer = setInterval(function(){
if(oMenu.offsetLeft < iTarget){
speed = Math.abs(speed);
}else{
speed = -Math.abs(speed);
}
//2、运动和停止分开
if(oMenu.offsetLeft == iTarget){
clearInterval(timer);
}else{
oMenu.style.left = oMenu.offsetLeft + speed + 'px';
}
}, 30);
}
}
淡入淡出:
window.onload = function(){
var oImg = document.getElementById("img1");
oImg.onmouseenter = function(){
//30 => 100
startMove(100);
}
oImg.onmouseleave = function(){
//100 => 30
startMove(30);
}
/*
类似于透明度这类型的css属性,使用中间变量替代计算。
*/
var alpha = 30;
var timer = null;
function startMove(iTarget){
var oImg = document.getElementById("img1");
var speed = 2;
clearInterval(timer);
timer = setInterval(function(){
if(alpha > iTarget){
speed = -Math.abs(speed);
}else{
speed = Math.abs(speed);
}
//1、取当前值
if(alpha == iTarget){
clearInterval(timer);
}else{
alpha += speed;
//給当前图片上设置透明度,考虑浏览器兼容
oImg.style.opacity = alpha / 100;
oImg.style.filter = "alpha(opacity=" + alpha + ")";
document.title = alpha;
}
}, 30);
}
}
1.问题:到达某个特定位罝停止
解决:做判断,符合条件时关掉定时器(存定时器timer)
速度变慢(一般不动时间,而是改数字-速度)
用变量存速度
2.问题:取7时,offsetLeft没有等于300的时候,div停不下来
解决:>=300 //停在 301
3.问题:到300后点击按钮还继续走
原因:点击按钮,执行函数,开定时器(执行当前函数一至少执行一次)
解决:加else (没有到达目标之前才执行)
4.问题:连续点击,速度变快
原因:每点击一次就开一个定时器,点击几次就有几个定时器同时工作
解决:保证每次只有一个定时器工作,先cearlnterval ()
4.2、变速运动
逐渐变慢,最后停止
距离越远速度越大
速度由距离决定
速度=(目标值-当前值)/缩放系数
window.onload = function(){
var oBtn = document.getElementById("btn1");
var oDiv = document.getElementById("div1");
oBtn.onclick = function(){
startMove(500);
}
var timer = null;
function startMove(iTarget){
var oDiv = document.getElementById("div1");
var oTxt1 = document.getElementById("txt1");
clearInterval(timer);
timer = setInterval(function(){
//计算速度
var speed = (iTarget - oDiv.offsetLeft) / 8;
speed = Math.ceil(speed);
if(oDiv.offsetLeft == iTarget){
clearInterval(timer);
}else{
oDiv.style.left = oDiv.offsetLeft + speed + 'px';
txt1.value += oDiv.offsetLeft + ", " + speed + "\n";
}
}, 30);
}
}
- 缓存菜单
window.onload = function(){
var oMenu = document.getElementById("menu");
//获取当前的目的值
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
var windowHeight = document.documentElement.clientHeight || document.body.clientHeight;
var iH = parseInt(scrollTop + (windowHeight - oMenu.offsetHeight) / 2);
startMove(iH);
window.onscroll = function(){
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
var windowHeight = document.documentElement.clientHeight || document.body.clientHeight;
var iH = parseInt(scrollTop + (windowHeight - oMenu.offsetHeight) / 2);
startMove(iH);
}
}
var timer = null;
function startMove(iTarget){
var oMenu = document.getElementById("menu");
clearInterval(timer);
timer = setInterval(function(){
//计算速度
var speed = (iTarget - oMenu.offsetTop) / 8;
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
//运动和停止分开
if(oMenu.offsetTop == iTarget){
clearInterval(timer);
}else{
oMenu.style.top = oMenu.offsetTop + speed + 'px';
document.title = oMenu.offsetTop + ", " + iTarget;
}
}, 30);
}
1.问题:并没有真正到达300 原因:速度只剩0.9 像素是屏幕能够显示的最小单位,并不会四舍五入掉 Math.ceil() 向上取整 Math.floor() 向下取整 2.问题:向左走,又差一块–Math.floor () 判断:三目 speed=speed>0 ? Math.ceil ( speed ): Math.floor ( speed )
4.3、多物体运动
多个div ,鼠标移入变宽
运动框架传参obj,知道让哪个物体动起来
用到缓冲一定要取整
多物体运动:
window.onload = function(){
var aDivs = document.getElementsByTagName("div");
for(var i = 0; i < aDivs.length; i++){
aDivs[i].onmouseover = function(){
startMove(this, 300);
}
aDivs[i].onmouseout = function(){
startMove(this, 100);
}
}
}
/*
原因是:我们整个页面上只有一个定时器。
解决:让每一个运动的物体,独立拥有自己的定时器。
*/
// 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);
}
多物体-淡入淡出:
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);
}
1.问题:div没运动回去 //清除前一个定时器
原因:只有一个定时器
解决:加物体上的定时器,使每个物体都有一个定时器。定时器作为物体属性
多个div淡入淡出
首先关闭物体上的定时器
经验:多物体运动框架所有东西都不能共用
2.问题:不是因为定时器,而是因为alpha
解决:作为属性附加到物体上 /不以变量形式存在
3.问题:offset 的 bug 加border变宽
原因:offsetWith并不是真正的width ,它获取的是盒模型尺寸
解决:躲着 宽度扔到行间,parselnt ( oDiv.style.width )
进一步解决: getStyle ( obj, name ) currentStyle , getComputedStyle
加border ,只要offset就有问题 去掉offset
示例,多物体运动框架:
4.4.任意值运动
- 【offset问题】
window.onload = function(){
var oDiv = document.getElementById("div1");
setInterval(function(){
// alert(oDiv.offsetWidth); //width + border
oDiv.style.width = oDiv.offsetWidth - 1 + 'px';
}, 30);
}
-
用getStyle方法来获取当前有效眼样式
node.currentStyle ? node.currentStyle[cssStr] : getComputedStyle(node)[cssStr];
window.onload = function(){
var oDiv = document.getElementById("div1");
setInterval(function(){
//获取当前值
var iCur = parseInt(getStyle(oDiv, "width"));
alert(iCur);
oDiv.style.width = iCur - 1 + 'px';
}, 30);
}
//获取当前有效样式浏览器兼容的写法
function getStyle(node, cssStr){
return node.currentStyle ? node.currentStyle[cssStr] : getComputedStyle(node)[cssStr];
}
- 任意值运动的单位分为透明度和px。
- px单位的任意值
- 【多物体多样式】
window.onload = function(){
var aDivs = document.getElementsByTagName("div");
aDivs[0].onclick = function(){
//宽变成300
startMove(this, "width", 300);
}
aDivs[1].onclick = function(){
//高变成300
startMove(this, "height", 300);
}
aDivs[2].onclick = function(){
//marginLeft => 300
startMove(this, "marginLeft", 300);
}
aDivs[3].onclick = function(){
//fontSize => 100
startMove(this, "fontSize", 100);
}
aDivs[4].onmouseover = function(){
startMove(this, "opacity", 100);
}
aDivs[4].onmouseout = function(){
startMove(this, "opacity", 30);
}
}
//startMove(oDiv, "width", 300);
function startMove(node, attr, iTarget){
clearInterval(node.timer);
node.timer = setInterval(function(){
//计算速度
var iCur = null;
if(attr == "opacity"){
iCur = parseInt(parseFloat(getStyle(node, "opacity")) * 100);
}else{
iCur = parseInt(getStyle(node, attr))
}
var speed = (iTarget - iCur) / 8;
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
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);
}
4.5.链式运动
- 链式运动:
- 在第一个动画结束的时候,开始第二个动画。
- 【注】关键点,找到第一个动画结束的时候。
window.onload = function(){
var aDivs = document.getElementsByTagName("div");
aDivs[0].onmouseover = function(){
//嵌套function 实现链式运动
startMove(this, "width", 300, function(){
startMove(this, "height", 300, function(){
startMove(this, "opacity", 100);
})
});
}
aDivs[0].onmouseout = function(){
startMove(this, "opacity", 30, function(){
startMove(this, "height", 100, function(){
startMove(this, "width", 100);
})
});
}
}
回调函数:
我们把函数当做参数传入,并且在合适调用的方式,叫做回调函数。在别的编程语言(C语言、C++)叫做函数指针。
function startMove(node, attr, iTarget, complete){//complete = show;
clearInterval(node.timer);
node.timer = setInterval(function(){
//计算速度
var iCur = null;
if(attr == "opacity"){
iCur = parseInt(parseFloat(getStyle(node, "opacity")) * 100);
}else{
iCur = parseInt(getStyle(node, attr))
}
var speed = (iTarget - iCur) / 8;
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
if(iCur == iTarget){
clearInterval(node.timer);
if(complete){
complete.call(node);
}
// alert("运动结束了");
/*
当运动结束以后,应该做什么的代码在这里不能写死。
【注】封装函数,形参,根据函数不确定的值决定的。
可以,将一段代码编写的权利交给别人。声明一个形参,这个形参是用来接收,从外面封装好的一个函数的。
*/
}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);
}
多出来的一个参数,只有传进去的时候才调用
鼠标移入变宽,结束之后弹出abc
先横向展开.再以向展开
鼠标移出,先变回不透明,变矮,变窄
五、封装运动框架
1. 认识运动(运动框架)
<1>每次启动定时器,将上一次定时器关闭
<2>运动和停止 if…else
2. 分享到菜单和淡入淡出
startMove(iTarget);
3. 缓冲运动
var speed = (iTarget - iCur) / 8;
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
4. 多物体运动
node.timer;
多物体淡入淡出:任何一个变量都不能公用
startMove(node, iTarget);
5. 多物体多样式运动
offset系列 透明度
startMove(node, attr, iTarget);
6. 链式运动
startMove(node, attr, iTarget, complete)
7. 完美运动
startMove(node, cssObject, complete);
六、运动框架的应用
1. banner图
- 编写图片div
<body>
<div id = 'div1'>
<ul id = 'ul1'>
<li>
<img src="img/1.jpg" alt=""/>
</li>
<li>
<img src="img/2.jpg" alt=""/>
</li>
<li>
<img src="img/3.jpg" alt=""/>
</li>
<li>
<img src="img/4.jpg" alt=""/>
</li>
</ul>
</div>
</body>
- 设计div样式
<style>
*{margin: 0px; padding: 0px}
#ul1 li{list-style: none; width: 200px; height: 200px; margin: 10px; float: left;}
#ul1 li img{width: 100%; height: 100%}
#ul1 {position: absolute; left: 0px}
#div1{width: 880px; height: 220px; border: 1px solid black; margin: 150px auto; position: relative; overflow: hidden;}
</style>
- js代码:
- 调用startMove()函数
- 【tips】将四张图片添加到末尾;若滚动四张结束,则设置left = 0px ; 回到第一张。
<script src = "../startMove.js"></script>
<script>
window.onload = function(){
var oUl1 = document.getElementById("ul1");
var oDiv1 = document.getElementById("div1");
/*
直接把这四张图片再添加到末尾
*/
oUl1.innerHTML += oUl1.innerHTML;
//重新设置一下ul的宽
oUl1.style.width = 220 * 8 + 'px';
setInterval(function(){
//让ul向左运动一个图片的宽
startMove(oUl1, {left: oUl1.offsetLeft - 220}, function(){
if(oUl1.offsetLeft <= -oUl1.offsetWidth / 2){
oUl1.style.left = "0px";
}
})
}, 2000);
}
</script>