Image sequence scroll animation แบบ Apple ใน WordPress
หน้าสินค้าของ Apple มีลูกเล่นที่ใครเห็นก็อยากทำตาม คือพอเลื่อนหน้าจอ สินค้าจะหมุน เปิดออก หรือแยกชิ้นส่วนอย่างลื่นไหล และตรงกับระยะที่เลื่อนพอดี สิ่งนี้ไม่ใช่วิดีโอ แต่เป็นภาพนิ่งหลายภาพ หรือเฟรม ที่วาดต่อกันลงบน canvas โดยให้ตำแหน่งการเลื่อนเป็นตัวเลือกว่าจะแสดงเฟรมไหน บทความนี้อธิบายหลักการ วิธีทำเฟรม โค้ดสำหรับทำใน WordPress และสิ่งที่ต้องระวังไม่ให้ผู้เข้าชมต้องจ่ายแพงกว่าสิ่งที่ได้เห็น
หลักการทำงาน
มีสามส่วน
- เซกชันที่สูง ความสูงของมันคือระยะเลื่อน ยิ่งสูงแอนิเมชันยิ่งเล่นช้า ปกติจะสูงราวสามถึงสี่หน้าจอ
- canvas ที่อยู่กับที่ ข้างในเซกชันมี canvas ที่ตั้ง
position: stickyจึงค้างอยู่บนหน้าจอขณะที่เซกชันเลื่อนผ่านไป - ระยะที่เลื่อนเป็นตัวเลือกเฟรม ผู้เข้าชมเลื่อนผ่านเซกชันไปแล้วเท่าไร คิดเป็น 0 ถึง 1 คูณด้วยจำนวนเฟรม ก็คือเฟรมที่ต้องวาด
ทำไมใช้เฟรมแทนวิดีโอ วิดีโอก็เล่นตามการเลื่อนได้ แต่เวลาเบราว์เซอร์กระโดดไปยังจุดใดในวิดีโอ จะไปที่ keyframe ที่ใกล้ที่สุด วิดีโอทั่วไปจึงกระตุกหรือตามไม่ทันเมื่อถูกเลื่อนไปมา ส่วนเฟรมแม่นยำเสมอ เฟรมที่ 57 ก็คือเฟรมที่ 57 ทุกครั้ง ที่ต้องแลกคือน้ำหนัก เพราะทุกเฟรมเป็นรูปที่ต้องดาวน์โหลด เนื้อหาส่วนใหญ่ต่อจากนี้จึงเป็นเรื่องการทำให้น้ำหนักนั้นต่ำที่สุด
ทำเฟรม
เริ่มจากวิดีโอสั้น ๆ เช่น งานเรนเดอร์ 3D หรือสินค้าบนแท่นหมุน แล้วตัดเป็นรูปด้วย ffmpeg
mkdir frames
ffmpeg -i input.mp4 -vf "fps=30,scale=1920:-2" -c:v libwebp -quality 80 frames/hero_%04d.webp
คำสั่งนี้สร้างไฟล์ hero_0001.webp, hero_0002.webp ไปเรื่อย ๆ
คือ 30 เฟรมต่อวิดีโอหนึ่งวินาที กว้าง 1920 พิกเซล เป็น WebP ตัวเลขที่ควรตั้งเป้าคือ
- 60 ถึง 150 เฟรม น้อยกว่า 60 จะดูกระตุกเมื่อเลื่อนยาว ๆ
มากกว่า 150 แทบไม่ได้สวยขึ้นแต่หนักขึ้นเสมอ ถ้าอยากได้น้อยลงให้ลดค่า
fps - กว้างเท่าหน้าจอ ไม่ต้องมากกว่านั้น 1920 พิกเซลพอสำหรับเดสก์ท็อปส่วนใหญ่ มือถือต้องการน้อยกว่านั้นมาก
- WebP หรือ JPEG ที่บีบอัดดี ถ้าแต่ละเฟรม 60–150 KB เฟรม 120 ภาพจะหนัก 7–18 MB ซึ่งเยอะมากสำหรับมือถือ วิธีโหลดเฟรมจึงสำคัญพอ ๆ กับโค้ดที่วาด
อัปโหลดเฟรมทั้งหมดเข้าคลังสื่อพร้อมกัน WordPress จะเก็บชื่อไฟล์ไว้ตามเดิม
hero_0001.webp จึงไปอยู่ที่ที่อยู่อย่าง /wp-content/uploads/2026/10/hero_0001.webp
เปิดดูสักไฟล์ในคลังสื่อเพื่อดูที่อยู่ของคุณ
WordPress ยังสร้างขนาดเล็กลงของทุกเฟรมตามปกติด้วย ซึ่งกลายเป็นประโยชน์สำหรับมือถือ
โค้ด
นี่คือเวอร์ชันขั้นต่ำที่ใช้งานได้ทันที มาร์กอัปวางบนหน้า ในบล็อก HTML หรือวิดเจ็ต HTML ของ page builder ตั้งขนาด canvas ให้เท่ากับขนาดของเฟรม
<div class="frames">
<canvas class="frames-canvas" width="1920" height="1080"></canvas>
</div>
CSS ทำให้เซกชันสูง และให้ canvas ค้างอยู่บนหน้าจอ
.frames {
height: 300vh; /* ระยะเลื่อน */
background: #000;
}
.frames-canvas {
position: sticky;
top: 0;
display: block;
width: 100%;
height: 100vh;
height: 100svh;
object-fit: contain;
}
JavaScript วาดเฟรมที่ตรงกับการเลื่อน ตั้ง count เป็นจำนวนเฟรม และ src เป็นที่อยู่ของเฟรม
const section = document.querySelector('.frames');
if (section) {
const canvas = section.querySelector('canvas');
const context = canvas.getContext('2d');
const count = 120;
const src = (i) => `/wp-content/uploads/2026/10/hero_${String(i + 1).padStart(4, '0')}.webp`;
const frames = [];
let shown = -1;
const ready = (i) => frames[i] && frames[i].complete && frames[i].naturalWidth > 0;
const draw = () => {
const rect = section.getBoundingClientRect();
const distance = rect.height - window.innerHeight;
const progress = distance > 0 ? Math.min(1, Math.max(0, -rect.top / distance)) : 0;
const wanted = Math.round(progress * (count - 1));
// ระหว่างที่เฟรมที่ต้องการยังไม่มา วาดเฟรมใกล้ที่สุดที่มาแล้วแทน
for (let d = 0; d < count; d++) {
const i = [wanted - d, wanted + d].find(ready);
if (i !== undefined) {
if (i !== shown) {
context.drawImage(frames[i], 0, 0, canvas.width, canvas.height);
shown = i;
}
return;
}
}
};
// โหลดทุกเฟรมที่แปดก่อน แล้วค่อยเติมช่องว่าง ให้ครอบคลุมทั้งช่วงตั้งแต่เนิ่น ๆ
for (const step of [8, 4, 2, 1]) {
for (let i = 0; i < count; i += step) {
if (!frames[i]) {
frames[i] = new Image();
frames[i].onload = draw;
frames[i].src = src(i);
}
}
}
let queued = false;
window.addEventListener('scroll', () => {
if (!queued) {
queued = true;
requestAnimationFrame(() => {
queued = false;
draw();
});
}
}, { passive: true });
}
มีสองจุดในโค้ดที่ทำงานหนักที่สุด จุดแรก มันวาดใหม่เฉพาะเมื่อเฟรมเปลี่ยน การเลื่อนอยู่ในเฟรมเดิมจึงไม่เสียอะไรเลย จุดที่สอง มันโหลดทุกเฟรมที่แปดก่อน แล้วค่อยโหลดเฟรมที่อยู่ระหว่างกลาง พร้อมวาดเฟรมใกล้ที่สุดที่มาถึงแล้ว แอนิเมชันจึงใช้ได้ตั้งแต่วินาทีแรก หยาบนิดหน่อยตอนแรกแล้วลื่นในอีกครู่เดียว แทนที่จะต้องรอจนเฟรมที่ 120 มาถึง
ใน StyleScript ปลั๊กอินฟรีสำหรับ CSS และ JavaScript ของคุณเอง
CSS ใส่ในกล่อง CSS ส่วนสคริปต์ใส่ในกล่อง JavaScript โดยโหลดในส่วนท้าย
บรรทัด if (section) ทำให้สคริปต์ไม่มีผลอะไรกับหน้าอื่นที่ไม่มีแอนิเมชัน
สิ่งที่เวอร์ชันขั้นต่ำยังไม่ได้ทำ
เวอร์ชันสำหรับเว็บจริงต้องมีอีกหลายอย่าง และทุกข้อเป็นเรื่องของคนที่ดูบนมือถือ
- โหลดเมื่อจำเป็นเท่านั้น แบบที่เขียนไว้ ทุกเฟรมจะเริ่มดาวน์โหลดทุกครั้งที่มีคนเข้าหน้า
แม้เซกชันจะอยู่ล่างมากหรือไม่มีใครเลื่อนไปถึง
ควรเริ่มโหลดเมื่อเซกชันเข้าใกล้หน้าจอ ด้วย
IntersectionObserver - เฟรมน้อยลงและเล็กลงบนมือถือ มือถือไม่ต้องใช้เฟรมกว้าง 1920 พิกเซล และไม่ต้องใช้ทุกเฟรม ขนาดเล็กของแต่ละเฟรมที่ WordPress ทำไว้ กับการใช้ทุกเฟรมที่สองหรือสาม ดูเหมือนกันบนจอเล็ก แต่หนักน้อยกว่ามาก
- canvas ที่คมโดยไม่สิ้นเปลือง ตั้งขนาด canvas เท่าพื้นที่ที่แสดงคูณ
devicePixelRatioโดยไม่เกิน 2 คมกว่านั้นตามองไม่เห็น แต่ยังกินหน่วยความจำ - ลดการเคลื่อนไหวให้คนที่ขอ ผู้เข้าชมที่ตั้งเครื่องให้ลดการเคลื่อนไหว ควรเห็นภาพนิ่งภาพเดียว ไม่ใช่แอนิเมชันที่วิ่งตามการเลื่อน
- หน่วยความจำ เฟรมขนาดเต็มที่ถอดรหัสแล้วหลายร้อยภาพใส่ในหน่วยความจำของมือถือไม่พอ ให้เก็บเป็นรูปภาพแล้วให้เบราว์เซอร์จัดการเอง แทนการแปลงทุกอย่างเป็น bitmap ไว้ล่วงหน้า
เวอร์ชันที่ไม่ต้องเขียนโค้ด
StyleScript Pro ซึ่งเป็น add-on ของปลั๊กอินฟรี มีสิ่งนี้เป็นเอฟเฟกต์สำเร็จรูป
ชื่อ Image Sequence Animation ตามการเลื่อน ตั้งค่าผ่านฟอร์มที่ หน้าตา → StyleScript → เอฟเฟกต์
เลือกเฟรมทั้งหมดจากคลังสื่อพร้อมกันได้ในครั้งเดียว ซึ่งจะเล่นตามลำดับชื่อไฟล์
หรือใส่ที่อยู่ของไฟล์ที่มีเลขลำดับ โดยเขียน {0001} ตรงที่เป็นตัวเลข
วางได้ด้วย shortcode หรือบล็อก เอฟเฟกต์ StyleScript หรือใส่ในองค์ประกอบที่มีอยู่บนหน้าแล้ว
มันทำทุกข้อข้างบนให้ เฟรมเริ่มโหลดเมื่อเอฟเฟกต์เข้าใกล้หน้าจอ โหลดเป็นรอบ และวาดเฟรมใกล้ที่สุดไปก่อนจนกว่าแต่ละเฟรมจะมาถึง มือถือได้ทุกเฟรมที่สอง สาม หรือสี่ตามที่คุณเลือก ในขนาดเล็กที่ WordPress ทำไว้ canvas คมได้ไม่เกินสองเท่าของพิกเซลหน้าจอ และวาดใหม่เฉพาะเมื่อเฟรมเปลี่ยน ผู้เข้าชมที่ขอให้ลดการเคลื่อนไหวจะเห็นภาพนิ่ง และพื้นที่ถูกจองไว้ก่อนที่อะไรจะโหลด จึงไม่มีอะไรบนหน้าขยับ มันเตือนเมื่อเฟรมรวมกันหนักเกินกว่าที่เราแนะนำ ดูตัวอย่างบนหน้าไหนก็ได้ก่อนขึ้นเว็บจริง และถ้าวันหนึ่งอยากได้โค้ดมาดูแลเอง ปุ่ม แยกเป็นโค้ด จะส่งให้เป็นโค้ดที่อ่านออก
หรือใช้วิดีโอที่เลื่อนตามได้
ถ้าเฟรมจะหนักเกินไป วิดีโอไฟล์เดียวก็ทำงานเดียวกันได้ ถ้าเข้ารหัสมาสำหรับงานนี้ การกระโดดไปมาในวิดีโอจะลื่นก็ต่อเมื่อ keyframe อยู่ใกล้กัน วิดีโอจึงต้องมี keyframe ทุกไม่กี่เฟรม ไม่มีเสียง และมี index อยู่ต้นไฟล์
ffmpeg -i input.mp4 -an -vf "scale=-2:1080" -c:v libx264 -crf 23 -preset slow -g 5 -keyint_min 5 -sc_threshold 0 -pix_fmt yuv420p -movflags +faststart scrub.mp4
เอฟเฟกต์ Video Scrubbing ตามการเลื่อน ของ StyleScript Pro เล่นวิดีโอแบบนี้ตามการเลื่อนของผู้เข้าชม ทำให้การกระโดดไปมาลื่นขึ้น และแสดงภาพปกให้ผู้เข้าชมที่ขอให้ลดการเคลื่อนไหว
เก็บโค้ดที่ใส่เองไว้ในที่เดียว
StyleScript เก็บ CSS, JavaScript และโค้ดส่วนหัว/ส่วนท้ายของคุณแยกจากธีม ใส่แต่ละอย่างในที่ของมัน และเก็บทุกเวอร์ชันที่บันทึก