ข้ามไปยังเนื้อหา
Tayakorn
HyperFrames Handbook

HyperFrames Handbook

LEVEL 1 · พื้นฐานที่ใช้ทุกวัน

Timeline เดียวที่หยุดนิ่ง

บทที่ 2 ทิ้งไว้ว่า timeline ต้อง "สร้างจบแล้วหยุดนิ่งรอ" บทนี้ลงมือจริงด้วย GSAP และไขว่าทำไมท่าเขียนบางท่าปลอดภัยกว่าอีกท่าทั้งที่ผลลัพธ์บนตาเหมือนกัน

5.1 สัญญาสามข้อ

ทุก composition ลงทะเบียน timeline เดียว แบบนี้ (โค้ดจริงจากการ์ด):

window.__timelines = window.__timelines || {};
const tl = gsap.timeline({ paused: true });

tl.fromTo(
  "#headline-text",
  { autoAlpha: 0, y: 40 },
  { autoAlpha: 1, y: 0, duration: 0.8, ease: "power3.out" },
  0.6
);

window.__timelines["main"] = tl;

สัญญา: (ก) paused: true — คนหมุนเวลาคือเครื่องเรนเดอร์ (ข) สร้างแบบ synchronous ตอนโหลดเพจ ห้ามไปอยู่ใน setTimeout / Promise / event handler เพราะเครื่องอาจถามเฟรมก่อนโค้ดพวกนั้นวิ่ง (ค) กุญแจ "main" ต้องตรงกับ data-composition-id ของ root เป๊ะ — มันคือช่องทางเดียวที่เครื่องใช้ตามหา timeline

ตัวเลขท้ายบรรทัด (0.6) คือตำแหน่งบนแกนเวลาของเรื่อง — คุณกำลังวางแผนอนาคต ไม่ใช่สั่งเล่น

data-composition-id="main"window.__timelines["main"]ต้องตรงกันเครื่องเรนเดอร์seek(4.0)0s10sสร้าง synchronous ตอนโหลด · paused: true · ไม่มีใครเรียก play() — มีแต่ถูก seek
FIG 5.1 timeline สร้างจบตั้งแต่เพจโหลดแล้วหยุดนิ่ง (paused: true) — เข็มไม่เดินเอง เครื่องเรนเดอร์เป็นคนหมุนไปยัง t ที่ต้องการ · กุญแจ window.__timelines ต้องตรงกับ data-composition-id เป๊ะ เพราะเป็นช่องทางเดียวที่เครื่องใช้ตามหา timeline

5.2 ทำไมต้อง fromTo — กลไกที่มองไม่เห็นด้วยตา

gsap.to() บอกแค่ปลายทาง แล้วให้ GSAP "จำสภาพตอนเริ่ม" เอาเอง ปัญหาคือมันจำตอนที่ถูกถามถึงครั้งแรก — และเครื่องเรนเดอร์ถามเฟรมไม่เรียงลำดับ (หลาย worker แบ่งกันถาม — บทที่ 11) สภาพตอนเริ่มที่ถูกจำจึงขึ้นกับลำดับการถาม ผิดกฎ f(t) ทันที

gsap.fromTo() ประกาศทั้งสองปลายเอง ไม่ต้องจำอะไร ถามลำดับไหนก็ได้คำตอบเดิม ผมทดลองเพิ่ม CSS transform: translateY(40px) ให้ element เดียวกับที่มี fromTo แล้วถ่ายเฟรมก่อนคิวเข้า: element หายไปเลย — fromTo ยึดสภาพเริ่มของมัน (ซ่อนไว้) ตั้งแต่เพจโหลด ค่า CSS ไม่มีผลอะไร นี่คือเหตุผลที่กติกาบอกให้ตั้งค่าเริ่มใน tween ไม่ใช่ใน CSS: ให้ความจริงมีที่อยู่ที่เดียว

5.3 ลืมลงทะเบียนแล้วใครจับ — ทดลองจริง

ผมคอมเมนต์บรรทัด window.__timelines["main"] = tl; ทิ้ง: lint เงียบสนิท แต่ check ล้มด้วยสามข้อจาก motion sidecar — ไฟล์ index.motion.json ที่ผมประกาศเจตนาไว้ข้าง composition:

✗ motion_appears_late: never reaches visible opacity but should be visible by 1.6s
  Fix: The renderer seeks a paused timeline; a forward-only reveal can be skipped.
✗ motion_out_of_order: #headline-text should appear before #outro-card-line ...
✗ motion_frozen: nothing moves within #root between 2.5s and 6.5s (4s static)

ไม่มี timeline = ไม่มีอะไรขยับ และเครื่องรู้เพราะผมบอกไว้ก่อนว่าอะไรควรขยับเมื่อไหร่ sidecar หน้าตาแบบนี้:

{
  "assertions": [
    { "kind": "appearsBy", "selector": "#headline-text", "bySec": 1.6 },
    { "kind": "before", "a": "#headline-text", "b": "#outro-card-line" },
    { "kind": "keepsMoving", "withinSelector": "#root" }
  ]
}

ประกาศเจตนาให้เครื่องอ่านได้ แล้วเครื่องจะเฝ้าแทนคุณ — นี่คือหัวใจของการทำวิดีโอแบบวิศวกร

อ่านแบบเต็มเล่ม