mass Framework css模塊 v3


本模塊最大的亮點是完美解決了一個世界難題,在IE678下模擬CSS3 transform 2D。

CSS3 transform 2D歸根結底就是矩陣變換的問題,大家都知道利用IE的矩陣濾鏡來解。但里面的坑太多,一旦發生旋轉或扭曲,然后再進行位移,之前所有的JS庫都是計算錯誤的。本來將一步步帶你揭開這謎底。

要實現css transform 2D,就要動用到一個CSS3新屬性——transform。但在本文寫作之時,還沒有任何一個瀏覽器支持w3c所說的那個標准屬性,都是帶前綴。因此mass Framework,使用了一個叫cssName的方法,取得所有能用的私有實現名。transform在框架可能用WebkitTransform, MozTransform, OTransform, msTramsform代替。你們會注意,IE9下竟然是小寫開頭,這是一個坑。

賦值時,有兩種形式。最初是使用translate, scale, rotate, skew等方法進行變菁,后來添加了一個與IE濾鏡很相似的matrix方法。

-webkit-transform: rotate(60deg) skew(0deg, -30deg) scale(1, 1.15);
-webkit-transform: matrix(-1,0,0,1,0,0)

此外,還有translateX,translateY,skewX,skewY,scaleX,scaleY。它們在CSS中不區分大小寫的,因此JS框架要使用toLowerCase避雷。此外,skew是不標准的,這是第二個坑。

像skew與rotate開頭的方法,它們的參數必須帶單位,這是第三個坑。

scale方法允許只傳入一個參數,第二個參數默認為等於第一個參數,這是第四個坑。

matrix形式下賦值,FF的第五個參數與第六個參數必須帶單位,這是第五個坑。

像"rotate(60deg) skew(0deg, -30deg) scale(1, 1.15)"這樣的寫法,其實是進行了三次矩陣乘法,並且可以重復定義兩個以上相同函數。這是第七個坑。

-webkit-transform: translate(10px 20px) rotate(60deg) skew(0deg, -30deg) scale(1, 1.15) translateX(110px)//位移了兩次了。

取值時,都會自動轉換為matrix形式,但每個瀏覽器的精度都不一樣,這非常不利於測試。比如,FF小數點后有六位,opera有兩位,IE9有五位,webkit系多達16位,另,IE678的矩陣濾鏡也是16位。對於太小的位數,我們完全可以忽略掉。在瀏覽器中,如果一個數字的小數點后有超過八位數值,就會用指數表示,那么我們可以用/e/來檢測,再用toFixed進行微調!這是第八坑。

竟然是矩陣相乘,必須有一個初始矩陣。但如果沒有設置css3 transform屬性,標准瀏覽器是返回"none"字符串,而不是我們期望的matrix形式。IE678也一樣,返回null。由於標准瀏覽器的計算都是瀏覽器自行搞定,難點就在IE了。

   var ident  = "DXImageTransform.Microsoft.Matrix"
    adapter[ "transform:get" ] = function(node, name){
        var m = $._data(node,"matrix")
        if(!m){
            if(!node.currentStyle.hasLayout){
                node.style.zoom = 1;
            }
            //IE9下請千萬別設置  
 
 
 
         
            //http://www.cnblogs.com/Libra/archive/2009/03/24/1420731.html
            if(!node.filters[ident]){
                var old = node.currentStyle.filter;//防止覆蓋已有的濾鏡
                node.style.filter =  (old ? old +"," : "") + " progid:" + ident + "(sizingMethod='auto expand')";
            }
            var f = node.filters[ident];
            m = new $.Matrix2D( f.M11, f.M12, f.M21, f.M22, f.Dx, f.Dy);
            $._data(node,"matrix",m ) //保存到緩存系統,省得每次都計算
        }
        return name === true ? m : m.toString();
    }

