前言 一个朋友说要实现一个功能,他们在做一个GPS网站,上面有汽车(图片文件)在跑,然后问我怎么让汽车图片旋转。一开始我觉得没什么,毕竟CSS3里面已经有transform这个属性了,那么在javascript调用这个属性设置新值不就行了?试了之后才发现没这么简单……

演示地址   http://www.iweber.org/rotate/

前言   一个朋友说要实现一个功能,他们在做一个GPS网站,上面有汽车(图片文件)在跑,然后问我怎么让汽车图片旋转。一开始我觉得没什么,毕竟CSS3里面已经有transform这个属性了,那么在javascript调用这个属性设置新值不就行了?试了之后才发现没这么简单……

问题   首先,不得不提的IE,它不支持transform属性。那先不考虑这个的浏览器,说说chrome。CSS3有不少新属性在各个浏览器里面有不同的写法,比如border-shadow,我查了下,transform属性在不同浏览器下有-webkit-transform、-moz-transform、-o-transform各种写法。由于我们需要图片旋转角度不断变换,所以不能使用类来附加样式,只能使用"node.style.xxx = xxx;"的形式来改变样式。问题来了,我发现"node.style.transform = 'rotate(30deg)'"在chrome中报错!看来要根据不同浏览器设置对应的transform属性(有些CSS3属性虽然有多种写法,可是它们的简单形式是通用的,如border-shadow)。

参考文章    这是的一篇文章,讲解得非常详细,不过需要慢慢理解,我下面的解决方案是研究了一下午才写出的。大家想了解得更多,可查看该文章 

解决方案   经过一番探索,现在实现图片旋转主要有CSS3属性、滤镜和canvas三种方法。webkit浏览器使用CSS3属性应该是最简单的解决方案,而滤镜就是针对IE的,至于canvas方法,我试用了下,发现之前没接触过的话短时间不易掌握,有兴趣的可以看这个网址。下面就用CSS3属性和滤镜来解决问题。

  1. 检查浏览器支持哪种CSS3属性。方法是新建节点调出当前浏览器支持的所有style属性,然后依次查看有无相应的属性。CSS里面的属性到了javascript后不一定保持原来的写法,特别是多单词的属性,此时有一个特点,如margin-top到javascript中应写成marginTop,-webkit-transform应写成webkitTransform。于是有下面的代码:
<script type="text/javascript">
var transform = "";        //记录支持的CSS3样式
    window.onload = function(){
var imgNode = document.getElementById("blue7");

//先检测支持哪一种CSS3属性
        var transforms = new Array("transform", "MozTransform", 
            "webkitTransform", "OTransform", "msTransform");
var styles = document.createElement("div").style;
for(var i=0; i<transforms.length; i++){
if(transforms[i] in styles){
                transform = transforms[i];
break;
            }
        }
//alert(transform);
  1. 此时如果是webkit浏览器transform的值为对应的支持的属性值,IE的话值为""。
  2. 设置属性。此时虽然在webkit核心的浏览器下设置transform属性有简单的写法,但为了照顾IE,我统一使用矩阵变换的写法。步骤:接收要处理的节点及旋转角度,再生成对应矩阵,最后根据不同浏览器进行样式修改。代码如下:
function rotateIt(img, degree){
        img = typeof img == "string" ? document.getElementById(img) : img;
//img.style['-webkit-transform'] = "rotate(" + (-1*degree) + "deg)";    //简单旋转
        var cosa = Math.cos(degree*Math.PI/180), sina = Math.sin(degree*Math.PI/180);
//alert(Math.sin(Math.PI));        //最恐怖的地方
        if(degree == 90 || degree == 270)
            cosa = 0;
if(degree == 180)
            sina = 0;
var newMatrix = {M11: cosa, M12: (-1*sina), M21: sina, M22: cosa};

if(transform == ''){    //if IE
            img.style.filter = 
                "progid:DXImageTransform.Microsoft.Matrix(SizingMethod='auto expand')";
for (name in newMatrix)
                img.filters.item("DXImageTransform.Microsoft.Matrix")[name] = newMatrix[name];
/*img.style.left = (img.parent._clientWidth - img.offsetWidth)/2 + 'px';
            img.style.top = (img.parent._clientHeight - img.offsetHeight)/2 + 'px';*/
        }
else    //if not IE,使用矩阵方法旋转
            img.style[transform] = "matrix(" + newMatrix.M11  + "," + newMatrix.M12 + "," 
                + newMatrix.M21 + "," + newMatrix.M22 + ",0,0)";
    }
  1. 我说明下代码。旋转和缩放、翻转都是线性变换,根据这个思想可以构造出对应矩阵。上面我对webkit浏览器给出了简单旋转和矩阵旋转两种变换方式,不过要注意的是两种方法的旋转方向不同,所以简单旋转那里我乘以-1,统一逆时针旋转,其他的不难理解。IE浏览器的话先要对滤镜进行设置"auto expand",这样旋转的时候图片的容器才会随着改变大小,然后再进行滤镜矩阵对象赋值就行了。

总结   现在transform属性不比其他属性的支持度高,当我们要使用图片(其实可以是多种类型的节点)变换时要考虑不少兼容性问题,这就使得一个简单的功能编码起来很是麻烦,特别是我们要用javascript进行动态变换的时候。上面的代码其实还不够完善,比如IE下旋转图片会乱动,此时要重新定位,我没写,大家在IE下看我的演示页面就知道是怎样的。我把旋转功能写成一个函数,有兴趣的或者对矩阵不熟的读者可以使用函数方便地调用。