看了很多視頻、文章,最后卻通通忘記了,別人的知識依舊是別人的,自己卻什么都沒獲得。此系列文章旨在加深自己的印象,因此此系列文章大都是將別人的文章連復制帶寫而來,若有侵權,請及時通知,必定立即刪除。
首先回顧一下矩陣,為后面學martix作鋪墊

從上面這種寫法,已經(jīng)能看出矩陣乘法的規(guī)則了:系數(shù)矩陣第一行的2和1,各自與 x 和 y 的乘積之和,等于3

從transform開始:
前置屬性:
transform-origin
transform-style
perspective
perspective-origin
backface-visibility
2D變形:
translate
scale
rotate
skew
matrix
3D變形:
translate3d
scale3d
rotate3d
matrix3d
transform-origin用于指定元素變形的中心點。默認中心點就是元素的正中心,即XYZ軸的50% 50% 0處??梢酝ㄟ^該屬性改變元素在XYZ軸的中心點,正值表示正向位移,負值表示負向位移。(XYZ軸的正向分別是往右,往下,靠近用戶眼睛。反之為反向)
表示2維的x-offset/y-offset可以設px值也可以設%百分比,也可設top / right / bottom / left / center等keyword。表示3維的z-offset只能設px值,不能設%百分比,也沒有keyword。
默認中心點在元素正中心,因此關鍵字top等價于top center等價于50% 0%(x軸仍舊留在50%處,y軸位移到0%處)。同理各關鍵字例如right等價于right center等價于100% 50%,不多贅述
一圖勝千言:為圖片設置不同的中心點后,看它們旋轉,扭曲,縮放的效果。例如圖1表頭的第一行center表示transform-origin: center。第二行rotate(30deg);表示transform: rotate(30deg);

另外transform-origin指定變形中心點對translate位移沒有影響。translate位移始終相對于元素正中心進行位移,有懷疑精神的可以自己試一下。
其實transform-origin只是一個語法糖而已,你總是可以用translate來代替它。每個transform-origin都可以被兩個translate模擬出來(by CSS變形規(guī)范的編輯Aryeh Gregor)。例如:
transform: rotate(30deg);
transform-origin: 200px 300px;
//等價于
transform: translate(200px, 300px)
rotate(30deg)
translate(-200px, -300px);
transform-origin: 0 0;
transform-style這個屬性比較簡單只有兩個值flat和preserve-3d。用于指定舞臺為2D或3D,默認值flat表示2D舞臺,所有子元素2D層面展現(xiàn)。 preserve-3d看名字就知道了表示3D舞臺,所有子元素在3D層面展現(xiàn)。注意,在變形元素自身上指定該屬性是沒有用的,它用于指定舞臺,所以要在變形元素的父元素上設置該屬性。設定后,所有子元素共享該舞臺。一圖勝千言

.div1 {
float: left;
background-color: red;
transform: perspective(200px) rotateY(45deg);
}
.div1 img{
transform: translateZ(16px);
}
.p3d {
transform-style: preserve-3d;
}
<div class="div1"></div>
<div class="div1 p3d"></div>
兩圖唯一的區(qū)別是:右圖的父div上設了transform-style: preserve-3d;,因此呈現(xiàn)了3d效果。左圖的父div沒有設transform-style默認是flat,因此元素不會在Z軸展開(translateZ(16px)失效),只能呈現(xiàn)2D效果。
另外如果同時設了transform-style: preserve-3d;和overflow: hidden;,3D效果將失效,等價于transform-style: flat;。如果你發(fā)現(xiàn)3D效果沒有像預想地那樣出現(xiàn),可以檢查一下(包括祖先元素)是否有overflow: hidden;,該屬性將flatten everything…
perspective指定3D的視距。默認值是none表示無3D效果,即2D扁平化。上面例子代碼里其實已經(jīng)用到過該屬性了。介紹它之前,先借用rotateX / rotateY / rotateZ來明確一下xyz軸坐標的基本概念。一圖勝千言,依次是rotateX軸旋轉,rotateY軸旋轉,rotateZ軸旋轉:

.x {
transform: perspective(200px) rotateX(60deg);
}
.y {
transform: perspective(200px) rotateY(60deg);
}
.z {
transform: perspective(200px) rotateZ(60deg);
}



從圖中也可以看出,烤羊肉串就是X軸旋轉,鋼管舞就是Y軸旋轉,彩票轉盤就是Z軸旋轉。上面z軸只是一個點,想象一下就能明白,該點其實是一根垂直于屏幕的線,而perspective視距就是該線從屏幕到用戶眼睛的距離
實現(xiàn)3D的關鍵就是要有perspective視距,如果將上述代碼中perspective(200px)去掉

