运动的效果一般上都是在一定的时间内对其样式进行更改,从而在视觉上达到运动的感觉。这时候就要利用setInterval()在间隔时间内对其样式进行相应的改变。
想要一个物体运动一般需要原物体的位置,速度,运动到达的位置,运动的形式等。这些都会作为方法的参数进行传递。
前期准备
创建一个方法来进行获取该对象的属性值。需要的参数有两个:对象,属性名(运动的形式)。代码如下:
function getStyle(obj, attr) {
if (obj.currentStyle) {a
return obj.currentStyle[attr];
} else {
return getComputedStyle(obj, false)[attr];
}
}
currentStyle是为了兼容IE6浏览器,兼容性情况如下
getComputedStyle兼容现在的大多数浏览器
方法的具体使用可以查看文档
1.单方向上的运动
html页面里面有三个li标签,用来演示效果。原始宽度为200px,高度为100px。
window.onload = function(){
var lis = document.getElementsByTagName("li");
for(var i = 0;i<lis.length;i++){
lis[i].onmouseover = function(){
startMove(this,10,400);
}
lis[i].onmouseout = function(){
startMove(this,-10,200);
}
}
}
var timer = null;
function startMove(obj,speed,target){
//一开始就进行清楚,是为了消除onmouseon和onouseover事件之间的影响
//例如,当鼠标移上去后再移出来后就会发现li的宽度一直在10px之间不断的进行增加和减少的循环操作
//这是因为onmouseover和onmouseout的setInterval还存在,固有下面一行来清楚之间的影响
clearInterval(timer);
timer = setInterval(function(){
//如果已经达到了目标值,就停止
if(obj.offsetWidth == target){
clearInterval(timer);
}else{
obj.style.width = obj.offsetWidth + speed + "px";
}
},30)
}
这里先用宽度来进行展示。首先要弄懂style.width和offsetWidth的区别。
- offsetWidth属性可以返回对象的padding+border+width属性值之和,style.width返回值就是定义的width属性值。
- offsetWidth属性仅是可读属性,而style.width是可读写的。
- offsetWidth属性返回值是整数,而style.width的返回值是字符串。
- style.width仅能返回以style方式定义的内部样式表的width属性值。
startMove方法传入的是三个参数
- obj,对象参数
- speed,动画的变化速度
- target,变化所要达到的值
但是这里会有一个问题,就是timer是作为全局变量,每个li对象都是公用这么一个timer。
每个li运动之间的效果都会相互之间影响。问题如图所示:
解决这个问题很简单,就是把全局变量改成诶个li对象自己的变量。也就是为对象添加一个timer属性。
改进后的代码如下:
window.onload = function(){
var lis = document.getElementsByTagName("li");
for(var i = 0;i<lis.length;i++){
lis[i].timer = null; //为每个li对象添加timer属性
lis[i].onmouseover = function(){
startMove(this,10,400);
}
lis[i].onmouseout = function(){
startMove(this,-10,200);
}
}
}
//下面的timer也要相应的改成obj.timer
function startMove(obj,speed,target){
clearInterval(obj.timer);
obj.timer = setInterval(function(){
if(obj.offsetWidth == target){
clearInterval(obj.timer);
}else{
obj.style.width = obj.offsetWidth + speed + "px";
}
},30)
}
2.根据传递的属性来改变运动的形式
上面的代码只能进行单方向的运动,如果想要进行高度上的变化,就要把代码中的width相应的改成height。
这样就显得过于麻烦,可以改造一下startMove函数,可以根据传递进去的属性来改变运动的形式。这时候就要用到前期准备中的getStyle(obj,attr)方法了.
window.onload = function(){
var lis = document.getElementsByTagName("li");
for(var i = 0;i<lis.length;i++){
lis[i].timer = null;
lis[i].onmouseover = function(){
startMove(this,10,200,"height");
}
lis[i].onmouseout = function(){
startMove(this,-10,100,"height");
}
}
}
function startMove(obj,speed,target,attr){
clearInterval(obj.timer);
obj.timer = setInterval(function(){
//用getStyle方法来获取对应属性的属性值
var iattr = parseInt(getStyle(obj,attr));
if(iattr == target){
clearInterval(obj.timer);
}else{
//这里不能够使用style.+"attr"的形式
obj.style[attr] = iattr + speed + "px";
}
},30)
}
但是这里只有够对width或者height这样简单的变化,如果是opacity透明度的话就要进行相应的判断处理。(这里的opacity使用的是0~1)
function startMove(obj,speed,target,attr){
clearInterval(obj.timer);
obj.timer = setInterval(function(){
if(iattr == target){
clearInterval(obj.timer);
}else{
//进行属性的判断
if(attr == "opacity"){
obj.style.opacity = target;
}else{
obj.style[attr] = iattr + speed + "px";
}
}
},30)
}
3.缓冲运动
上面的运动的速度都是固定,并且要向startMove方法里面传递速度值。固定的速度看起来可能太僵硬,加上缓冲感觉更好些。缓冲运动的实现就是运动的加速度不断地额变小,这里就是将速度的值不断的变小,并且不传递进来速度值,而是根据目标值和原有值之间的差来决定。
function startMove(obj, target, attr) {
clearInterval(obj.timer);
obj.timer = setInterval(function() {
var iattr = null;
var speed = null;
if (attr == "opacity") {
//将小数的情况转成整数,方便后面进行速度的变化
iattr = Math.round(parseFloat(getStyle(obj, attr)) * 100);
speed = (target * 100 - iattr) / 8;
} else {
iattr = parseInt(getStyle(obj, attr));
speed = (target - iattr) / 8;
}
//如果出现小数的情况,浏览器会自动社区小数点,故而需要向上或者向下取整
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
if (iattr == target) {
clearInterval(obj.timer);
} else {
if (attr == "opacity") {
obj.style.opacity = (iattr + speed) / 100;
} else {
obj.style[attr] = iattr + speed + "px";
}
}
}, 30)
}
4.链式运动
上面的物体只能够执行一次运动,并没有第二次的运动。如何让其执行第一次执行变化后紧接着执行第二次的变化呢?这里让li的宽先变成400,紧接着高变为200。一开始想到的就是在鼠标事件里面再让其执行一个startMove()方法,情况如下:
lis[i].onmouseover = function() {
startMove(this, 400, "width");
startMove(this,200,"height");
}
运行的时候会发现,只执行了第二个startMove方法。这是因为当第二个startMove方法执行的时候就会立刻把第一个的timer给清除了,所以只剩下第二个方法在执行。
解决的方法就是向startMove方法里面传递一个函数。
window.onload = function() {
var lis = document.getElementsByTagName("li");
for (var i = 0; i < lis.length; i++) {
lis[i].timer = null;
lis[i].onmouseover = function() {
//第二个匿名function传递对象时不能够传递this,要将当前的this保存为一个变量再进行传递
var li = this;
startMove(this, 400, "width", function() {
startMove(li, 200, "height");
});
}
lis[i].onmouseout = function() {
var li = this;
startMove(this, 100, "height", function() {
startMove(li, 200, "width");
});
}
}
}
function startMove(obj, target, attr, fn) {
clearInterval(obj.timer);
obj.timer = setInterval(function() {
var iattr = null;
var speed = null;
if (attr == "opacity") {
iattr = Math.round(parseFloat(getStyle(obj, attr)) * 100);
speed = (target * 100 - iattr) / 8;
} else {
iattr = parseInt(getStyle(obj, attr));
speed = (target - iattr) / 8;
}
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
if (iattr == target) {
clearInterval(obj.timer);
//当上一个运动完成时执行下一个运动
if (fn) {
fn();
}
} else {
if (attr == "opacity") {
obj.style.opacity = (iattr + speed) / 100;
} else {
obj.style[attr] = iattr + speed + "px";
}
}
}, 30)
}
5.同时运动
同时运动需要一次传递多个属性和目标,要达到这个目的可以使用json。就相当于使用键值对的形式,如{width:400,height:200}。这样又可以吧startMove的的参数减少一个。
window.onload = function() {
var lis = document.getElementsByTagName("li");
for (var i = 0; i < lis.length; i++) {
lis[i].timer = null;
lis[i].onmouseover = function() {
//第二个匿名function传递对象时不能够传递this,要将当前的this保存为一个变量再进行传递
var li = this;
startMove(this, {
width: 400,
height: 200,
opacity: 0.3
});
}
lis[i].onmouseout = function() {
startMove(this, {
width: 200,
height: 100,
opacity: 1
})
}
}
}
//只需要对json进行遍历即可,并且target相应的换成json[attr]
function startMove(obj, json, fn) {
clearInterval(obj.timer);
obj.timer = setInterval(function() {
var iattr = null;
var speed = null;
for (var attr in json) {
if (attr == "opacity") {
iattr = Math.round(parseFloat(getStyle(obj, attr)) * 100);
speed = (json[attr] * 100 - iattr) / 8;
} else {
iattr = parseInt(getStyle(obj, attr));
speed = (json[attr] - iattr) / 8;
}
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
if (iattr == json[attr]) {
clearInterval(obj.timer);
if (fn) {
fn();
}
} else {
if (attr == "opacity") {
obj.style.opacity = (iattr + speed) / 100;
} else {
obj.style[attr] = iattr + speed + "px";
}
}
}
}, 30)
}
这里还有一个小问题,就是当一个属性的变化较小时,其他运动也到不到设置的目标值。解决这个问题只需要加入一个标志,用来判断运动是否达到当前目标值。
function startMove(obj, json, fn) {
clearInterval(obj.timer);
//运动是否完成的标志
var flag = true;
var iattr = null;
var speed = null;
obj.timer = setInterval(function() {
for (var attr in json) {
if (attr == "opacity") {
iattr = Math.round(parseFloat(getStyle(obj, attr)) * 100);
speed = (json[attr] * 100 - iattr) / 8;
} else {
iattr = parseInt(getStyle(obj, attr));
speed = (json[attr] - iattr) / 8;
}
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
//运动为完成,flag为false
if (iattr != json[attr]) {
flag = false;
}
if (attr == "opacity") {
obj.style.opacity = (iattr + speed) / 100;
} else {
obj.style[attr] = iattr + speed + "px";
}
//运动完成
if (flag) {
clearInterval(obj.timer);
if (fn) {
fn();
}
}
}
}, 30)
}
这个简易框架基本上已经完成了。这篇文章总结自慕课网JS动画效果