เบราว์เซอร์แปลงการเปลี่ยนแปลง CSS เป็นพิกเซลผ่าน pipeline และการเปลี่ยนแปลงที่แตกต่างกันจะเรียกใช้ระดับต่างๆ (มีค่ามากหรือน้อย):
Style → Layout (reflow) → Paint → Composite
เบราว์เซอร์แปลงการเปลี่ยนแปลง CSS เป็นพิกเซลผ่าน pipeline และการเปลี่ยนแปลงที่แตกต่างกันจะเรียกใช้ระดับต่างๆ (มีค่ามากหรือน้อย):
Style → Layout (reflow) → Paint → Composite
/* ❌ trigger LAYOUT (reflow) — costly, recalculates geometry every frame */
width, height, top, left, margin, padding, font-size
/* ⚠️ trigger PAINT — repaint pixels */
color, background, box-shadow, border-radius
/* ✅ trigger only COMPOSITE — GPU, no layout/paint */
transform, opacity
นี่คือกฎประสิทธิภาพที่สำคัญที่สุด: ทำให้ transform และ opacity เป็นภาพเคลื่อนไหว ไม่ใช่ width/top/margin. การเคลื่อนไหวคุณสมบัติ layout จะทำให้ layout ทำงานใหม่ในทุก frame (jank) transforms ผ่านเนื้อประสาณโดย GPU (smooth 60fps)
/* ❌ janky */ @keyframes a { to { left: 300px; width: 200px; } }
/* ✅ smooth */ @keyframes b { to { transform: translateX(300px) scaleX(2); } }
.animated { will-change: transform; } /* promotes to its own GPU layer ahead of time */
ใช้ will-change เฉพาะกับองค์ประกอบที่กำลังจะเป็นภาพเคลื่อนไหว — การใช้มากเกินไปจะสูญเสียหน่วยความจำโดยการสร้างชั้นมากเกินไป
// ❌ read-write-read-write forces multiple synchronous reflows
for (const el of els) { el.style.width = el.offsetWidth + 10 + "px"; }
// ✅ batch reads, then writes
const widths = els.map(el => el.offsetWidth);
els.forEach((el, i) => el.style.width = widths[i] + 10 + "px");
- contain: layout/paint — isolate a subtree so changes don't reflow the whole page
- content-visibility: auto — skip rendering offscreen content
- minimize deep selectors and huge unused stylesheets
ประสิทธิภาพการเรนเดอร์เป็นเรื่องเกี่ยวกับ การไม่เรียกใช้ layout ซ้ำๆ
การรู้ว่า transform/opacity เป็น composite-only (ถูก) ในขณะที่ width/top/margin บังคับให้ reflow (มีค่า) — บวกกับการหลีกเลี่ยง layout thrashing และการใช้ contain/content-visibility — นี่คือสิ่งที่ทำให้การเคลื่อนไหวอยู่ที่ 60fps และหน้าเว็บรู้สึกเร็ว