探讨JS动画中获取样式的缘故,是因为在添加边框之后的元素缩小其宽度时,会出现不减反增的bug。如下:
<style>
#div1{width: 200px;height: 200px;background: red;border: 1px solid black;}
</style>
<div id="div1"></div>
<script>
startMove();
function startMove(){
timer=setInterval(function(){
var oDiv=document.getElementById("div1");
oDiv.style.width=oDiv.offsetWidth-1+"px";
},30)
}
</script>
如上代码显示,我们的需求是让原本宽度为200像素的div元素以30毫秒的速度减少1个像素的宽度,那这目标效果应该是div元素宽度不断缩小。其实不仅是增添了border会出现这样的问题,在增加其他的譬如padding值都会出现offset这个属性导致的问题。
解决办法:在行间样式中设置宽度,并将当前的宽度样式的字符串解析,返回一个宽度值。这就可以解决问题使div元素宽度不断缩小。但是这是为什么?实现的原理是什么?还不清楚,猜测可能是因为行间样式的权重更大。
<style>
#div1{height: 200px;background: red;border: 1px solid black;}
</style>
<div id="div1" style="width:200px"></div>
<script>
startMove();
function startMove(){
timer=setInterval(function(){
var oDiv=document.getElementById("div1");
oDiv.style.width=parseInt(oDiv.style.width)-1+"px";
},30)
}
</script>
显然,解决办法在行间样式中设置不太方便也不太优美。我们可以来将其封装成一个获取样式的函数。点题了吧。
<style>
#div1{height: 200px;background: red;border: 1px solid black;}
</style>
<div id="div1" style="width:200px"></div>
<script>
startMove();
function startMove(){
timer=setInterval(function(){
var oDiv=document.getElementById("div1");
oDiv.style.width=parseInt(getStyle(oDiv,"width"))-1+"px";
},30)
}
function getStyle(obj,attr){
if(obj.currentStyle){
return obj.currentStyle[attr];
}else{
return getComputedStyle(obj,false)[attr];
}
}
</script>
- currentStyle :针对IE浏览器
- getComputedStyle :针对firefox浏览器
这个getStyle函数不仅仅可以修改宽度,其他的样式修改都可以实现的。譬如,增大字体大小。
<style>
#div1{height: 200px;background: red;border: 1px solid black;font-size: 12px;}
</style>
<div id="div1" style="width:200px"></div>
<script>
startMove();
function startMove(){
timer=setInterval(function(){
var oDiv=document.getElementById("div1");
oDiv.style.fontSize=parseInt(getStyle(oDiv,"fontSize"))+1+"px";
},30)
}
function getStyle(obj,attr){
if(obj.currentStyle){
return obj.currentStyle[attr];
}else{
return getComputedStyle(obj,false)[attr];
}
}
</script>
在实现多物体缓冲运动时,增添了border属性,发现鼠标移开元素恢复的宽度比原本的要大。
此时就用到了getStyle函数,并将原本使用的offset类的属性替换成parseInt(getStyle(obj,attr))。
<style>
ul,li{list-style: none;}
ul li{width: 200px;height: 100px;background: red;margin-bottom: 20px;border: 2px solid black;}
</style>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
<script>
var aLi=document.getElementsByTagName("li");
for(var i=0; i<aLi.length; i++){
aLi[i].timer=null;
aLi[i].onmouseover=function(){
startMove(this,400);
}
aLi[i].onmouseout=function(){
startMove(this,200);
}
}
function getStyle(obj,attr){
if(obj.currentStyle){
return obj.currentStyle[attr];
}else{
return getComputedStyle(obj,false)[attr];
}
}
function startMove(obj,iTarget){
clearInterval(obj.timer);
obj.timer=setInterval(function(){
var icur=parseInt(getStyle(obj,"width"));
var speed=(iTarget-icur)/8;
speed=speed>0?Math.ceil(speed):Math.floor(speed);
if(icur == iTarget){
clearInterval(obj.timer);
}else{
obj.style.width = icur+speed+"px";
}
},30)
}
</script>
之前已经说过getStyle函数可以处理border、font-szie...的变化,但也有无法直接套用处理的,那就是透明度。
对于透明度无法直接套用的原因:
因此为了可以一并处理透明度的问题,可以在这两个关键地方做一下兼容的处理。处理如下:
就此已经实现了透明度的变化,但仍然存在小bug,查看控制台发现透明度的值不是如期的0.3——1之间的变化,它存在着小数点。
<style>
ul,li{list-style: none;}
ul li{
width: 200px;
height: 100px;
background: red;
margin-bottom: 20px;
border: 2px solid black;
filter: alpha(opacity=30);
opacity: 0.3;
}
</style>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
<script>
window.onload=function(){
var aLi=document.getElementsByTagName("li");
for(var i=0; i<aLi.length; i++){
aLi[i].timer=null;
aLi[i].onmouseover=function(){
startMove(this,"opacity",100);
}
aLi[i].onmouseout=function(){
startMove(this,"opacity",30);
}
}
}
function getStyle(obj,attr){
if(obj.currentStyle){
return obj.currentStyle[attr];
}else{
return getComputedStyle(obj,false)[attr];
}
}
function startMove(obj,attr,iTarget){
clearInterval(obj.timer);
obj.timer=setInterval(function(){
var icur=0;
if(attr == "opacity"){
icur=parseFloat(getStyle(obj,attr))*100;
}else{
icur=parseInt(getStyle(obj,attr));
}
var speed=(iTarget-icur)/8;
speed=speed>0?Math.ceil(speed):Math.floor(speed);
if(icur == iTarget){
clearInterval(obj.timer);
}else{
if(attr == "opacity"){
obj.style.filter="alpha(opacity=)"+(icur+speed)+")";
obj.style.opacity=(icur+speed)/100;
}else{
obj.style[attr] = icur+speed+"px";
}
}
},30)
}
</script>