CSS å¨ç»ä¸ JavaScript å¨ç»çæ§è½
对ä¼å¤åºç¨ç¨åºèè¨ï¼å¨ç»å¯¹æä¾å好çç¨æ·ä½éªæçå
³é®çä½ç¨ãæä»¬æå¾å¤æ¹å¼çæ web å¨ç»ï¼æ¯å¦ CSS transition å animation æè
åºäº JavaScript çå¨ç»ï¼ä½¿ç¨ requestAnimationFrame()ï¼ãå¨è¿ç¯æç« ä¸ï¼æä»¬åæ CSS å¨ç»å JavaScript å¨ç»çæ§è½å·®å¼ã
CSS è¿æ¸¡åå¨ç»
CSS ä¸ç transition å animation é½å¯ä»¥ç¨äºç¼åå¨ç»ï¼å®ä»¬é½æåèªç使ç¨åºæ¯ï¼
- CSS
transitionæä¾äºä¸ä¸ªç®åçæ¹å¼å»åé å½åæ ·å¼ä¸ç»æç¶ææ ·å¼ä¹é´çå¨ç»ï¼æ¯å¦ä¸ä¸ª button çæ®éç¶æå hover ç¶æã尽管ä¸ä¸ªå ç´ å¤äºè¿æ¸¡ç¶æä¸ï¼æ°çè¿æ¸¡å¨ç»ä¹ä¼ç«å³ä»å½åæ ·å¼å¼å§ï¼è䏿¯ç´æ¥è·³è½¬å° CSS çæç»ç¶æãæµè§ä½¿ç¨ CSS transition 以è·åæ´å¤ç»èã - å¦ä¸æ¹é¢ï¼CSS
animationå 许å¼åè å»éè¿ä¸ä¸ªåå§ç¶æå±æ§å¼éå䏿ç»ç¶æå±æ§å¼éååé å¨ç»ï¼è䏿¯ååçåå§åæç»ç¶æãCSS animations ç±ä¸¤é¨åç»æï¼æè¿° CSS å¨ç»çæ ·å¼ï¼ä»¥åä¸ç»å ³é®å¸§ï¼è¡¨ç¤ºå¨ç»æ ·å¼çå¼å§åç»æç¶æï¼ä»¥åå¯è½çä¸é´ç¶æãæµè§ä½¿ç¨ CSS animation 以è·åæ´å¤ç»èã
å°±æ§è½æ¹é¢æ¥è¯´ï¼æ 论éè¿ CSS animation è¿æ¯ transition åé å¨ç»ï¼é½æ²¡æåºå«ãå¨è¿ç¯æç« ä¸äºè é½å½ç±»ä¸ºåºäº CSS çå¨ç»ã
requestAnimationFrame
requestAnimationFrame() æä¾äºä¸ç§ç¨ JavaScript 代ç å¶ä½å¨ç»ç髿æ¹å¼ãæ¬æ¹æ³çåè°å½æ°å¨ç»å¶ä¸ä¸å¸§ä¹åç±æµè§å¨è°ç¨ãä¸éè¦ä¸ä¸ªå»¶è¿åæ°ç setTimeout() æ setInterval() ç¸æ¯ï¼requestAnimationFrame() æçé«å¾å¤ãå¼å人åå¯ä»¥å¨ requestAnimationFrame() åè°å½æ°ä¸éè¿ç®åå°æ¹åå
ç´ çæ ·å¼ï¼æè
æ´æ°ç»å¸ç»å¶ï¼ççï¼æ¥å建å¨ç»ã
夿³¨ï¼å CSS transitions å animations 䏿 ·ï¼å½é¡µé¢å¨åå°è¿è¡æ¶ï¼requestAnimationFrame() 伿åã
æ´å¤ç»è请é 读 animating with JavaScript from setinterval to requestAnimationFrame.
æ§è½å¯¹æ¯ï¼
transitions vs. requestAnimationFrame
äºå®ä¸ï¼å¤§å¤æ°åºæ¯ä¸ï¼åºäº CSS çå¨ç»å 乿¯è· JavaScript å¨ç»è¡¨ç°ä¸è´ââè³å°å¨ FireFox 䏿¯å¦æ¤ãä¸äºåºäº Javascript çå¨ç»åºï¼å GSAP å Velocity.JSï¼çè³å£°ç§°ä»ä»¬å¨æ§è½ä¸å¯ä»¥å徿¯åç CSS transition/animation æ´å¥½ãè¿æ¯å¯è½çï¼å 为å¨éç»äºä»¶åçä¹åï¼CSS transition å animation å¨ä¸»ç UI 线ç¨ä»
ä»
æ¯éæ°ééå
ç´ çæ ·å¼ï¼è¿è·éè¿ requestAnimationFrame() åè°è·åéæ°ééå
ç´ æ ·å¼æ¯ä¸æ ·çï¼ä¹æ¯å¨ä¸ä¸æ¬¡éç»ä¹å触åãåå¦äºè
齿¯å¨ä¸» UI 线ç¨å建çå¨ç»ï¼é£å®ä»¬å¨æ§è½æ¹é¢æ²¡æå·®å¼ã
å¨è¿ä¸èï¼æä»¬å°ä¼ä½¿ç¨ FireFox éè¿ä¸ä¸ªæ§è½æµè¯ï¼å»ççåªç§å¨ç»æ¹å¼æ´å¥½ã
å¯ç¨ FPS å·¥å ·
å¨è¿è¡ç¤ºä¾ä¹åï¼è¯·å¯ç¨ FPS å·¥å ·å æ¥çå½å帧éçï¼
- å¨å°åæ ä¸ï¼è¾å
¥ about:configï¼ç¹å»
I'll be careful, I promise!æé®ï¼ä»¥è¿å ¥é ç½®å±å¹ã
- å¨æç´¢æ ä¸æç´¢
layers.acceleration.draw-fpsé¦é项ã - åå»è¯¥æ¡ç®å°å¼è®¾ç½®ä¸º
trueãç°å¨ä½ å¯ä»¥å¨ Firefox çªå£çå·¦ä¸è§çå°ä¸ä¸ªç´«è²çæ¡ã第ä¸ä¸ªæ¡ä»£è¡¨ FPSã
è¿è¡æ§è½æµè¯
å¼å§å¨ä¸é¢çæµè¯ä¸ï¼æ»å
± 1000 个 <div> å
ç´ éè¿ CSS å¨ç»è¿è¡åæ 转æ¢ã
const boxes = [];
const button = document.getElementById("toggle-button");
const boxContainer = document.getElementById("box-container");
const animationType = document.getElementById("type");
// create boxes
for (let i = 0; i < 1000; i++) {
const div = document.createElement("div");
div.classList.add("css-animation");
div.classList.add("box");
boxContainer.appendChild(div);
boxes.push(div.style);
}
let toggleStatus = true;
let rafId;
button.addEventListener("click", () => {
if (toggleStatus) {
animationType.textContent = " requestAnimationFrame";
for (const child of boxContainer.children) {
child.classList.remove("css-animation");
}
rafId = window.requestAnimationFrame(animate);
} else {
window.cancelAnimationFrame(rafId);
animationType.textContent = " CSS animation";
for (const child of boxContainer.children) {
child.classList.add("css-animation");
}
}
toggleStatus = !toggleStatus;
});
const duration = 6000;
const translateX = 500;
const rotate = 360;
const scale = 1.4 - 0.6;
let start;
function animate(time) {
if (!start) {
start = time;
rafId = window.requestAnimationFrame(animate);
return;
}
const progress = (time - start) / duration;
if (progress < 2) {
let x = progress * translateX;
let transform;
if (progress >= 1) {
x = (2 - progress) * translateX;
transform = `translateX(${x}px) rotate(${
(2 - progress) * rotate
}deg) scale(${0.6 + (2 - progress) * scale})`;
} else {
transform = `translateX(${x}px) rotate(${progress * rotate}deg) scale(${
0.6 + progress * scale
})`;
}
for (const box of boxes) {
box.transform = transform;
}
} else {
start = null;
}
rafId = window.requestAnimationFrame(animate);
}
å¨ç»å¯ä»¥éè¿ç¹å»â忢âæé®åæ¢å° requestAnimationFrame()ã
è¯ç两个é½è¿è¡ä¸ä¸ï¼æ¯è¾ä¸¤è
ç FPS å¼ï¼ç¬¬ä¸ä¸ªç´«è²æ¡ï¼ï¼å¯ä»¥çå° CSS å¨ç»å requestAnimationFrame() æ¯é常æ¥è¿çã
è±ç¦»ä¸»çº¿ç¨çå¨ç»
å³ä½¿æ¯ä¸é¢ç»åºçæµè¯ç»æï¼æä»¬ä»ç¶è®¤ä¸º CSS å¨ç»æ¯æ´å¥½çéæ©ã为ä»ä¹ï¼å ³é®æ¯åªè¦å¨ç»æ¶åç屿§ä¸å¼èµ· reflowï¼éæ°å¸å±ï¼ï¼åè CSS trigger è·å¾æ´å¤ä¿¡æ¯ï¼ï¼æä»¬å¯ä»¥æéæ ·æä½ç§»åºä¸»çº¿ç¨ãæå¸¸è§ç屿§æ¯ CSS transformã妿ä¸ä¸ªå ç´ è¢«æå为ä¸ä¸ª layerï¼transform 屿§å¨ç»å°±å¯ä»¥å¨ GPU ä¸è¿è¡ãè¿æå³çæ´å¥½å°æ§è½ï¼ç¹å«æ¯å¨ç§»å¨è®¾å¤ä¸ãå¨ OffMainThreadCompositing ä¸å¯»æ¾æ´å¤ç»èã
è¦å¨ç«ç䏿¿æ´» OMTAï¼è±ç¦»ä¸»çº¿ç¨çå¨ç»ï¼ ï¼ä½ éè¦åå¾ about:config ç¶åæç´¢ layers.offmainthreadcomposition.async-animationsï¼å°å
¶åæ¢å° trueã

æ¿æ´»ä¹åï¼å次è¿è¡ä¸é¢çä¾åãå¯ä»¥çå° CSS å¨ç»ç帧éçç°å¨é«å¤äºã
夿³¨ï¼å¨ Nightly å Developer çæ¬ï¼ä½ å¯è½çå° OMTA æ¯é»è®¤æ¿æ´»çï¼æä»¥ä½ éè¦åè¿æ¥æµè¯ï¼å æµè¯æ¿æ´» OMTA çæ åµï¼ç¶åæ¯æ²¡ææ¿æ´»çæ åµï¼ã
æ»ç»
æµè§å¨å¯ä»¥ä¼åæ¸²ææµç¨ãæ»ä¹ï¼æä»¬æ»æ¯å¯ä»¥å°½å¯è½éè¿ CSS transition å animation å建å¨ç»ãå¦æä½ çå¨ç»ççå¾å¤æï¼ä½ å¯è½ä¸å¾ä¸ä¾èµäº JavaScript å¨ç»ã