1 คะแนน โดย GN⁺ 2024-03-09 | 1 ความคิดเห็น | แชร์ทาง WhatsApp

ทริกแอนิเมชันด้วย Exponential Smoothing

  • มีเทคนิคแอนิเมชันง่าย ๆ อย่างหนึ่งที่ฉันใช้แทบตลอดตั้งแต่เริ่มทำงานที่เกี่ยวข้องกับแอนิเมชัน
  • เทคนิคนี้ถูกนำไปใช้ได้หลากหลาย ไม่ว่าจะเป็นการหมุนและเลื่อนกล้อง การขยับตัวละครในเกมแบบเทิร์นเบส การเคลื่อนย้ายองค์ประกอบ UI หรือการทำให้การเปลี่ยนระดับเสียงในไลบรารีเสียงนุ่มนวลขึ้น
  • เทคนิคนี้ไม่ใช่ของใหม่ และคุณอาจเคยได้ยินหรือเคยใช้มาก่อนแล้ว แต่ฉันจะอธิบายผ่านตัวอย่างบางส่วนและหลักการทางคณิตศาสตร์ของมัน

ปุ่มสลับ

  • สมมติว่ากำลังสร้างองค์ประกอบ UI เช่น ปุ่มสลับ
  • ตำแหน่งของสวิตช์ในปุ่มสลับจะคำนวณจากสถานะ โดยถ้าเปิดจะตั้งเป็น max_x และถ้าปิดจะตั้งเป็น min_x
  • วิธีนี้ใช้งานได้ดี แต่ถ้าไม่มีแอนิเมชันก็ดูขาดชีวิตชีวาไปหน่อย
  • แอนิเมชันไม่ได้แค่เพิ่มความสวยงามทางสายตา แต่ยังช่วยให้ผู้ใช้เข้าใจด้วยว่ากำลังเกิดอะไรขึ้น
  • แทนที่จะย้ายตัวบ่งชี้ของปุ่มสลับไปยังตำแหน่งใหม่ทันที ก็เปลี่ยนให้มันค่อย ๆ เคลื่อนไปอย่างนุ่มนวล
  • ตอนนี้จึงต้องอัปเดตแอนิเมชัน ซึ่งมีข้อเสียคือมันดูเหมือนเคลื่อนที่ด้วยความเร็วคงที่
  • จากนั้นสามารถเพิ่ม easing function เข้าไปได้ เช่น 3t^2-2t^3 หรือ sqrt(t)
  • ความแตกต่างของ easing function เหล่านี้จะสังเกตเห็นได้ชัดขึ้นเมื่อเล่นแอนิเมชันช้าลง
  • ตอนนี้แทนที่จะอัปเดตตำแหน่งของสวิตช์ ก็ต้องติดตามสถานะของแอนิเมชันแทน
  • หากใช้ sqrt ก็ต้องระบุ easing function คนละตัวอย่างชัดเจนตามทิศทางของแอนิเมชัน
  • อะไรจะดูดีที่สุดนั้นขึ้นอยู่กับรสนิยม แต่ sqrt ดูดีที่สุด เพราะสวิตช์เริ่มต้นได้เร็วมาก แต่จะชะลอลงอย่างเหมาะเจาะเมื่อเข้าใกล้ปลายทาง
  • ข้อเสียของเวอร์ชันนี้คือ แม้แต่ในกรณีที่ง่ายที่สุดก็ยังต้องจัดการหลายอย่างพอสมควร และถ้าผู้ใช้คลิกกลางคันระหว่างแอนิเมชัน ก็จะเกิดความไม่ต่อเนื่องที่ดูเหมือนมันกระโดดทันที