除了Z軸旋轉不受影響外,XY軸雖然還在旋轉,但失去了3D效果,是2D扁平化的旋轉。原因就是因為不設perspective的話,其默認值為none,沒有視距沒有3D
perspective 只能設px值,不能設%百分比。值越小表示用戶眼睛距離屏幕越近,相當于創(chuàng)建一個較大的3D舞臺。反之,值越大表示用戶眼睛 距離屏幕越遠,相當于創(chuàng)建一個較小的3D舞臺。這很容易理解,離的越近東西看起來越大,離的越遠東西看起來越小。但具體該怎么設呢?借用W3C的圖配合translateZ來幫助理解視距

圖中d就是perspective視距,Z就是translateZ軸的位移。Z軸正向位移時,3D舞臺將放大。反之,Z軸負向位移時,3D舞臺將縮小。 上圖Z是d的一半,因此3D舞臺上的元素將是原來的2倍。下圖Z同樣是d的一半,但由于是負值,所以3D舞臺上的元素將縮小三分之一
.divsp {
display: inline-block;
border: 1px blue dashed;
margin-left: 30px;
perspective: 100px;
}
.z1 {
transform: translateZ(-75px);
}
.z2 {
transform: translateZ(0px);
}
.z3 {
transform: translateZ(25px);
}
.z4 {
transform: translateZ(101px);
}
<div class="divsp"></div>
<div class="divsp"></div>
<div class="divsp"></div>
<div class="divsp"></div>
4張圖的視距都是100px,表示4張圖的3D舞臺距離你的眼睛100px。我們從右往左來理解。圖4的translateZ(101px)看到圖片消失了,因為3D舞臺距離你眼睛100px,而圖片從舞臺往Z軸正向位移101px,圖片到了你腦袋后面自然什么都看不見。如果設成translateZ(100px),相當于圖片緊貼著你的眼睛,所以全屏都是圖片。圖3的translateZ(25px),原始圖片為75px,放大后的圖片為100px。這是道初中數(shù)學題,你可以畫一個底邊是75px(圖片原始尺寸),高是75px(視距100px-Z軸位移25px=75px)的等腰三角形,然后高擴展到100px,底邊將等比例擴大3分之1至100px。圖2的translateZ(0px)表示Z軸沒有位移,因此仍舊是原始大小。圖4的translateZ(-75px),同樣是道初中數(shù)學題,原始圖片為75px,縮小到42.85px,再看看上面W3C的圖理解一下,很容易算出來
仔細看代碼的可以看出來,上面介紹XYZ軸旋轉時是直接在變形元素img上指定的transform: perspective(200px) rotateX(60deg);。而上面的代碼是給變形元素img的父div指定perspective: 100px;。你可以理解為前一種方式是perspective()函數(shù),后一種方式是perspective屬性。兩種指定方式是有區(qū)別的:
前者perspective()函數(shù)指定只針對當前變形元素,需要和transform其他函數(shù)一起使用,僅表示當前變形元素的視距。
后者perspective屬性指定用于3D舞臺,即3D舞臺的視距,里面的子元素共享這個視距

基點默認值是50% 50%即center,表示視距基點在中心點不進行任何位移。你可以讓基點在XY軸上進行位移,產生上圖那樣的效果。注意該屬性同樣應該定義在父元素上,適用于整個3D舞臺。它需要和perspective屬性結合著一起用。效果如下圖:

.td1 {
transform-style: preserve-3d;
perspective: 200px;
perspective-origin: center;
}
為節(jié)約篇幅,只貼出來圖1的3D舞臺的配置,其余8圖只需根據(jù)表頭修改perspective-origin即可。根據(jù)上面9宮格圖就比較容易理解perspective-origin視距基點的意思了。默認值50% 50%即center表示眼睛在舞臺正中心。然后根據(jù)XY軸的位移量,或關鍵字left(等價于x軸0%)等,調整眼睛看3D舞臺的位置
backface-visibility用于是否可以看見3D舞臺背面,默認值visible表示背面可見,可以設成hidden讓背面不可見。通常當旋轉時,如果不希望背面顯示出來,該屬性就很有用,設成hidden即可

.stage{
float: left;
margin: 5px;
perspective: 200px;
}
.container {
transform-style: preserve-3d;
}
.image {
backface-visibility: hidden;
}
.front {
position: absolute;
z-index: 1;
}
.back {
transform: rotateY(180deg);
}
.stage:nth-child(1) .container{ transform: rotateY(0deg); }
.stage:nth-child(2) .container{ transform: rotateY(30deg); }
.stage:nth-child(3) .container{ transform: rotateY(60deg); }
.stage:nth-child(4) .container{ transform: rotateY(90deg); }
.stage:nth-child(5) .container{ transform: rotateY(120deg); }
.stage:nth-child(6) .container{ transform: rotateY(150deg); }
.stage:nth-child(7) .container{ transform: rotateY(180deg); }
<div class="stage"> //為節(jié)約篇幅該DOM請無腦復制7個
<div class="container">


</div>
</div>
DOM結構中就能看出,是兩張圖片(一正一反)疊在了一起。由于變形元素img設了backface-visibility: hidden;, 當Y軸旋轉超過90度時(Y軸旋轉正好90度時,正中間圖4為一片空白,就像丁字褲在視線里消失了_),正面的圖片將不可見,底下的背面圖片顯示出來 了。如果將img的backface-visibility屬性去掉(默認為visibility),效果如下圖。Y軸旋轉超過90度時,將顯示正面的圖 片的背部(所謂背部對屏幕來說其實就是圖片矩陣的X軸值取反)

常見的3D的HTML結構如下:
<舞臺>
//為舞臺加上perspective
<容器>
//為容器加上preserve-3d,使容器內元素共享同一個3D渲染環(huán)境
<元素>
//為元素加上transform效果
</容器>
</舞臺>
translate位移系列中用于2D的有:translate,translateX,translateY
translate位移,類似于position:relative屬性??稍O單值,也可設雙值。正數(shù)表示XY軸正向位移,負數(shù)為反向位移。設單值表示只X軸位移,Y軸坐標不變,例如transform: translate(100px);等價于transform: translate(100px,0);。這點和CSS中其他單值屬性稍有不同,不要誤以為單值是X軸和Y軸均位移。當然最好還是用雙值,如果真的和Y軸無關,也請用translateX(100px),雖然效果是一樣的,但代碼可讀性更高。同理如果和X軸無關,可以用transform: translateY(100px);等價于transform: translate(0, 100px);
上面說了效果類似于position:relative屬性,但和position語義不同,position用于頁面布局,而translate 屬于transform中的一個系列,用于元素變形。你可能覺得語義不同有什么卵用,效果OK不就行了?就看你用什么標準來衡量效果了。CSS的神奇之處 在于你可以將一個屬性用在完全違背它原意的場景下,拋開代碼可讀性不談,違背原意有時還是會有細微差別的。如結合動畫效果時,translate能小于 1px過渡,因此動畫效果更為平滑。但position最小單位就是1px,動畫效果肯定打折扣。另外用translate實現(xiàn)動畫時,可以使用GPU, 動畫的FPS更高,而position顯然無法享受這個優(yōu)勢。其他如回流和重繪也都有差異。因此如果你在該用translate的地方用了 position,今后一些需求變動達不到要求,你也沒什么立場可抱怨的了
scale縮放系列中用于2D的有:scale,scaleX,scaleY
scale縮放,同樣可以設單值和雙值。單值時表示X軸和Y軸等值縮放。默認值為1,要縮小請設0.01~0.99之間的值,要放大請設超過1的值。例如縮小一倍可以transform: scale(.5);,放大一倍可以transform: scale(2);。效果在最上面介紹transform-origin時圖片里已經(jīng)有了,不多贅述。
如果只想X軸縮放,可以用scaleX(.5)相當于scale(.5, 1)。同理只想Y軸縮放,可以用scaleY(.5)相當于scale(1, .5)。

scale還能設負數(shù),負數(shù)會先將元素反轉再縮放,如transform: scale(-.5, -1.5);,效果見上面右圖。為何反轉能理解吧?XY軸像素矩陣各值取反后,效果等價于反轉。當然你同樣可以用rotate實現(xiàn)反轉
rotate旋轉系列中用于2D的有:rotate
正數(shù)表示順時針旋轉,負數(shù)表示逆時針旋轉。如transform: rotate(30deg);在2D層面上沒有rotateX / rotateY,它倆和rotateZ都是3D旋轉
skew扭曲系列中用于2D的有:skew,skewX,skewY
skew扭曲可以設單值和雙值。單值時表示只X軸扭曲,Y軸不變,如transform: skew(30deg);等價于transform: skew(30deg, 0);??紤]到可讀性,不推薦用單值,應該用transform: skewX(30deg);。skewY表示只Y軸扭曲,X軸不變。效果在最上面介紹transform-origin時圖片里已經(jīng)有了,不多贅述
matrix矩陣前面沒有直接接觸,但卻是所有2D變形的本質,上面所有2D變形效果都可以用matrix矩陣來實現(xiàn)
3D變形有translate3d位移,scale3d縮放,rotate3d旋轉, matrix3d矩陣。(注意skew扭曲是沒有3D的)。3D的用法和2D差不多,只不過多了個Z軸的值而已
translate3d位移系列中用于3D的有:translate3d,translateZ
translate3d(tx,ty,tz),其中tz的Z軸長度只能為px值,不能為%百分比。translateZ等價于translate3d(0,0,tz)。 Z軸的值越大表示離眼睛越近,元素就越大,但當值大于perspective視距時元素將消失,因為眼睛無法看見眼睛背后的東西,這在上面介紹 perspective時已經(jīng)介紹過,不再贅述。值越小表示離眼睛越遠,元素就越小。實際使用中translateZ效果和2D的scale縮放效果非常 像,但原理是有區(qū)別的,translateZ是Z軸上位移,而scale是XY軸的縮放。還是那句話,盡量將屬性用在符合屬性愿意的場合
scale3d縮放系列中用于3D的有:scale3d,scaleZ
cale3d(sx,sy,sz),其中sz為Z軸的縮放比例,取值同sx,sy一樣,在0.01~0.99時元素縮小,1時大小不變,大于1時元素變大。scaleZ等價于scale(1,1,sz)。需要注意的是單獨使用scale3d或scaleZ不會有任何效果,需要配合其他屬性在3D舞臺上才能出現(xiàn)效果,否則Z軸的縮放比例根本無法定義
rotate3d旋轉系列中用于3D的有:rotate3d,rotateX,rotateY,rotateZ
rotate3d(x,y,z,a)這里多了一個參數(shù)a(讀音是阿爾法…)表示3D舞臺上旋轉的角度,而xyz的取值為0~1為各軸的旋轉矢量值。rotate3d,rotateX,rotateY,rotateZ的效果在上面都有展示,不贅述。
matrix3d矩陣是所有3D變形的本質,上面所有3D變形效果都可以用matrix3d矩陣來實現(xiàn)
matrix(scaleX(),skewX(),skewY(),scaleY(),translateX(),translateY());
現(xiàn)在來看看變形對CSS層級的影響。說起層級,absolute絕對是層級間的高富帥,見一個睡一個,sorry,是見一個壓一個,sorry,是見一個覆蓋一個

//左圖


//右圖


左圖因為第一張img具有absolute,完全無視DOM結構中的順序,妥妥地覆蓋了第二張img。右圖給第二張img設了transform,通常我們會認為scale(1)是廢代碼,但實際從右圖已經(jīng)看出,由于設立transform,使元素具有了相當于absolute的層級,因此兩張img平級了,根據(jù)DOM中的順序,后者覆蓋了前者
(這里使用的是scale,你可以改成rotate,skew等,效果都一樣。即層級和transform有關,但和具體哪個transform函數(shù)無關)
因為absolute和transform平級,你可以調整上面兩張img的順序,這樣設了transform的圖片會被absolute覆蓋。如果你非要讓absolute高人一等,可以設z-index:1這樣層級會高于transform,達到覆蓋效果。
和absolute同系列的relative和fixed也適用上述層級關系。如果你頁面上有個fixed廣告標簽,頁面滾動時被transform元素覆蓋了,請不要驚訝,試試設一下z-index
rotate demo
<style>div{
margin:100px;
width:100px;
height:100px;
background:red;
transition: transform 0.5s linear;
}
div:hover{
transform:rotate(45deg) rotate(-70deg);
}</style>
<div></div>
等價于直接寫rotate(-30deg)
matrix表示法
<style>
div{
margin:100px;
width:100px;
height:100px;
background:red;
transition: transform 0.5s linear;
}
div:hover{
transform:matrix(0.5,0.5,-0.5,0.5,0,0) scale(1.414);
}
</style>
<div></div>
用matrix實現(xiàn)平移:
<style>
div{
margin:100px;
width:100px;
height:100px;
background:red;
transition: transform 0.5s linear;
}
div:hover{
transform:matrix(1,0,0,1,20,40);
}
</style>
<div></div>
scale的matrix表示法
<style>
div{
margin:100px;
width:100px;
height:100px;
background:red;
transition: transform 0.5s linear;
}
div:hover{
transform:matrix(1.5,0,0,1.5,0,0);
}
</style>
<div></div>
扭曲:沿著一個角度發(fā)生切變(高度不變,整體傾斜),又叫切變
skew matrix表示法
<style>
div{
margin:100px;
width:100px;
height:100px;
background:red;
transition: transform 0.5s linear;
}
div:hover{
transform:matrix(1,0,-1,1,0,0);
}
</style>
<div></div>
matrix(scaleX(),skewX(),skewY(),scaleY(),translateX(),translateY());
旋轉:
cos45 -sin45
sin45 cos45
matrix的測試器:
<meta charset="utf-8">
<style>
div{
margin:200px auto;
height:100px;
width:200px;
background:red;
font-size: 20px;
color:white;
line-height:100px;
text-align:center;
}
input{
height:30px;
}
</style>
參數(shù)1:<input type="number" value="1" step="0.5"/>
參數(shù)2:<input type="number" value="0" step="0.5"/>
參數(shù)3:<input type="number" value="0" step="0.5"/>
參數(shù)4:<input type="number" value="1" step="0.5"/>
<br />
參數(shù)5:<input type="number" value="0" step="10"/>
參數(shù)6:<input type="number" value="0" step="10"/>
<div>I love this world!</div>
<script>
var body = document.querySelector('body');
var target = document.querySelector('div');
body.onclick = function() {
var inputs = document.querySelectorAll('input');
var array = [];
[].forEach.call(inputs, function(input) {
array.push(input.value);
});
target.style.transform = 'matrix(' + array.join(',') + ')';
}
</script>
由以上可知,其他變換都可以由martix轉換而來,接下來講講原理:
matrix(scaleX(),skewX(),skewY(),scaleY(),translateX(),translateY());
旋轉:
cos45 -sin45
sin45 cos45
先看看容易點的2D矩陣matrix,共有6個參數(shù)matrix(a, b, c, d, e, f),各參數(shù)在矩陣中的位置:

元素原本的XY軸坐標經(jīng)matrix變換后,新坐標為x1,y1。計算公式如下:

這是線性代數(shù)里最簡單的內容了,雖然難一點的線性代數(shù)的知識全還給老師了,但這點應該不難理解,橫行乘豎行
以translate位移舉例。想將其在XY軸上相對原點位置位移,很簡單translate(tx, ty);即可。你也可以用matrix實現(xiàn)。原點坐標為xy,經(jīng)translate位移后新坐標需要為x1=x+tx,y1=y+ty。根據(jù)上圖,等價于matrix(1,0,0,1,tx,ty);。例如translate(50px, 100px);等價于matrix(1,0,0,1,50,100);
同理scale縮放后新坐標需要為x1=xsx,y1=ysy,因此等價于matrix(sx,0,0,sy,0,0);。例如scale(0.5, 1.5);等價于matrix(0.5,0,0,1.5,0,0);
位移和縮放比較簡單,rotate旋轉將元素旋轉一個角度稍微復雜點。旋轉后各像素的新坐標需要為x1=cos(a)x-sin(a)y,y1=sin(a)x+cos(a)y,因此等價于matrix(cos(a),sin(a),-sin(a),cos(a),0,0);。例如rotate(45deg);等價于matrix(0.53,0.85,-0.85,0.53,0,0);(cos(45)=0.53,sin(45)=0.85)
skew扭曲后各像素新坐標需要為x1=x+tan(ax)y,y1=tan(ay)x+y,因此等價于matrix(1,tan(ay),tan(ax),1,0,0);。例如skew(45deg);等價于matrix(1, 0, 1, 1, 0, 0);(tan(45)=1)
translate(tx, ty); matrix(1, 0, 0, 1, tx, ty);
scale(sx, sy); matrix(sx, 0, 0, sy, 0, 0);
rotate(a); matrix(cos(a), sin(a), -sin(a), cos(a), 0, 0);
skew(ax, ay); matrix(1, tan(ay), tan(ax), 1, 0, 0);
你可能會疑惑,誰會放著現(xiàn)成的transform內置函數(shù)不用,去用matrix/matrix3d?除非你故意不想讓他人看懂代碼,你可以將
transform: rotate(15deg) translate(230) scale(1.5,2.6) skew(220deg,-150deg) translate(230px);
寫成
transform: matrix(1.06,1.84,0.54,2.8,466px,482px);
保證你的代碼誰也看不懂誰也改不了,知識產權那是妥妥的,公司辭退誰都不會辭退到你。但恕我才疏學淺,除了滿足個人好奇心,體會到一點智商上的優(yōu)越感外,實際項目中似乎確實沒什么卵用…本篇您就當看個樂呵
transition從效果上看是一種平滑過渡的動畫,本質上是在線性時間內將屬性從開始值過渡到結束值。例如獲得焦點,點擊鼠標等動作導致CSS屬性值的變化是瞬間完成的,感覺有點生硬。用transition可以指定在某時間段內將屬性值平滑過渡,增強用戶體驗
4個子屬性
transition-property
transition-duration
transition-timing-function
transition-delay
transition-property指定需要過渡的CSS屬性。并不是所有屬性都能過渡的,只有能數(shù)字量化的CSS屬性才能過渡。哪些屬于能數(shù)字量化的CSS屬性呢?例如:
顏色系,如color,background-color,border-color,outline-color等
數(shù)字系,實在太多了,如width,height,top,right,bottom,left,line-height,background-position,word-spacing,font-weight,vertical-align,outline-outset,z-index,zoom,opacity等。
01系,如visibility(0表示隱藏,1表示顯示)
除了單個指定屬性外,transition-property還能設為all,表示所有屬性都將獲得過渡效果。
transition-duration過渡需要的時間,單位可指定s秒,也可指定ms毫秒。默認值是0,表示立刻變化。如果設置了多個過渡屬性,但每個屬性的過渡時間都一樣,你沒必要為transition-duration設多個值,只有設一個即可,該值會應用到所有過渡屬性上。
transition-timing-function過渡函數(shù),有l(wèi)inear,ease,ease-in,ease-out,ease-in-out,cubic-bezier(n,n,n,n),steps。其實它們都是貝賽爾曲線。如下

