1 คะแนน โดย GN⁺ 2024-03-09 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • Exponential smoothing เป็นเทคนิคเรียบง่ายที่ใช้ได้กว้างกับแอนิเมชันที่ต้องเคลื่อนไปหาค่าเป้าหมายอย่างนุ่มนวล เช่น ปุ่มสลับ กล้อง องค์ประกอบ UI และระดับเสียงของออดิโอ
  • สมการหลักคือ position += (target - position) * (1 - exp(- speed * dt)) และตอบสนองต่อ การเปลี่ยนเป้าหมาย ได้อย่างเป็นธรรมชาติ โดยอาศัยเพียงตำแหน่งปัจจุบันกับตำแหน่งเป้าหมาย
  • การเคลื่อนที่เชิงเส้นและ easing แบบทั่วไปมักต้องจัดการเรื่องคลิกระหว่างทาง อินพุตกล้องที่รวดเร็ว ค่า dt ที่มาก ทำให้เกิดการกระโดด การสั่น หรือคิวอินพุต แต่ exponential smoothing ลดความซับซ้อนด้วยโครงสร้างเดียวกัน
  • 1 - exp(- speed * dt) มาจาก คำตอบของสมการเชิงอนุพันธ์ ที่สมการอัปเดตแบบสัดส่วนกำลังแก้อยู่ โดยเมื่อ dt เล็กจะเกือบเหมือนสูตรเดิม และเมื่อ dt ใหญ่ก็ยังหลีกเลี่ยง overshoot ได้
  • แม้จะไปไม่ถึงค่าเป้าหมายแบบแม่นตรงทางคณิตศาสตร์ แต่ด้วยข้อจำกัดของความแม่นยำแบบ floating point และขีดจำกัดการรับรู้การเปลี่ยนแปลงของผู้ใช้ ในแอนิเมชันจริงก็ถือว่าเสร็จสมบูรณ์ได้เพียงพอ

ปัญหาที่เห็นได้ชัดจากปุ่มสลับ

  • ตำแหน่งสวิตช์ของปุ่มสลับคำนวณได้อย่างง่ายดายด้วย turned_on ? max_x : min_x แต่เมื่อสถานะเปลี่ยน ตำแหน่งจะย้ายแบบฉับพลันทันที ทำให้ ขาดความมีชีวิตชีวา
  • แอนิเมชันแบบเชิงเส้นมักเขียนโดยอัปเดตตำแหน่งด้วยความเร็วคงที่แล้วจำกัดให้อยู่ในช่วง
position.x += (turned_on ? 1 : -1) * speed * dt;
position.x = clamp(position.x, min_x, max_x);
  • การเคลื่อนที่ด้วยความเร็วคงที่ทำให้ตำแหน่งเป็น ฟังก์ชันเชิงเส้น ของเวลา จึงอาจดูแข็งทื่อ
  • หากใส่ easing function เพิ่มเข้าไป ก็ทำให้การเคลื่อนไหวดูนุ่มนวลขึ้นได้
    • cubic smoothstep แบบคลาสสิก: 3t² - 2t³
    • easing แบบรากที่สอง: sqrt(t)
  • smoothstep มีสมมาตร 1 - f(t) = f(1 - t) จึงใช้โค้ดเดียวกันได้ทั้งแอนิเมชันไปข้างหน้าและย้อนกลับ
  • ส่วน sqrt ต้องใช้สูตรต่างกันตามทิศทาง
    • ตอนเปิด: sqrt(t)
    • ตอนปิด: 1 - sqrt(1 - t)
  • sqrt เริ่มต้นเร็วแล้วค่อย ๆ ช้าลงเมื่อเข้าใกล้เป้าหมาย แต่แม้จะเป็นเพียงปุ่มสลับ 2 สถานะ ก็ยังต้องมี การจัดการสถานะ อย่าง t ทิศทาง และการคำนวณ easing
  • หากผู้ใช้คลิกซ้ำระหว่างที่แอนิเมชันยังเล่นอยู่ วิธี easing แบบเดิมอาจทำให้เกิด ความไม่ต่อเนื่อง จากการที่ตำแหน่งกระโดดทันที

สูตรของ exponential smoothing

  • Exponential smoothing คือการกำหนดตำแหน่งเป้าหมายไว้ แล้วค่อย ๆ ดึงตำแหน่งปัจจุบันเข้าไปหาเป้าหมายทีละนิด
target = (state.value ? max_x : min_x);
position.x += (target - position.x) * (1 - exp(- dt * speed));
  • ปริมาณที่อัปเดตถูกกำหนดจาก target - position.x ซึ่งเป็นความต่างระหว่างตำแหน่งปัจจุบันกับเป้าหมาย
  • 1 - exp(- dt * speed) คือ ค่าสัมประสิทธิ์การอินเตอร์โพเลต ที่กำหนดว่าแต่ละเฟรมจะขยับเข้าใกล้เป้าหมายมากแค่ไหน
  • แม้เป้าหมายจะเปลี่ยนกลางทาง ก็สามารถเคลื่อนไปยังเป้าหมายใหม่ต่อได้ทันที โดยไม่ต้องเก็บเวลาแอนิเมชันหรือสถานะทิศทางแยกต่างหาก
  • ในตัวอย่างปุ่มสลับ มันให้ความรู้สึกเริ่มเร็วและช้าลงเมื่อใกล้เป้าหมายคล้าย easing แบบ sqrt แต่ลดปัญหาการกระโดดเมื่อมีการคลิกกลางทาง

ข้อดีที่เด่นขึ้นอีกในงานเคลื่อนกล้อง

  • ปัญหาเดียวกันนี้เกิดขึ้นกับกล้องที่เคลื่อนที่บนแผนที่เช่นกัน
  • หากทำอินเตอร์โพเลตด้วยความเร็วคงที่แบบง่าย ๆ ก็มักใช้เครื่องหมายของทิศทางเป้าหมายในแต่ละแกน
position.x += sign(target.x - position.x) * speed * dt;
position.y += sign(target.y - position.y) * speed * dt;
  • เมื่อถึงจุดสิ้นสุดของแอนิเมชัน เครื่องหมายของ target - position อาจสลับไปมาระหว่างบวกกับลบ ทำให้เกิด การสั่น
  • เพื่อป้องกันสิ่งนี้ จำเป็นต้องมีฟังก์ชันอัปเดตแยกที่จำกัด delta ให้อยู่ในช่วง max_delta
float update(float & value, float target, float max_delta)
{
    float delta = target - value;
    delta = min(delta,  max_delta);
    delta = max(delta, -max_delta);
    value += delta;
}
  • หากจะใช้ cubic easing กับการเคลื่อนกล้อง ก็ต้องใส่อีเวนต์การเคลื่อนที่ที่ร้องขอไว้ในคิวแล้วประมวลผลทีละรายการ ทำให้โครงสร้างซับซ้อนขึ้น
  • วิธีที่เพิกเฉยต่ออินพุตของผู้ใช้ระหว่างแอนิเมชันจะให้ความรู้สึกน่าหงุดหงิดมากสำหรับผู้ใช้
  • ถ้าใช้ exponential smoothing ก็สามารถจัดการการเคลื่อนกล้องได้ด้วยโค้ดแทบจะเหมือนกับปุ่มสลับ
position.x += (target.x - position.x) * (1.0 - exp(- speed * dt));
position.y += (target.y - position.y) * (1.0 - exp(- speed * dt));
  • หากผู้ใช้คลิกอย่างรวดเร็ว ความต่างระหว่างเป้าหมายกับตำแหน่งปัจจุบันจะมากขึ้น จึงทำให้กล้อง เร็วขึ้นอย่างเป็นธรรมชาติ

ทำไมต้องเป็น 1 - exp(- speed * dt)

  • สมการอัปเดตแบบสัดส่วนอย่างง่ายเขียนได้ดังนี้
position += (target - position) * speed * dt;
  • สมการนี้เคลื่อนที่เร็วขึ้นเมื่อความต่างระหว่างเป้าหมายกับตำแหน่งปัจจุบันมากขึ้น และไม่ต้องมีสถานะเพิ่มเติมนอกจากตำแหน่งปัจจุบันกับตำแหน่งเป้าหมาย
  • หากเขียนด้วย lerp ก็จะได้สมการเดียวกัน
position = lerp(position, target, speed * dt);
  • เมื่อ speed * dt เข้าใกล้ 0 จะเคลื่อนที่ช้า และเมื่อเข้าใกล้ 1 จะเข้าใกล้เป้าหมายอย่างรวดเร็ว
  • ถ้า speed * dt มากกว่า 1 จะเกิด overshoot คืออินเตอร์โพเลตเลยผ่านเป้าหมายไป
    • ในตัวอย่าง เมื่อ speed = 220 และ dt = 1 / 125 จะได้ speed * dt มากกว่า 1 ทำให้เกิดการสั่น
    • ถ้า speed * dt < 2 ความต่างเชิงสัมบูรณ์อาจยังลดลงได้ แต่ถ้า speed * dt > 2 ก็แทบไม่ให้พฤติกรรมที่ใช้งานได้
  • เราอาจจำกัดค่าสัมประสิทธิ์อินเตอร์โพเลตด้วย min(1, speed * dt) ได้ แต่ก็ไม่ใช่คำตอบที่จัดการกรณี dt ใหญ่ได้อย่างลื่นไหล
    • โค้ดอาจทำงานช้า จนเฟรมเรตตก
    • ผู้ใช้อาจสลับไปแท็บหรือหน้าต่างอื่น ทำให้โค้ดหยุดไปพักหนึ่งแล้วกลับมาพร้อม dt ที่ยาวหลายวินาที
  • ในการจำลองฟิสิกส์ เราอาจจำกัด dt ที่มากเกินไปหรือแบ่งเป็นหลายอัปเดตได้ แต่ในงานแอนิเมชัน การให้กล้องและปุ่มยังทำงานอย่างเป็นธรรมชาติแม้ dt จะใหญ่ จะส่งผลดีกับประสบการณ์ผู้ใช้มากกว่า

คำตอบในมุมของสมการเชิงอนุพันธ์

  • รูปแบบ A += B * dt โดยทั่วไปสอดคล้องกับการแก้เชิงตัวเลขของ สมการเชิงอนุพันธ์ dA/dt = B
  • สมการอัปเดตอย่างง่ายกำลังแก้สมการต่อไปนี้อยู่
d(position) / dt = (target - position) * speed
  • หากกำหนดตัวแปร x = position, a = target, c = speed จะได้ว่า
dx / dt = (a - x) * c
  • เมื่อแก้สมการนี้โดยตรง จะได้รูปแบบดังนี้
x = x0 + (a - x0) * (1 - exp(-c * t))
  • ดังนั้น ถ้าสูตรที่ถูกต้องเมื่อ dt เล็กคือ position += (target - position) * speed * dt สูตรที่ใช้ได้กับ dt ใด ๆ ก็คือ
position += (target - position) * (1 - exp(- speed * dt));
  • จาก Taylor expansion ที่ว่า exp(x) ≈ 1 + x เมื่อ x เล็ก จึงได้ว่าเมื่อ dt เล็ก 1 - exp(-speed * dt) ≈ speed * dt และกลับไปเทียบเท่าสูตรเดิม
  • แม้ speed * dt จะใหญ่มาก exp(-speed * dt) ก็จะเข้าใกล้ 0 และ 1 - exp(...) จะเข้าใกล้ 1 ทำให้เคลื่อนไปยังค่าที่ใกล้เป้าหมายได้อย่างเสถียร
  • สมการเดียวกันนี้เขียนด้วย lerp ได้เช่นกัน
position = lerp(position, target, 1 - exp(- speed * dt));
position = lerp(target, position, exp(- speed * dt));

การเลือกค่า speed

  • แอนิเมชันทั่วไปมักคิดในรูป ระยะเวลา เช่น “เคลื่อนที่ให้เสร็จภายใน 0.125 วินาที” แต่ในทางเทคนิคแล้ว exponential smoothing ต้องใช้เวลาอนันต์จึงจะไปถึงเป้าหมายได้อย่างแม่นตรง
  • exp(- speed * time) จะเล็กลงเรื่อย ๆ ตามเวลา แต่ไม่มีวันเป็น 0 ดังนั้นหากค่าเริ่มต้นกับค่าเป้าหมายต่างกัน position ก็จะไม่เท่ากับ target แบบสมบูรณ์ในทางคณิตศาสตร์
  • ในทางปฏิบัติ มันจะไปถึงขีดจำกัดความแม่นยำของ floating point หรือความต่างของตำแหน่งกล้องจะเล็กจนผู้ใช้มองไม่ออก จึงดูเหมือนว่าแอนิเมชันสิ้นสุดแล้ว
  • ความหมายของ speed คือ ภายในเวลา 1 / speed นั้น position จะเข้าใกล้ target มากขึ้นพอดี e = 2.71828... เท่า
  • ในการใช้งานจริง มักตั้ง speed ไว้ในช่วง 5..50 และถ้าเทียบกับความเร็วของแอนิเมชันแบบเชิงเส้นหรือ cubic ที่ให้ความรู้สึกใกล้กัน ค่า 2 * speed ของ exponential smoothing อาจให้ความรู้สึกเหมาะสม

ความเชื่อมโยงกับการประมวลผลสัญญาณ

  • หากค้นหาคำว่า “exponential smoothing” หรือ “exponential moving average” จะพบบทความ Exponential smoothing บน Wikipedia
  • เมื่อ dt คงที่และ target เปลี่ยนทุกครั้งที่วนลูป ค่าอาจอัปเดตตามดัชนีรอบได้ในลักษณะประมาณนี้
    • factor = 1 - exp(- speed * dt)
    • โดยทั่วไปมักกำหนด factor เองเป็นค่าระหว่าง 0 และ 1
  • exponential smoothing แบบไม่ต่อเนื่องคือ อะนาล็อกแบบไม่ต่อเนื่อง ของวิธีที่ใช้ในแอนิเมชัน
  • ในงานประมวลผลสัญญาณ มันก็ถูกใช้ด้วยเหตุผลเดียวกัน คือทำงานได้โดยอาศัยเพียงค่าเฉลี่ยปัจจุบัน โดยไม่ต้องเก็บรายการค่าก่อนหน้าหรือสถานะซับซ้อน
  • ในออดิโอดิจิทัล dt มักถูกตรึงไว้เป็นค่ากลับของความถี่การสุ่มตัวอย่างคือ 1 / freq
    • เช่น 1/44100, 1/48000

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

 
GN⁺ 2024-03-09
ความคิดเห็นบน Hacker News
  • ดูเหมือนประเด็นสำคัญตรงนี้ยังไม่ได้ถูกพูดถึงมากพอ นี่ไม่ใช่แค่ เส้นโค้ง easing อีกแบบหนึ่งระหว่าง 0 ถึง 1 หรือ smoothstep() แต่เป็น วิธีแบบไร้สถานะ ที่จัดการอินพุตแทบทุกแบบได้อย่างสม่ำเสมอ จึงมีประโยชน์มาก
    ถ้าเคยใช้ทรานซิชันของ CSS น่าจะคุ้นกับปัญหานี้ หากกำหนดระยะเวลาไว้ที่ 400ms ทำไมต้องเป็น 400ms พอดี? ไม่ควรขึ้นอยู่กับระยะทางที่ต้องเคลื่อนที่หรือ?
    อย่างที่หลายคนบอก exponential smoothing มีปัญหาว่ามันเข้าใกล้จุดหมายแบบเชิงเส้นกำกับ แต่ไม่มีวันไปถึงจริง ๆ วิธีแก้ที่เห็นได้ชัดคือหยุดแอนิเมชันเมื่อสเต็ปเล็กกว่าค่า threshold แต่ก็ไม่งดงามนัก
    ตอนใช้แนวทางคล้ายกันกับ inertial scrolling การเพิ่มพจน์แรงเสียดทานจำลองเข้าไปมีประโยชน์ พจน์นี้จะหักล้างพจน์เอ็กซ์โปเนนเชียล ทำให้ในทางปฏิบัติทำตัวเหมือนความเร็วขั้นต่ำ ตัวอย่างใน Desmos อยู่ที่นี่: https://www.desmos.com/calculator/98ufbuzxhj

    • อาจแก้ได้โดยตีความ exponential smoothing เป็น สมการเชิงอนุพันธ์สามัญ สำหรับระยะทาง D ถึงจุดหมาย exponential smoothing คือการอัปเดตแบบ Euler ของ dD/dt=-C*D และคำตอบคือ D(t)=A*exp(-C*t) จึงเข้าใกล้ 0 แบบเชิงเส้นกำกับแต่ไม่ถึง
      เปลี่ยนเป็นสมการที่ไปถึง 0 ได้ภายในเวลาจำกัด เช่น dD/dt=-C*sqrt(D) ก็พอ คำตอบจะเคลื่อนที่คล้ายพาราโบลาครึ่งหนึ่ง แล้วเมื่อถึง 0 ก็อยู่ตรงนั้นต่อไป การอัปเดตแบบ Euler ของสมการนี้ก็ ไร้สถานะ ตามต้องการเช่นกัน
    • ผมไม่เข้าใจว่าทำไมการหยุดแอนิเมชันเมื่อสเต็ปต่ำกว่า threshold ถึงไม่งดงาม มันเรียบง่าย ใช้งานและตรวจสอบได้ง่าย และยังให้ เอฟเฟกต์ทางสายตา ที่ต้องการด้วย จึงดูสะอาดพอแล้ว
    • ถ้ามีปัญหาเรื่อง 400ms ของทรานซิชัน CSS ผมคิดว่า พารามิเตอร์ความเร็ว ของฟังก์ชันเอ็กซ์โปเนนเชียลก็น่าจะมีปัญหาเดียวกันไม่ใช่หรือ
  • ในฐานะนักพัฒนาเกม ผมมองว่า UI ส่วนใหญ่เหมาะกับ easing tween ที่มีระยะเวลากำหนดไว้ล่วงหน้ามากกว่า แต่เมื่ออยากทำให้การเคลื่อนไหวต่อเนื่องและคาดเดาไม่ได้ ซึ่งไม่มีจุดเริ่มต้นกับจุดจบชัดเจน ดูลื่นขึ้น แอนิเมชันอีกชนิดนี้มีประโยชน์มาก
    เช่น เวลาผู้เล่นลากไทล์บนกริดด้วยเมาส์แล้วอยากให้มัน snap เข้ากริด หรือกรณีขยับกล้องเหมือนในตัวอย่างของบทความ
    ในกรณีแบบนี้ เคล็ดลับการอินเตอร์โพเลตแบบเอ็กซ์โปเนนเชียล มีประโยชน์มาก แต่ไม่ค่อยเป็นที่รู้จัก เกมจำนวนมากใช้การอินเตอร์โพเลตเชิงเส้นที่ไม่แม่นพอ แล้วเจอปัญหาว่าเมื่อมีคนรันบนจอ 240Hz แอนิเมชันให้ความรู้สึกแปลกไปหมด ต่างจากยุคที่ 60fps เป็นมาตรฐาน
    เลยดีใจที่เห็นบทความนี้ ความรู้เฉพาะทางเกินพอดีแบบนี้มักถ่ายทอดกันแบบมุขปาฐะจากซีเนียร์สู่จูเนียร์ในทีมราวกับการฝึกงานช่าง จึงเข้าถึงได้ยาก

  • ผมชอบบทความนี้ แต่สำหรับสวิตช์ toggle ผมอยากบอกว่าการตัดสินของผู้เขียนที่ว่า sqrt ดีกว่าฟังก์ชันกำลังสามนั้นผิดในเชิงวัตถุวิสัย ถ้าดูวิธีที่ สวิตช์ toggle จริง ๆ มักทำงาน ฟังก์ชันกำลังสามเป็นตัวเลือกที่ดีกว่าสำหรับสถานการณ์นี้
    ลองนึกถึงสวิตช์เบรกเกอร์ไฟฟ้าในบ้าน หรือสวิตช์ที่มักเห็นบนอะนาล็อกซินธ์กับอุปกรณ์เสียง อุปกรณ์เหล่านี้มุ่งเน้นสุนทรียะบางอย่าง และแอมป์กีตาร์ Hughes & Kettner ตัวเล็กที่ผมมีก็มีสวิตช์สองตัวที่กดแล้วให้ความรู้สึกดี
    สวิตช์แบบนี้ตอนแรกจะมีแรงต้านเล็กน้อย แล้วเพราะโครงสร้างสปริง มันจะดีด “แป๊ก” ไปยังตำแหน่งใหม่ทันที พฤติกรรมนี้ใช้ ฟังก์ชันกำลังสาม จำลองได้ดีกว่า sqrt หรือ exponential smoothing
    นอกจากข้อทักท้วงเล็ก ๆ นี้ บทความดีมาก แสดงให้เห็นว่าการใช้แอนิเมชันให้ดี เช่น easing function ที่เหมาะสม ช่วยยกระดับประสบการณ์ผู้ใช้ได้ แต่ถ้านำไปใช้แบบไม่ระมัดระวังเหมือนตัวอย่างการอินเตอร์โพเลตเชิงเส้น ก็อาจทำให้รำคาญและทำลายประสบการณ์ได้
    [0] ก็ขึ้นอยู่กับชนิดของสวิตช์ toggle ด้วย อย่างไรก็ตาม ชนิดที่เห็นในอุปกรณ์อย่าง Minimoog ก็มีพฤติกรรม “ต้านก่อนแล้วดีดแป๊กไปตำแหน่งใหม่” ทำให้ใช้งานสนุก อนึ่ง ไม่ได้อวดอุปกรณ์นะ ผมไม่มี Minimoog

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

  • น่าสนใจที่บทความนี้ส่วนใหญ่สุดท้ายก็กลับไปที่ easing ดูเหมือนว่าคนรุ่นใหม่แต่ละรุ่นต้องค้นพบสิ่งนี้ด้วยตัวเองอีกครั้ง
    ผมจำได้ว่าเคยหลงใหลเว็บไซต์ทดลองของ Yugo Nakamura ในช่วงปลายยุค 90 เป็นหนึ่งในเว็บไซต์ยุคแรก ๆ ที่ผมเห็นซึ่งใช้ easing อย่างเสรีจนให้ความรู้สึกเป็นธรรมชาติ: https://www.youtube.com/watch?v=NLt7Gwnt3WY

    • ผมนั่งแถวหน้าสุดในคอนเฟอเรนซ์ที่ Yugo ขึ้นพูด และจำได้ว่าตอนเขาไล่โชว์เดโมให้ดู ผู้เชี่ยวชาญ Flash คนอื่น ๆ ตื่นเต้นกันสุด ๆ
  • ด้วยเหตุผลบางอย่าง ผมอยากมี toggle แบบนี้ แตะหรือคลิกค้างไว้แล้วมันค่อย ๆ เคลื่อนไปประมาณ 75% ของเป้าหมาย พอปล่อยแล้วค่อยดีดส่วนที่เหลือไปทันที
    จากมุมมอง UX ก็ไม่แน่ใจว่าจะมีความหมายอะไร อาจหมายความว่าการตั้งค่าจะถูกนำไปใช้จริงหรือบันทึกในตอนสุดท้าย
    หรืออาจเป็นส่วนหนึ่งของกล่องโต้ตอบ “แน่ใจจริง ๆ หรือ?” ก็ได้ ระหว่างกดค้าง การตั้งค่าจะถูกใช้ แต่ก่อนที่มันจะดีดเข้าที่ สามารถกด Escape เพื่อย้อนกลับได้

    • https://jsfiddle.net/u1vybhqg/
      input:checked + .slider:active:before { transform: translateX(8px); transition: 1s; }
      input:not(:checked) + .slider:active:before { transform: translateX(18px); transition: 1s; }
    • ผมเคยเห็น ผู้ใช้สูงอายุ หลายคนพยายามเลื่อน toggle แล้วไม่มีอะไรเกิดขึ้น ดังนั้นถ้าจะทำแบบนั้น ก็ควรทำให้เลื่อน toggle ได้จริงด้วย ไม่อย่างนั้นใช้ checkbox ไปเลยน่าจะดีกว่า
  • ชอบบทความนี้มาก เคยเขียนเทคนิคที่แทบจะเหมือนกันเป๊ะเมื่อราว 10 ปีก่อน ตอนนั้นเรียกว่า lazy-easy และยังใช้อยู่จนถึงตอนนี้
    บางครั้งเราก็แค่อยากได้แอนิเมชันที่ลื่นไหล โดยไม่ต้องทำ state management ทั้งหมด: https://www.hailpixel.com/articles/lazy-animation-with-lazy-...

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

  • จริง ๆ แล้วเป็นแนวทางที่ค่อนข้างดี และก็ดีในฐานะ proof of concept ของเทคนิคแอนิเมชัน/อีซิงด้วย นึกถึง Flickity ขึ้นมามาก
    https://metafizzy.co/blog/initial-demos/
    https://metafizzy.co/blog/math-time-resting-position/
    https://metafizzy.co/blog/particle-to-slider/
    https://metafizzy.co/blog/flickity-begins/
    โดยเฉพาะเดโมนี้: https://codepen.io/desandro/pen/myXdej
    เทคนิคนี้ไม่ได้มีประโยชน์แค่กับสวิตช์เท่านั้น ที่จริงคงไม่มีใครใช้องค์ประกอบแบบนี้กับสวิตช์ และคงไม่รันลูป requestAnimationFrame 20 ลูปพร้อมกันทั่วทั้งไซต์ด้วย คงไม่ใส่องค์ประกอบที่ตั้งใจทำให้พังลงไปเช่นกัน
    นอกจากนี้ยังไม่มี optimization ที่หยุดเรนเดอร์เมื่อเดลต้าเล็กพอ และน่าจะยังมีการปรับแต่งเล็ก ๆ อีกเป็นสิบอย่างเพื่อให้ถึงระดับ production
    ดูจากปฏิกิริยาตรงนี้แล้ว เหมือนหลายคนไม่ได้อ่านบทความแล้วก็เดาเอาเอง มองเห็นแต่ต้นไม้ไม่เห็นป่า หรือไม่ก็มีอคติและช่างเยาะเย้ยเกินไปจนเอาการตัดสินแบบไม่มืออาชีพมาแชร์เพื่อให้ดูฉลาด
    HN กลายเป็นเหมือน Reddit ตั้งแต่เมื่อไหร่กัน?

    • ในกรณีนี้ หลายคนอ่านบทความไม่ได้ เพราะเมื่อเลื่อนหน้าลงไปประมาณหนึ่งในสาม เบราว์เซอร์ ที่ไม่ใช่ Chromium ส่วนใหญ่จะค้างไปเลย
    • ถ้าถามว่า “HN กลายเป็นเหมือน Reddit ตั้งแต่เมื่อไหร่กัน?” ก็ลองอ่านแค่ย่อหน้าสุดท้ายของ https://news.ycombinator.com/newsguidelines.html ก็พอ
    • การเปลี่ยนแปลงแบบนั้นดำเนินมาสักพักแล้ว แต่ตามแนวทางของ HN การพูดเรื่องแบบนั้นโดยตรงถูกห้ามไว้ คนเลยไม่พูดกัน เป็นเรื่องแปลกจริง ๆ
    • ในกรณีนี้ ความเยาะเย้ยถากถาง ดูไม่ค่อยยุติธรรมเท่าไร
      ส่วนประเด็นที่ว่า HN กำลังเปลี่ยนเป็นเหมือน Reddit นั้น บางทีในแวดวงเทคโนโลยีโดยรวมอาจมีบรรยากาศถากถางแบบคลุมเครืออยู่ก็ได้ มีความไม่ไว้วางใจรองรับอยู่ว่ารัฐบาลและบริษัทต่าง ๆ ตั้งใจจะใช้เทคโนโลยีทรงพลังที่กำลังเกิดขึ้นอย่างไร และพวกเขากำลังสร้างมันขึ้นมาเพื่อจำกัดและควบคุมปัจเจกบุคคล มากกว่าจะเพิ่มเสรีภาพของปัจเจกหรือไม่
  • ใกล้เคียงกับแก่นแท้ของ Emotional Design(https://en.wikipedia.org/wiki/Emotional_Design) เลยทีเดียว แม้จะเป็นแอนิเมชันเล็ก ๆ ก็ยังมีเรื่องให้พูดถึงได้มากมาย