คลังเทคนิคแอนิเมชันระดับ Awwwards ที่เราถอดออกมาเป็นสูตรพร้อมใช้ — แต่ละอันมี demo เล่นจริง, โค้ดกด copy ได้, สูตรของเรา และโน้ต accessibility
Scroll Tracking & Smooth Scrolling
ติดตามตำแหน่ง/ทิศ/ความเร็วการ scroll แล้วผูกกับแอนิเมชัน. หัวใจคือ Lenis ทำ scroll ให้ลื่นเหมือนเนย + GSAP ScrollTrigger ยิงแอนิเมชันตอน element เข้า viewport และทำ parallax.
// 1) Lenis smooth scroll + sync กับ GSAP ticker const lenis = new Lenis({ lerp: 0.1, smoothWheel: true }); gsap.ticker.add((t)=> lenis.raf(t * 1000)); lenis.on('scroll', ScrollTrigger.update); // 2) parallax layers ตามความเร็วต่างกัน gsap.utils.toArray('.layer').forEach((el)=>{ gsap.to(el, { yPercent: -30 * el.dataset.sp, ease: 'none', scrollTrigger: { trigger: el.parentElement, scrub: true } }); });
สูตรของเรา
- lerp 0.08–0.12 = นุ่มกำลังดี เกินนั้นหน่วง
- scrub:true ผูก timeline เข้ากับ scrollbar 1:1
- เลข data-speed เล็ก = ชั้นหลัง, ใหญ่ = ชั้นหน้า
Accessibility
- ปิด Lenis ถ้า prefers-reduced-motion
- อย่า hijack scroll จนปุ่ม keyboard เลื่อนไม่ได้
Text-Splitting Animation
แตกข้อความเป็นตัวอักษร/คำ/บรรทัด แล้วสั่งแต่ละชิ้นทำงานแยกกัน เกิดเป็น cascading reveal. ใช้ SplitType (free, ใช้กับ lib ไหนก็ได้) แทน SplitText ของ GSAP.
// แตกเป็นตัวอักษร แล้ว stagger ขึ้นทีละตัว const split = new SplitType('#headline', { types: 'chars' }); gsap.from(split.chars, { yPercent: 110, opacity: 0, duration: 0.8, ease: 'power4.out', stagger: 0.03 });
สูตรของเรา
- ครอบ overflow:hidden ที่ parent → ตัวอักษรโผล่จากใต้เส้น
- stagger 0.02–0.04 สำหรับ char, 0.08 สำหรับ word
- ผูกกับ ScrollTrigger เพื่อ reveal ตอนเลื่อนถึง
Accessibility
- SplitType เก็บข้อความเดิมให้ screen reader อ่านได้
- reduced-motion → แสดงข้อความเต็มทันที ไม่ stagger
Micro-animations
แอนิเมชันเล็กๆ ให้ feedback ตอนผู้ใช้ทำอะไร — hover, click, ripple. ทำให้ UI รู้สึกมีชีวิตและเข้าใจง่าย. Article แนะนำ Rive/Lottie สำหรับงานซับซ้อน; เราทำ pattern พื้นฐานด้วย GSAP ก่อน.
// hover: ยืดเล็กน้อย / click: ripple จากจุดที่กด btn.addEventListener('pointerenter',()=> gsap.to(btn,{scale:1.06,duration:.3,ease:'back.out(3)'})); btn.addEventListener('pointerleave',()=> gsap.to(btn,{scale:1,duration:.3})); btn.addEventListener('pointerdown',(e)=>{ const r=btn.getBoundingClientRect(), d=document.createElement('span'); d.className='ripple'; btn.appendChild(d); const size=Math.max(r.width,r.height); gsap.set(d,{width:size,height:size,left:e.clientX-r.left-size/2,top:e.clientY-r.top-size/2}); gsap.to(d,{scale:2.4,opacity:0,duration:.6,onComplete:()=>d.remove()}); });
สูตรของเรา
- back.out / elastic ให้ความรู้สึก "ตอบสนอง"
- งานหนัก (โลโก้ขยับ, mascot) → Lottie/Rive ดีกว่าโค้ดมือ
- สั้น 0.2–0.4s เท่านั้น ไม่งั้นกวนใจ
Accessibility
- state hover ต้องมี state :focus-visible คู่เสมอ
- อย่าใช้สีอย่างเดียวบอก state — เพิ่ม shape/scale
Transitions & Reveals
วิธีพาผู้ใช้ข้ามหน้า/section/state อย่างลื่น — fade, slide, zoom, FLIP. ใน vanilla ใช้ GSAP (+ Barba.js สำหรับ page transition); ใน React ใช้ Framer Motion. เราโชว์ scroll-reveal + FLIP card.
// vanilla: reveal on scroll gsap.from('.ln', { y: 40, opacity: 0, duration: .7, stagger: .12, ease: 'power3.out', scrollTrigger: { trigger: '.reveal-row', start: 'top 80%' } }); // React (Framer Motion) page transition // <motion.div initial={{opacity:0,y:20}} animate={{opacity:1,y:0}} // exit={{opacity:0}} transition={{duration:.5,ease:[.22,1,.36,1]}} />
สูตรของเรา
- page transition: Barba.js (vanilla) / AnimatePresence (React)
- GSAP Flip = morph layout ระหว่าง state ลื่นมาก
- cubic-bezier(.22,1,.36,1) = "expo out" เนียน
Accessibility
- หลัง route เปลี่ยน ย้าย focus ไป heading ใหม่
- reduced-motion → crossfade สั้นๆ แทน slide ใหญ่
Easing
หัวใจของ "feel". ไม่มี easing = หุ่นยนต์เคลื่อนคงที่. เราเทียบ ease ยอดนิยมแบบเล่นจริง เพื่อเลือกอารมณ์ให้ถูกงาน.
// GSAP — เปลี่ยนอารมณ์แค่เปลี่ยน ease string gsap.to(dot, { x: '100%', duration: 1.4, ease: 'back.out(1.7)' }); // CSS — สำหรับ transition เบาๆ // transition: transform .6s cubic-bezier(.22,1,.36,1);
สูตรของเรา
- UI ปกติ → power2/power3.out (เข้าเร็ว ออกนุ่ม)
- ปุ่ม/ของเล่น → back.out ให้เด้งน่ารัก
- elastic/bounce ใช้น้อยๆ เป็นลูกเล่น
Accessibility
- หลีกเลี่ยง bounce/elastic แรงๆ กับคนที่ไวต่อ motion
- duration สั้นลงเมื่อ reduced-motion
SVG & Mask Animations
ใช้ mask/clip-path เผยหรือซ่อน element แบบลื่น — reveal รูป/วิดีโอ, write-on text, shape morph. Scalable + เบา. เราโชว์ mask reveal ตามเมาส์/scroll.
// ขยายรัศมี clipPath เพื่อเผยเนื้อหา gsap.fromTo('#cpC', { attr:{ r:0 } }, { attr:{ r:360 }, duration:1.4, ease:'power3.inOut' }); // CSS mask reveal (วิธีที่ Lightship ใช้กับวิดีโอ) // mask-image: radial-gradient(circle, #000 var(--r), transparent var(--r));
สูตรของเรา
- animate attr {r} ของ circle ใน clipPath = reveal วงกลม
- ผูก --r กับ scroll → mask เปิดตามการเลื่อน
- มี mask reveal + วิดีโอ = effect "wow" ราคาถูก
Accessibility
- เนื้อหาสำคัญอย่าซ่อนหลัง mask อย่างเดียว
- วิดีโอใต้ mask ใส่ poster + ปุ่มหยุด
3D Animation
วัตถุ 3D เพิ่มมิติและ interactivity — โชว์สินค้าหมุนได้, scroll แล้วพลิก. เครื่องมือ no-code: Spline; asset หนัก: Blender → Three.js. เราโชว์วัตถุ 3D ที่หมุนตาม scroll/เมาส์.
// Three.js: scene + mesh + หมุนตาม scroll const scene=new THREE.Scene(), cam=new THREE.PerspectiveCamera(50,w/h,.1,100); const r=new THREE.WebGLRenderer({antialias:true,alpha:true}); const mesh=new THREE.Mesh( new THREE.IcosahedronGeometry(1.3,0), new THREE.MeshStandardMaterial({color:0x7b5cff,flatShading:true})); scene.add(mesh, new THREE.DirectionalLight(0xffffff,1.4)); (function loop(){ mesh.rotation.y+=.005; r.render(scene,cam); requestAnimationFrame(loop); })();
สูตรของเรา
- Spline สำหรับ deadline เร็ว/ไม่อยากเขียน shader
- โชว์สินค้า → flatShading + 2-3 ไฟ ดูพรีเมียม
- ผูก rotation กับ ScrollTrigger scrub = สินค้าพลิกตาม scroll
Accessibility
- ใส่ fallback ภาพนิ่งถ้า WebGL ไม่รองรับ
- หยุด render loop เมื่อ tab ไม่โฟกัส (ประหยัดแบต)
WebGL & Three.js
เลเวลสูงสุด — shader-based effect, particle, distortion ตามเมาส์ (เหมือน Hatom). GPU-accelerated. เราโชว์ particle field ที่ตอบสนองเมาส์.
// particle field — Points + ขยับ parallax ตามเมาส์ const geo=new THREE.BufferGeometry(), N=1200, pos=new Float32Array(N*3); for(let i=0;i<N*3;i++) pos[i]=(Math.random()-.5)*12; geo.setAttribute('position',new THREE.BufferAttribute(pos,3)); const pts=new THREE.Points(geo, new THREE.PointsMaterial({size:.04,color:0xe8ff3a})); // loop: pts.rotation.y += .001; cam.position.x += (mouseX-cam.position.x)*.05;
สูตรของเรา
- Unicorn Studio = prototype shader โดยไม่เขียน GLSL
- distortion เมาส์ = displacement shader บน plane + texture
- คุม particle count ตาม devicePixelRatio กันเครื่องอืด
Accessibility
- WebGL กิน GPU — ปิด/ลดเมื่อ reduced-motion หรือ mobile
- อย่าวางข้อความสำคัญทับ effect ที่อ่านยาก