การเลื่อนกล้อง

  • สมมติว่ามีสถานการณ์ที่แผนที่และกล้องต้องเลื่อนหรือเคลื่อนที่ไปรอบ ๆ
  • การเพิ่มแอนิเมชันตรงนี้ก็เป็นความคิดที่ดีเช่นกัน
  • มีการยกตัวอย่างโค้ดที่ทำ interpolation ด้วยความเร็วคงที่
  • อาการสั่นที่เกิดหลังแอนิเมชันเสร็จ มาจากการที่ target.x - position.x สลับไปมาระหว่างค่าบวกและค่าลบ
  • สิ่งที่ต้องใช้คือฟังก์ชันสำหรับ clamp ค่า delta แทน sign(delta)
  • วิธีนี้ซับซ้อนพอสมควรสำหรับเรื่องที่ควรจะง่าย
  • มันดูแปลกเมื่อความเร็วแอนิเมชันมากกว่าระยะจนทำให้ไปถึงก่อนแอนิเมชันควรจบ
  • จะเลือกเพิกเฉยต่ออินพุตของผู้ใช้ขณะแอนิเมชันทำงานอยู่ก็ได้ แต่สิ่งนี้สร้างประสบการณ์ที่น่าหงุดหงิดมากให้ผู้ใช้
  • แนวทางที่สมบูรณ์แบบก็คือ Exponential Smoothing นั่นเอง
  • เมื่อเทียบกับตัวอย่างปุ่มสลับแล้ว โค้ดแทบไม่ต่างกันเลย

กลไกภายใน

  • อธิบายว่า 1 - exp(- speed * dt) คืออะไร และมันทำงานอย่างไร
  • เริ่มจากเวอร์ชันง่าย ๆ ก่อน โดยทำให้ความเร็วในการเคลื่อนที่เป็นสัดส่วนกับระยะห่างระหว่าง position ปัจจุบันกับตำแหน่งใหม่ target ที่ต้องไปให้ถึง
  • วิธีนี้ไม่ต้องเก็บสถานะใด ๆ เพิ่มเติมนอกจากตำแหน่งปัจจุบันและตำแหน่งเป้าหมาย และยังปรับตัวอัตโนมัติได้แม้ target จะเปลี่ยนกะทันหัน
  • แต่มีปัญหาเล็กน้อยอยู่ ถ้า speed * dt มากกว่า 1 ตำแหน่งจะเคลื่อนเลยเป้าหมาย
  • เพื่อแก้ปัญหานี้ สามารถ clamp ค่าไว้ที่ 1 ได้
  • สาเหตุที่ speed * dt ใหญ่เกินไป อาจเป็นเพราะค่า speed สูงเกินไปหรือ dt ใหญ่เกินไป
  • สำหรับแอนิเมชัน คงจะดีถ้าทุกอย่างทำงานได้สมบูรณ์เมื่อใส่ dt เข้าไป

สมการเชิงอนุพันธ์ (โอ้ ไม่นะ)

  • มีการนำเสนอแนวทางสองขั้นตอนเพื่อแก้ปัญหานี้
  • ที่ position += (target - position) * speed * dt ใช้ได้กับ dt ที่เล็ก แต่ล้มเหลวกับ dt ที่ใหญ่ เป็นปัญหาคลาสสิกของวิธีเชิงตัวเลขในการแก้สมการเชิงอนุพันธ์
  • จากนั้นดูว่าสมการนี้กำลังแก้อะไรอยู่
  • และอธิบายว่า position += (target - position) * (1 - exp(- speed * dt)) คือสูตรที่ถูกต้องสำหรับทุกค่า dt

การเลือกความเร็ว

  • โดยทั่วไปเรามักคิดถึงแอนิเมชันในแง่ของระยะเวลา
  • แต่เมื่อใช้สูตรแบบเอ็กซ์โปเนนเชียล แอนิเมชันจะใช้เวลาจบอย่างเป็นอนันต์ในเชิงเทคนิค
  • ความหมายของพารามิเตอร์ speed คือ 1 / speed จะเป็นเวลาที่ position เข้าใกล้ target มากขึ้นอีก e = 2.71828... เท่า

Exponential Smoothing

  • ถ้าค้นหาคำว่า "Exponential Smoothing" คุณอาจเจอบทความวิกิที่ดูเหมือนไม่เกี่ยวกันเลย แต่จริง ๆ แล้วมันมีสูตรที่คล้ายกับสิ่งที่โพสต์นี้พูดถึงมาก
  • หากสมมติว่า dt คงที่เสมอ และ target เปลี่ยนทุกครั้งในแต่ละรอบ ก็สามารถจัดทำดัชนีค่าตามลำดับรอบ และคำนวณบางอย่างอย่าง position[i] = (target[i] - position[i - 1]) * factor

ชื่อย่อหน้าสุดท้าย

  • ฉันมีไอเดียสำหรับโพสต์นี้มาหลายเดือนแล้ว และก็ดีใจที่ในที่สุดทำมันเสร็จ
  • ขอบคุณที่รับชมและอ่าน devlog

ความเห็นของ GN⁺

  • บทความนี้อธิบายเทคนิค Exponential Smoothing ที่ใช้ทำให้แอนิเมชันลื่นไหลและเป็นธรรมชาติมากขึ้น เทคนิคนี้ช่วยยกระดับประสบการณ์ผู้ใช้และทำให้อินเทอร์เฟซใช้งานได้อย่างเป็นธรรมชาติมากขึ้น
  • Exponential Smoothing ยังมีประโยชน์ในการจำลองการเคลื่อนไหวทางกายภาพ เช่น ในการพัฒนาเกมที่ต้องการให้การเคลื่อนที่ของตัวละครหรือการเคลื่อนกล้องดูเป็นธรรมชาติมากขึ้น
  • เทคนิคนี้มีประสิทธิภาพมากเป็นพิเศษเมื่อต้องแสดงผลการเปลี่ยนสถานะขององค์ประกอบในส่วนติดต่อผู้ใช้ในเชิงภาพ ตัวอย่างเช่น สามารถทำให้การเคลื่อนไหวของสไลเดอร์หรือสวิตช์ดูนุ่มนวลขึ้นได้
  • หากมองในเชิงวิพากษ์ Exponential Smoothing อาจควบคุมความเร็วและระยะเวลาของแอนิเมชันได้ยากอย่างแม่นยำ ซึ่งอาจเป็นข้อจำกัดเมื่อดีไซเนอร์ต้องการปรับแต่งเอฟเฟกต์แอนิเมชันแบบละเอียดเฉพาะจุด
  • ไลบรารีหรือเฟรมเวิร์กแอนิเมชันอื่นที่ให้ความสามารถคล้ายกัน เช่น GreenSock Animation Platform(GSAP) หรือ anime.js ซึ่งให้การควบคุมแอนิเมชันที่ละเอียดกว่า พร้อม easing function ที่หลากหลาย
  • เมื่อนำเทคนิค Exponential Smoothing มาใช้ จำเป็นต้องหาสมดุลระหว่างความเป็นธรรมชาติของแอนิเมชันกับความแม่นยำในการควบคุม ข้อดีของการเลือกใช้เทคนิคนี้คือประสบการณ์ผู้ใช้ที่ดีขึ้น ส่วนข้อเสียคืออาจปรับจังหวะเวลาแอนิเมชันแบบละเอียดได้ยาก

1 ความคิดเห็น

 
GN⁺ 2024-03-09
ความคิดเห็นบน Hacker News
  • สรุปความคิดเห็นแรก:

    • เน้นว่าวิธีนี้ไม่ใช่เพียงเส้นโค้ง easing หรือฟังก์ชัน smoothstep() แต่เป็นวิธีแบบไร้สถานะ (stateless) ที่จัดการอินพุตหลากหลายแบบได้อย่างสม่ำเสมอ
    • หากเคยใช้ CSS transition มาก่อน ก็จะเข้าใจปัญหาที่เทคนิคนี้เข้ามาช่วยแก้ได้
    • exponential smoothing มีปัญหาที่จะเข้าใกล้จุดหมาย แต่ไม่มีวันไปถึงจริง ๆ
    • กล่าวว่าตอนใช้วิธีคล้ายกันกับ inertial scrolling การเพิ่มพจน์แรงเสียดทาน (ปลอม) ก็มีประโยชน์
  • สรุปความคิดเห็นที่สอง:

    • ในฐานะนักพัฒนาเกม ผู้แสดงความคิดเห็นชอบใช้ eased tweens ที่กำหนดระยะเวลาไว้ล่วงหน้าสำหรับสถานการณ์ UI ส่วนใหญ่
    • แต่เทคนิคนี้มีประโยชน์มากเมื่อใช้ทำให้การเคลื่อนไหวที่ต่อเนื่องและคาดเดาไม่ได้ ซึ่งไม่มีจุดเริ่มต้น/สิ้นสุดที่ชัดเจน ดูนุ่มนวลขึ้น
    • exponential lerp มีประโยชน์แต่ยังไม่เป็นที่รู้จักแพร่หลาย และบางเกมก็ใช้ linear interpolation ที่แม่นยำน้อยกว่า จนเกิดปัญหาด้านแอนิเมชัน
    • จึงกล่าวขอบคุณสำหรับบทความ เพราะความรู้เฉพาะทางแบบนี้มักเข้าถึงได้ยาก
  • สรุปความคิดเห็นที่สาม:

    • ไม่เห็นด้วยกับที่ผู้เขียนบอกว่า sqrt เหมาะกับสวิตช์ toggle มากกว่า cubic
    • มองว่าเมื่อพิจารณาวิธีการทำงานของสวิตช์ toggle จริง ๆ แล้ว ฟังก์ชัน cubic เหมาะสมกว่า
    • ประเมินว่าบทความนี้แสดงให้เห็นได้ดีว่าแอนิเมชันช่วยยกระดับประสบการณ์ผู้ใช้ได้อย่างไร
  • สรุปความคิดเห็นที่สี่:

    • ชื่นชมลูกเล่นไม่เชิงเส้นง่าย ๆ ที่เพิ่มความสนุกให้กับปฏิสัมพันธ์ออนไลน์
    • กล่าวว่าสิ่งนี้มีบทบาทสำคัญต่อการรับรู้สี และผู้คนไม่ได้เข้าใจเรื่องความเร่งได้เสมอไป
  • สรุปความคิดเห็นที่ห้า:

    • ชอบบทความนี้ และบอกว่าเคยเขียนเทคนิคเดียวกันนี้ไว้เมื่อเกือบ 10 ปีก่อนภายใต้ชื่อ 'lazy-easy'
    • บอกว่ายังใช้เทคนิคนี้อยู่เมื่ออยากได้แอนิเมชันที่ลื่นไหลโดยไม่ต้องจัดการสถานะ
  • สรุปความคิดเห็นที่หก:

    • กล่าวว่าบทความเกี่ยวกับ easing ดูเหมือนเป็นสิ่งที่แต่ละรุ่นต้องกลับมาค้นพบใหม่เสมอ
    • ย้อนความว่าช่วงปลายยุค 90 เว็บไซต์เชิงทดลองของ Yugo Nakamura เป็นหนึ่งในตัวอย่างแรก ๆ ที่ใช้ easing อย่างอิสระจนเกิดความรู้สึกแบบออร์แกนิก
  • สรุปความคิดเห็นที่เจ็ด:

    • เสนอไอเดียเกี่ยวกับ toggle ที่จะเคลื่อนช้า ๆ ระหว่างค้างการแตะ/คลิกไว้ และเมื่อปล่อยแล้วส่วนที่เหลือจะ snap เร็ว
    • ยังไม่แน่ใจว่าสิ่งนี้มีความหมายอย่างไรในเชิง UX แต่คิดว่าอาจใช้บอกจังหวะที่การตั้งค่าถูกนำไปใช้หรือถูกบันทึกแล้ว
  • สรุปความคิดเห็นที่แปด:

    • มองว่าเทคนิคนี้มีประโยชน์ไม่ใช่แค่กับสวิตช์ แต่ใช้ได้กับงานอีกหลายแบบ
    • กล่าวถึงเดโมที่เกี่ยวข้องกับ Flickity และชี้ว่าเทคนิคนี้ยังไม่ได้รวมการปรับแต่งหลายอย่างที่จำเป็นสำหรับความพร้อมใช้งานจริงในงาน production
    • ตำหนิว่าหลายคนในคอมเมนต์ดูเหมือนจะไม่ได้อ่านบทความให้ดี หรือพลาดประเด็นสำคัญไป
  • สรุปความคิดเห็นที่เก้า:

    • ให้ความเห็นเชิงบวกกับบทความ และบอกว่าเดโมทำงานได้ดีบน Chrome แต่ใน Firefox กลับมีปัญหาที่หน้าเว็บค้าง
  • สรุปความคิดเห็นที่สิบ:

    • มองว่าแอนิเมชันเล็ก ๆ เป็นรูปแบบสูงสุดของ emotional design ที่ถ่ายทอดเรื่องราวได้มากมาย