上面代碼有幾個注意點,濾鏡必須hasLayout,因此使用zoom hack。由於濾鏡都是共用一個屬性,我們不知道這元素之前綁定了多個濾鏡,如透明濾鏡,PNG補丁濾鏡,盒子陰影濾鏡,這些都是很常用的,因此我們要防止覆蓋已有的濾鏡。這是第九個坑。brendankenny在《On the Behavior of 2d Transformations in Internet Explorer, Part 2》吹噓,其找到一種方法能快速圍繞元素中心變形,其實是大謬其然!直接覆寫已有濾鏡了!

// set linear transformation via Matrix Filter
var filt = 'progid:DXImageTransform.Microsoft.Matrix(';
filt +=   'M11=' + a;
filt += ', M21=' + b;
filt += ', M12=' + c;
filt += ', M22=' + d;
filt += ', SizingMethod="auto expand")';
target.style['filter'] = filt;//★★★出問題的代碼
 
// assuming a-d are local variables
// and halfW and halfH are initialized properly
 
// horizontal shift
a = Math.abs(a); // or go ternary
c = Math.abs(c);
var sx = (a - 1)*halfW + c*halfH;
 
// vertical shift
b = Math.abs(b);
d = Math.abs(d);
var sy = b*halfW + (d - 1)*halfH;
 
// translation, corrected for origin shift
// rounding helps--but doesn't eliminate--integer jittering
target.style.left = Math.round(x + e - sx) + 'px';
target.style.top = Math.round(y + f - sy) + 'px';

而元素默認是圍繞中心變形,這就是最大的坑,第十個坑。IE濾鏡是圍張左上角變形的。為了模擬這效果,老外前赴后繼封閉研究它。其中最傑出的方案,由heygrady 在《Correcting Transform Origin and Translate in IE》,至后來的 useragentman的cssSandpaper,louisremi的jquery.transform2都是沿襲其思路。然后,美中不足的是heygrady 取變形前的矩形尺寸是不可行的:

  var filter = $elem[0].style.filter;
    $elem[0].style.filter = '';
    
    // measure the element
    var width = $elem.outerWidth();
    var height = $elem.outerHeight();
    
    // re-do the filter
    $elem[0].style.filter = filter;//這其實變不回去了

它們計算新矩陣的坐標也異常復雜,IE下只能用相對定位或margin來模擬,但不管什么做,都要重新計算左下角的坐標。haygrady用了一個文件jquery.matrix.calculations.js來放置這些函數,可見復雜度多大。就算計出來,多多少少有偏差。因此在這里我必須轉向了。下面是我獲取變形前的矩形的尺寸的方法:

           var filter = node.filters[ident];
            filter.M11 =  filter.M22 = 1;//取得未變形前的寬高,原始矩陣為[1,0,0,1,0,0]
            filter.M12 =  filter.M21 = 0;
            var width = node.offsetWidth;
            var height = node.offsetHeight;
            filter.M11 = m.a;//進行矩陣變換
            filter.M12 = m.c
            filter.M21 = m.b;
            filter.M22 = m.d;
            filter.Dx  = m.tx;
            filter.Dy  = m.ty;
            $._data(node,"matrix",m);
            var tw =  node.offsetWidth, th = node.offsetHeight;//取得變形后高寬
            node.style.position = "relative";
            node.style.left = (width - tw)/2  + m.tx + "px";
            node.style.top = (height - th)/2  + m.ty + "px";

可能有人問,為什么不一開始就緩存它們的寬高呢?因為可能會有直接把濾間寫到外部樣式表的情況,而這樣式表又先於我們的庫加載,因此這時也取得變形前的寬高。

剩下來就是計算寬高了,這個用矩陣乘法就可以算出了,結果也在上面。

最后放個例子:

          $.require("ready,css",function(){

                $(".sample1").css("transform","scale(1.3) rotate(270deg) skew(-40deg, 0deg) translate(200px,-140px)")

            });

源碼地址:css.js, css_fix.js

這是測試


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM