← บทความทั้งหมด

Image sequence scroll animation แบบ Apple ใน WordPress

หน้าสินค้าของ Apple มีลูกเล่นที่ใครเห็นก็อยากทำตาม คือพอเลื่อนหน้าจอ สินค้าจะหมุน เปิดออก หรือแยกชิ้นส่วนอย่างลื่นไหล และตรงกับระยะที่เลื่อนพอดี สิ่งนี้ไม่ใช่วิดีโอ แต่เป็นภาพนิ่งหลายภาพ หรือเฟรม ที่วาดต่อกันลงบน canvas โดยให้ตำแหน่งการเลื่อนเป็นตัวเลือกว่าจะแสดงเฟรมไหน บทความนี้อธิบายหลักการ วิธีทำเฟรม โค้ดสำหรับทำใน WordPress และสิ่งที่ต้องระวังไม่ให้ผู้เข้าชมต้องจ่ายแพงกว่าสิ่งที่ได้เห็น

หลักการทำงาน

มีสามส่วน

  1. เซกชันที่สูง ความสูงของมันคือระยะเลื่อน ยิ่งสูงแอนิเมชันยิ่งเล่นช้า ปกติจะสูงราวสามถึงสี่หน้าจอ
  2. canvas ที่อยู่กับที่ ข้างในเซกชันมี canvas ที่ตั้ง position: sticky จึงค้างอยู่บนหน้าจอขณะที่เซกชันเลื่อนผ่านไป
  3. ระยะที่เลื่อนเป็นตัวเลือกเฟรม ผู้เข้าชมเลื่อนผ่านเซกชันไปแล้วเท่าไร คิดเป็น 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 ตัวเลขที่ควรตั้งเป้าคือ

อัปโหลดเฟรมทั้งหมดเข้าคลังสื่อพร้อมกัน 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) ทำให้สคริปต์ไม่มีผลอะไรกับหน้าอื่นที่ไม่มีแอนิเมชัน

สิ่งที่เวอร์ชันขั้นต่ำยังไม่ได้ทำ

เวอร์ชันสำหรับเว็บจริงต้องมีอีกหลายอย่าง และทุกข้อเป็นเรื่องของคนที่ดูบนมือถือ

เวอร์ชันที่ไม่ต้องเขียนโค้ด

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 และโค้ดส่วนหัว/ส่วนท้ายของคุณแยกจากธีม ใส่แต่ละอย่างในที่ของมัน และเก็บทุกเวอร์ชันที่บันทึก

ดูว่า StyleScript ทำอะไรได้  ดูราคา