看貝賽爾曲線就知道了,linear是勻速過渡,ease是先快再慢的節(jié)奏,ease-in是加速沖刺的節(jié)奏,ease-out是減速到停止的節(jié)奏,ease-in-out是先加速后減速的節(jié)奏
現(xiàn)在動畫的精度越來越高,如果預定義好的這些函數(shù)滿足不了你的需求,可以通過cubic-bezier(n,n,n,n)自定義平滑曲線。從上面的圖形中觀察到,貝塞爾曲線有4個點,左下為起始點P0坐標固定為(0,0),右上為終點P3坐標固定為(1,1),中間有兩點P1和P2的坐標就是cubic-bezier(n,n,n,n)的參數(shù)。通過4條連起來的直線,生成平滑的曲線。一圖勝千言
steps函數(shù)有兩個參數(shù),第一個參數(shù)是分割的數(shù)量,第二個參數(shù)可選,是關鍵字start,end,不設的話默認是end。因此steps(4, start)等價于step-start(4),steps(4, end)等價于step-end(4)。例如steps(4, end)并非像貝塞爾曲線那樣平滑過渡,相當于將過渡過程從頭至尾分成4步,在每一步瞬間完成過渡。最上面的例子中已經(jīng)有所展示,很容易理解
transition-delay延遲開始過渡的時間,默認值是0,表示不延遲,立即開始過渡動作。單位是s秒或ms毫秒。w3cschool上沒說的是,該屬性還能設負時間,意思是讓過渡動作從該時間點開始啟動,之前的過渡動作不顯示。不信你可以試試。負延遲時間在animation動畫里確實有用,但恕我才疏學淺,在transition里實在不知道有什么卵用
你可以單獨指定這4個子屬性,也可以像background等屬性一樣,合并在transition屬性里指定。但合并時要注意,因為有transition-duration和transition-delay都是時間,瀏覽器會根據(jù)先后順序,將第一個時間認作為transition-duration,第二個時間認作為transition-delay
是分開或者合并指定并無標準答案。分開指定可能代碼易讀性高一點。但當頁面需要適應各瀏覽器時,每個都要加上-ms-,-moz-等前綴,代碼會變的很多,合并在一起代碼稍微少點。另外也可以同時指定多個過渡效果,例如transition: background 1s linear 2s, border-radius 2s ease-in 3s
常見的就是偽類觸發(fā):hover,:focus,:active,:checked等。還有例如@media媒體查詢,根據(jù)設備大小,橫屏豎屏切換時觸發(fā)。還有如click,keydown等JS事件觸發(fā)。頁面加載也能觸發(fā)就不一一列舉了。總之過渡的本質是在時間段內平滑過渡屬性值,與怎么觸發(fā)沒有關系
transition既然涉及時間,自然就有事件。參照MDN有transitionend事件,顧名思義就是過渡結束時觸發(fā)該事件。但該事件比較坑。例如過渡padding時,代碼如下
#tempDiv {
padding: 1px;
transition-property: padding;
transition-duration: 1s;
}
#tempDiv:hover {
padding: 5px;
}
function showMessage() {
console.log('finished'); //過渡結束時觸發(fā)打印log
}
var element = document.getElementById("tempDiv");
element.addEventListener("transitionend", showMessage, false);
你可以代碼貼到瀏覽器里試試,結果出現(xiàn)4條log。因為過渡屬性指定的是padding,所以在padding-top,padding-right,padding-bottom,padding-left過渡結束時均觸發(fā)了transitionend事件。因此log被打印了4次。
如果上述CSS中將transition-property: padding;改為all,同樣會觸發(fā)4次。除非你改成transition-property: padding-top;這樣才能只觸發(fā)一次,但現(xiàn)實中只過渡一邊的場景非常少,通常都是4邊同時過渡,因此例如padding,margin,border之類的屬性,用transitionend事件會有多次捕捉的情況發(fā)生。
ransition過渡時有時會出現(xiàn)一些比較曖昧的情形,比如設成em的屬性,如你所知em是根據(jù)font-size來計算的。類似還有rem,vh,vw等都是根據(jù)另一個屬性的值來計算得到它的值。舉個例子padding:2em;,如果font-size被改變了,此時padding的“書面值”不變,仍舊是2em,但“實際值”將會發(fā)生變化并觸發(fā)transition過渡。這被稱作“隱式過渡”。多數(shù)瀏覽器會實現(xiàn)隱式過渡,但傳聞IE很特別,具體有多特別我也沒試過
開關過渡,顧名思義就是觸發(fā)源的事件結束后會恢復到原始狀態(tài)。永久過渡就是過渡后不恢復到原始狀態(tài)。上面的例子都是開關過渡,當鼠標hover事件結束后,圖片恢復原始尺寸。但永久過渡的話,鼠標hover事件結束后,圖片仍舊保持放大后的尺寸。
//開關過渡,可以恢復原狀態(tài)
.transition {
transition: all 1s ease-in-out;
}
.transition:hover {
transform: scale(1.5);
}
//永久過渡,不能恢復到原狀態(tài)
.forever {
transition: all 1s ease-in-out 999999s;
}
.forever:hover {
transform: scale(1.5);
transition: all 1s ease-in-out;
}
因為回到原始尺寸的transition-duration被設成了一個很大的時間,999999s差不多有12天,理論上你頁面開12天就能看到關閉過渡的效果,但現(xiàn)實等于永久過渡。該技巧無需任何JS腳本
通常我們屬性過渡時,都是定值到定值的過渡,例如width:100px過渡到200px。但要過渡到width:auto就不行了。就算你指定transition: width 1s linear;會發(fā)現(xiàn)根本不會有1秒的平滑的過渡效果,而是瞬間完成過渡
.div1 {
background-color: red;
width: 100px;
height: 50px;
}
#box1:hover {
width: auto;
transition: width 1s linear;
}
<div id="box1" class="div1"></div>
如果想要過渡效果,必須將auto轉換為固定值。那么問題來了,如何轉換呢?需要靠JS,通過getComputedStyle獲取auto后的固定值后,通過style設置該值,然后再觸發(fā)CSS的過渡效果。
鼠標hover后,用JS模擬過渡效果
window.onload = function(){
var box = document.getElementById("box2"),
originWidth = box.clientWidth,
width2AutoLater = null,
width2OriginLater = null;
var width2Auto = function(element, time) {
if (typeof window.getComputedStyle == "undefined") return;
var width = window.getComputedStyle(element).width;
element.style.width = "auto";
var targetWidth = window.getComputedStyle(element).width;
element.style.width = width;
setTimeout(function() {
element.style.transition = "width "+ time +"ms linear";
element.style.width = targetWidth;
}, 10);
};
var width2Origin = function(element, time) {
setTimeout(function() {
element.style.transition = "width 0s linear";
element.style.width = originWidth + "px";
}, 10);
};
function callLater(func, paramA, paramB){
return function(){
func.call(this, paramA, paramB);
};
}
width2AutoLater = callLater(width2Auto, box, 1000);
width2OriginLater = callLater(width2Origin, box, 1000);
box.addEventListener("mouseenter", width2AutoLater);
box.addEventListener("mouseleave", width2OriginLater);
}
<div id="box2" class="div1"></div>
思路:給div注冊了mouseenter和mouseleave事件來模擬hover效果。
mouseenter綁定width2Auto函數(shù),函數(shù)里臨時將div的width設為auto后,用getComputedStyle得到寬度值,將該寬度值和transition設進該div的style里。
mouseleave綁定width2Origin函數(shù),函數(shù)里將div的width改回初始值。
因為注冊事件的函數(shù)addEventListener的第二個參數(shù)是回調函數(shù)名,不能給回調函數(shù)傳參數(shù)。因此使用閉包的特性,定義了callLater中間函數(shù),函數(shù)里通過call調用上述兩個函數(shù)
animation:
transition過渡屬性通過讓屬性在時間段內根據(jù)貝塞爾曲線平滑過渡,呈現(xiàn)出動畫效果,但畢竟功能有限。本篇介紹的animation屬性和傳統(tǒng)的動畫制作一樣,能控制幀的每一步,制作出更強大的動畫效果
子屬性:
animation-name
animation-duration
animation-timing-function
animation-delay
animation-iteration-count
animation-direction
animation-play-state
animation-fill-mode
@keyframes
animation-name指定@keyframes的名字,CSS加載時會應用該名字的@keyframes規(guī)則來實現(xiàn)動畫
animation-duration動畫持續(xù)時間,默認是0表示無動畫,單位可以設s秒或ms毫秒
animation-timing-function動畫播放方式,默認值ease,可以設linear,ease,ease-in,ease-out,ease-in-out,cubic-bezier(n,n,n,n),steps。關于貝塞爾曲線和steps可以參照上一篇transition,和transition-timing-function類似,不多贅述。
animation-delay延遲開始動畫的時間,默認值是0,表示不延遲,立即播放動畫。單位是s秒或ms毫秒。允許設負時間,意思是讓動畫動作從該時間點開始啟動,之前的動畫不顯示。例如-2s 使動畫馬上開始,但前 2 秒的動畫被跳過。
animation-iteration-count動畫循環(huán)播放的次數(shù),默認值為1,即放完一遍后不循環(huán)播放。除數(shù)字外也可以設關鍵字infinite表示無限循環(huán)播放。
animation-direction動畫播放的方向,可設normal,alternate,alternate-reverse。默認值是normal表示正常播放動畫。alternate表示輪轉正反向播放動畫,即動畫會在奇數(shù)次(1,3,5…)正常播放,而在偶數(shù)次(2,4,6…)反向播放。alternate-reverse正好反過來,奇數(shù)次反向播動畫,偶數(shù)次正向播動畫
.myDiv1 {
width: 75px;
height: 75px;
background-color: red;
position:relative;
animation:aDirection 5s infinite;
}
@keyframes aDirection {
from {left:0px;}
to {left:200px;}
}
.alter { animation-direction:alternate; }
.alterR { animation-direction:alternate-reverse; }
<div class="myDiv1"></div>
<div class="myDiv1 alter"></div>
<div class="myDiv1 alterR"></div>
其實例子頁面就能看到alternate/alternate-reverse的動畫效果可以平滑地實現(xiàn)反轉效果。例如例子頁面中閃爍提示的例子。你可以用text-decoration: blink來實現(xiàn)閃爍,但它的功能有限支持有限。用CSS3動畫實現(xiàn)的閃爍效果更棒。(當然閃爍的使用必須克制,要定時定次數(shù),不能無限閃。無限閃通常會讓用戶很生氣后果很嚴重):
@keyframes blink {
to { color: transparent } //文字色平滑過渡到透明
}
.blink {
animation: .5s blink 6; //觸發(fā)動畫6次,因為設了alternate,所以看上去閃爍了3次
animation-direction: alternate;
}
animation-play-state動畫的狀態(tài),可設running,paused。默認值running表示正在播放動畫,paused表示暫停動畫。通常在JS端使用該屬性object.style.animationPlayState=”paused”來暫停動畫。
animation-fill-mode動畫的時間外屬性,可設none,forwards,backwards,both。默認值none表示動畫播完后,恢復到初始狀態(tài)。forwards當動畫播完后,保持@keyframes里最后一幀的屬性。backwards表示開始播動畫時,應用@keyframes里第一幀的屬性,要看出效果,通常要設animation-delay延遲時間。both表示forwards和backforwards都應用
例如設置2s的延遲時間。初始為紅色,第一幀動畫變?yōu)榫G色,最后一幀動畫變?yōu)樗{色
.myDiv2 {
width: 75px;
height: 75px;
background-color: red;
position:relative;
animation:mymove 5s 1 2s;
}
@keyframes mymove {
from {left:0px; background-color:green;}
to {left:200px; background-color: blue;}
}
.forwards { animation-fill-mode:forwards; }
.bkforwards { animation-fill-mode:backwards; }
.both { animation-fill-mode:both; }
<div class="myDiv2"></div>
<div class="myDiv2 forwards"></div>
<div class="myDiv2 bkforwards"></div>
<div class="myDiv2 both"></div>
圖1不解釋了,最正常的效果,初始為紅色,動畫開始成綠色,動畫結束成藍色,結束后恢復初始狀態(tài)紅色。圖2設成forwards,和圖1的區(qū)別是動畫結束后不恢復初始狀態(tài),仍舊是藍色。圖3設成backwards,在動畫開始前(即延遲時間段內)應用第一幀的動作,設成了綠色,動畫結束后恢復原始狀態(tài)。圖4設成both兼具forwards和backwards的效果
@keyframes動畫幀就是區(qū)別animation和transition的關鍵。在transition中是無法更細致地控制時間段內執(zhí)行的動作的,而在animation中用@keyframes可以細致地指定第一幀要干什么,第二幀要干什么
語法:@keyframes開頭,后接animation-name。實體內可以創(chuàng)建%百分比來劃分時間段。關鍵字from等于0%,to等于100%
@keyframes mymove {
0% {top:0px; left:0px; background:red;}
25% {top:0px; left:100px; background:blue;}
50% {top:100px; left:100px; background:yellow;}
75% {top:100px; left:0px; background:green;}
100% {top:0px; left:0px; background:red;}
}
你可以單獨指定上面這些子屬性,也可以像background等屬性一樣,合并在animation屬性里指定。例如animation: moveten 1s step(10,end) infinite alternate 3s backwards;。但合并時要注意,因為有animation-duration和animation-delay都是時間,瀏覽器會根據(jù)先后順序,將第一個時間認作為animation-duration,第二個時間認作為animation-delay。
分開指定可能代碼清晰點,但因為頁面需要適應各瀏覽器時,每個都要加上-ms,-moz等前綴的話代碼會變的很多,合并在一起代碼稍微少點。
另外也可以同時指定多個動畫效果,例如animation: moveten1 1s ease .5s, moveten2 2s ease 1s forwards;
寫幾個demo來實踐一下:
正負旋轉相消3D動畫
loading
簡單來說,瀏覽器為了提升動畫的性能,為了在動畫的每一幀的過程中不必每次都重新繪制整個頁面。在特定方式下可以觸發(fā)生成一個合成層,合成層擁有單獨的 GraphicsLayer。
需要進行動畫的元素包含在這個合成層之下,這樣動畫的每一幀只需要去重新繪制這個 Graphics Layer 即可,從而達到提升動畫性能的目的。
那么一個元素什么時候會觸發(fā)創(chuàng)建一個 Graphics Layer 層?從目前來說,滿足以下任意情況便會創(chuàng)建層:
- 硬件加速的 iframe 元素(比如 iframe 嵌入的頁面中有合成層)
- 硬件加速的插件,比如 flash 等等
- 使用加速視頻解碼的 元素
- 3D 或者 硬件加速的 2D Canvas 元素
- 3D 或透視變換(perspective、transform) 的 CSS 屬性
- 對自己的 opacity 做 CSS 動畫或使用一個動畫變換的元素
- 擁有加速 CSS 過濾器的元素
- 元素有一個包含復合層的后代節(jié)點(換句話說,就是一個元素擁有一個子元素,該子元素在自己的層- 里)
- 元素有一個 z-index 較低且包含一個復合層的兄弟元素
參考鏈接: