- Exponential smoothing adalah teknik sederhana yang bisa digunakan secara luas untuk animasi yang perlu mengikuti nilai target dengan halus, seperti tombol toggle, kamera, elemen UI, dan volume audio
- Rumus intinya adalah
position += (target - position) * (1 - exp(- speed * dt)), dan dengan hanya posisi saat ini serta posisi target, ia merespons perubahan target secara alami
- Pergerakan linear dan easing umum memerlukan penanganan seperti lompatan, jitter, atau antrean input saat klik di tengah animasi, input kamera cepat, atau
dt besar, sedangkan exponential smoothing mengurangi kompleksitas dengan struktur yang sama
1 - exp(- speed * dt) berasal dari solusi persamaan diferensial yang diselesaikan oleh rumus pembaruan proporsional; pada dt kecil hampir sama dengan rumus lama, dan pada dt besar pun menghindari overshoot
- Secara matematis nilai target tidak pernah dicapai persis, tetapi karena presisi floating point dan batas perubahan yang bisa dideteksi pengguna, dalam animasi nyata ia berperilaku seolah sudah cukup selesai
Masalah yang terlihat pada tombol toggle
- Posisi switch pada tombol toggle bisa dihitung sederhana seperti
turned_on ? max_x : min_x, tetapi ketika status berubah, posisinya langsung berpindah seketika sehingga kurang terasa hidup
- Animasi linear diimplementasikan dengan memperbarui posisi saat ini pada kecepatan tetap lalu membatasi rentangnya
position.x += (turned_on ? 1 : -1) * speed * dt;
position.x = clamp(position.x, min_x, max_x);
- Gerakan dengan kecepatan tetap membuat posisi menjadi fungsi linear terhadap waktu, sehingga gerakannya bisa terlihat kaku
- Menambahkan fungsi easing dapat membuat gerakan lebih halus
- Cubic smoothstep klasik:
3t² - 2t³
- Easing akar kuadrat:
sqrt(t)
smoothstep memiliki simetri 1 - f(t) = f(1 - t), sehingga kode yang sama bisa dipakai untuk animasi maju dan mundur
sqrt memerlukan rumus berbeda tergantung arah
- Saat dinyalakan:
sqrt(t)
- Saat dimatikan:
1 - sqrt(1 - t)
sqrt bergerak cepat di awal dan melambat dengan halus saat mendekati target, tetapi bahkan untuk toggle sederhana dengan 2 status pun tetap perlu manajemen state seperti t, arah, dan perhitungan easing
- Jika pengguna mengeklik lagi di tengah animasi, pendekatan easing yang ada dapat menciptakan diskontinuitas berupa posisi yang tiba-tiba meloncat
Rumus exponential smoothing
- Exponential smoothing bekerja dengan menentukan posisi target, lalu menarik posisi saat ini sedikit demi sedikit ke arah target
target = (state.value ? max_x : min_x);
position.x += (target - position.x) * (1 - exp(- dt * speed));
- Besar pembaruan ditentukan berdasarkan selisih antara posisi saat ini dan posisi target, yaitu
target - position.x
1 - exp(- dt * speed) adalah koefisien interpolasi yang menentukan seberapa jauh bergerak ke arah target pada setiap frame
- Walaupun target berubah di tengah jalan, gerakan langsung berlanjut menuju target baru tanpa waktu progres atau state arah animasi terpisah
- Pada contoh tombol toggle, gerakannya mulai cepat dan melambat di dekat target seperti easing
sqrt, tetapi masalah lompatan saat klik di tengah animasi berkurang
Keunggulan yang makin besar pada pergerakan kamera
- Masalah yang sama juga muncul pada kamera yang bergerak di atas peta
- Jika interpolasi berkecepatan tetap diimplementasikan secara sederhana, kode menggunakan tanda arah target per sumbu
position.x += sign(target.x - position.x) * speed * dt;
position.y += sign(target.y - position.y) * speed * dt;
- Pada titik selesai animasi, tanda dari
target - position dapat bolak-balik antara positif dan negatif sehingga terjadi jitter
- Untuk mencegahnya, diperlukan fungsi pembaruan terpisah yang membatasi
delta ke rentang 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;
}
- Untuk menerapkan cubic easing pada pergerakan kamera, event pergerakan yang diminta harus dimasukkan ke antrean dan diproses satu per satu, sehingga strukturnya menjadi rumit
- Mengabaikan input pengguna selama animasi terasa sangat menjengkelkan bagi pengguna
- Dengan exponential smoothing, pergerakan kamera bisa ditangani dengan kode yang hampir sama seperti tombol toggle
position.x += (target.x - position.x) * (1.0 - exp(- speed * dt));
position.y += (target.y - position.y) * (1.0 - exp(- speed * dt));
- Jika pengguna mengeklik dengan cepat, selisih antara target dan posisi saat ini membesar, sehingga kamera secara alami menjadi lebih cepat
Mengapa 1 - exp(- speed * dt)?
- Pembaruan proporsional sederhana dapat ditulis dengan rumus berikut
position += (target - position) * speed * dt;
- Rumus ini bergerak lebih cepat ketika selisih antara target dan posisi saat ini lebih besar, dan tidak memerlukan state tambahan selain posisi saat ini dan posisi target
- Jika ditulis dengan
lerp, rumusnya menjadi sama
position = lerp(position, target, speed * dt);
- Jika
speed * dt mendekati 0, gerakannya lambat; jika mendekati 1, ia mendekati target dengan cepat
- Jika
speed * dt lebih besar dari 1, interpolasi melewati target dan terjadi overshoot
- Pada contoh ketika
speed = 220 dan dt = 1 / 125, speed * dt menjadi lebih besar dari 1 sehingga muncul jitter
- Jika
speed * dt < 2, selisih absolutnya bisa berkurang, tetapi jika speed * dt > 2, perilakunya tidak lagi berguna
- Koefisien interpolasi bisa dibatasi dengan
min(1, speed * dt), tetapi ini bukan solusi yang menangani situasi ketika dt membesar secara mulus
- Kode yang lambat dapat membuat frame rate turun
- Pengguna dapat berpindah ke tab atau jendela lain sehingga kode berhenti, lalu aktif kembali dengan
dt beberapa detik
- Dalam simulasi fisika,
dt besar dapat dibatasi atau dipecah menjadi beberapa pembaruan, tetapi untuk animasi, pengalaman pengguna lebih baik jika kamera dan tombol tetap bergerak alami bahkan pada dt besar
Solusi dilihat dari persamaan diferensial
- Bentuk
A += B * dt umumnya merupakan penyelesaian numerik dari persamaan diferensial dA/dt = B
- Rumus pembaruan sederhana menyelesaikan persamaan berikut
d(position) / dt = (target - position) * speed
- Jika variabelnya ditetapkan sebagai
x = position, a = target, dan c = speed, bentuknya menjadi
dx / dt = (a - x) * c
- Jika persamaan ini diselesaikan langsung, didapat bentuk berikut
x = x0 + (a - x0) * (1 - exp(-c * t))
- Karena itu, jika rumus yang benar pada
dt kecil adalah position += (target - position) * speed * dt, maka rumus yang dapat dipakai untuk dt sembarang adalah
position += (target - position) * (1 - exp(- speed * dt));
- Dalam ekspansi Taylor,
exp(x) ≈ 1 + x, sehingga pada dt kecil 1 - exp(-speed * dt) ≈ speed * dt, sama seperti rumus sederhana sebelumnya
- Walaupun
speed * dt sangat besar, exp(-speed * dt) mendekati 0 dan 1 - exp(...) mendekati 1, sehingga bergerak stabil ke nilai yang dekat dengan target
- Rumus yang sama juga bisa ditulis dengan
lerp
position = lerp(position, target, 1 - exp(- speed * dt));
position = lerp(target, position, exp(- speed * dt));
Memilih nilai speed
- Animasi umum mudah dipikirkan sebagai durasi, seperti “bergerak dalam 0,125 detik”, tetapi secara teknis exponential smoothing membutuhkan waktu tak hingga untuk mencapai target secara persis
exp(- speed * time) makin kecil seiring waktu tetapi tidak pernah menjadi 0, sehingga jika nilai awal dan target berbeda, position secara matematis juga tidak pernah sepenuhnya sama dengan target
- Dalam praktiknya, animasi tampak selesai karena mencapai batas presisi floating point, atau karena selisih posisi kamera sudah terlalu kecil untuk terlihat oleh pengguna
- Makna
speed adalah selama waktu 1 / speed, position menjadi tepat e = 2.71828... kali lebih dekat ke target
- Dalam penggunaan nyata,
speed biasanya berada pada rentang 5..50, dan untuk kecepatan animasi linear atau cubic dengan rasa yang sama, sekitar 2 * speed pada exponential smoothing bisa terasa sesuai
Kaitan dengan pemrosesan sinyal
- Jika mencari “exponential smoothing” atau “exponential moving average”, Anda dapat menemukan artikel Exponential smoothing di Wikipedia
- Jika
dt tetap dan target berubah pada setiap iterasi, nilai diperbarui kira-kira seperti berikut berdasarkan indeks iterasi
factor = 1 - exp(- speed * dt)
- Biasanya
factor ditentukan langsung sebagai nilai antara 0 dan 1
- Exponential smoothing diskret adalah analogi diskret dari cara yang digunakan dalam animasi
- Dalam pemrosesan sinyal, teknik ini juga dipakai karena dapat bekerja hanya dengan nilai rata-rata saat ini tanpa menyimpan daftar nilai sebelumnya atau state yang rumit
- Pada audio digital, biasanya
dt tetap sebagai kebalikan dari frekuensi sampling, yaitu 1 / freq
1 komentar
Pendapat Hacker News
Rasanya poin intinya belum cukup dibahas di sini. Ini bukan sekadar kurva easing lain antara 0 dan 1 atau
smoothstep(), melainkan metode stateless yang menangani hampir semua input secara teratur, jadi benar-benar bergunaKalau pernah memakai transisi CSS, masalah ini pasti terasa familier. Jika durasinya ditetapkan 400 ms, kenapa harus 400 ms? Bukankah seharusnya bergantung pada jarak yang harus ditempuh?
Seperti yang dikatakan orang lain, exponential smoothing punya masalah: ia hanya mendekati tujuan secara asimtotik dan tidak pernah benar-benar sampai. Ada solusi yang jelas, yaitu menghentikan animasi saat langkahnya lebih kecil dari ambang batas, tetapi itu tidak elegan
Saat memakai pendekatan serupa untuk inertial scrolling, menambahkan suku mirip gesekan ternyata berguna. Suku ini meniadakan suku eksponensial sehingga pada praktiknya berperilaku seperti kecepatan minimum. Contoh Desmos di sini: https://www.desmos.com/calculator/98ufbuzxhj
Dke tujuan. Exponential smoothing adalah pembaruan Euler daridD/dt=-C*D, dan solusinyaD(t)=A*exp(-C*t), sehingga ia mendekati 0 secara asimtotik tetapi tidak mencapainyaKita bisa menggantinya dengan persamaan yang menuju 0 dalam waktu hingga, misalnya
dD/dt=-C*sqrt(D). Solusinya bergerak seperti setengah fungsi kuadrat lalu, begitu mencapai 0, tetap di sana. Pembaruan Euler untuk persamaan ini juga stateless seperti yang diinginkanSebagai pengembang game, menurut saya untuk sebagian besar UI, easing tween dengan durasi yang sudah ditentukan sebelumnya lebih baik. Namun ketika ingin menghaluskan gerakan kontinu dan tak terduga yang titik awal dan akhirnya tidak jelas, jenis animasi lain ini sangat berguna
Contohnya ketika pemain menyeret tile di atas grid dengan mouse dan ingin membuatnya snap ke grid, atau saat menggerakkan kamera seperti contoh di artikel
Dalam kasus seperti ini, trik interpolasi eksponensial sangat berguna, tetapi belum dikenal luas. Banyak game memakai interpolasi linear yang kurang tepat, lalu mengalami masalah ketika seseorang menjalankannya di monitor 240 Hz—berbeda dengan masa ketika 60 fps adalah standar—sehingga rasa animasinya menjadi benar-benar aneh
Karena itu saya senang melihat artikel ini. Pengetahuan yang terlalu spesifik seperti ini biasanya sulit diakses karena sering diteruskan secara lisan ala magang dari senior tim ke junior
Saya suka artikelnya, tetapi untuk toggle switch, saya ingin mengatakan bahwa penilaian penulis bahwa
sqrtlebih baik daripada fungsi kubik itu secara objektif keliru. Jika melihat cara toggle switch sungguhan umumnya bekerja, fungsi kubik adalah pilihan yang lebih baik untuk situasi iniBayangkan sakelar pemutus listrik di rumah, atau sakelar yang sering terlihat pada synth analog dan peralatan audio. Itu perangkat yang memang mengejar estetika tertentu, dan ampli gitar kecil Hughes & Kettner yang saya punya juga memiliki dua sakelar dengan rasa tekan yang menyenangkan
Sakelar seperti ini pada awalnya memberi sedikit resistansi, lalu karena struktur pegasnya tiba-tiba menjentik ke posisi baru. Gerakan ini dimodelkan lebih baik oleh fungsi kubik daripada
sqrtatau exponential smoothingDi luar catatan kecil itu, artikelnya sangat bagus. Ia menunjukkan dengan baik bahwa animasi yang digunakan dengan tepat, seperti fungsi easing yang sesuai, dapat meningkatkan pengalaman pengguna, tetapi jika diterapkan tanpa kehati-hatian seperti pada contoh interpolasi linear, hasilnya bisa mengganggu dan merusak pengalaman
[0] Memang tergantung jenis toggle switch. Namun jenis yang bisa dilihat pada perangkat seperti Minimoog juga menunjukkan gerakan “menahan lalu menjentik ke posisi baru”, sehingga menyenangkan untuk dioperasikan. Sebagai catatan, ini bukan pamer perangkat; saya tidak punya Minimoog
Saya terus terkejut betapa sering trik nonlinier sederhana dapat menambah kesenangan dalam interaksi online. Dalam persepsi warna, ini juga penting untuk memahami mengapa dua warna tidak cukup bisa dibedakan oleh sebagian orang
Yang aneh adalah manusia tidak selalu memahami akselerasi dengan baik. Jangan lari ke atas bukit dengan keyakinan bahwa api bergerak hampir dengan kecepatan konstan seperti di tanah datar. Api berakselerasi saat menanjak
Anak-anak cepat belajar seberapa cepat bola yang dilempar bergerak menyusuri tanah, tetapi tidak selalu memahami seberapa cepat bola itu bergerak saat mengenai tangan karena gravitasi
Menarik bahwa sebagian besar artikel ini pada akhirnya bermuara pada easing. Sepertinya setiap generasi baru harus menemukannya kembali sendiri
Saya ingat terpesona oleh situs web eksperimental Yugo Nakamura pada akhir 90-an. Itu salah satu situs web awal yang saya lihat yang memakai easing secara bebas untuk memberi nuansa organik: https://www.youtube.com/watch?v=NLt7Gwnt3WY
Entah kenapa saya ingin ada toggle seperti ini. Saat disentuh atau diklik dan ditahan, ia bergerak perlahan sampai sekitar 75% menuju target, lalu saat dilepas, sisanya menjentik
Dari sudut pandang UX, saya tidak yakin apa maknanya. Mungkin berarti pengaturan baru benar-benar diterapkan atau disimpan di bagian akhir
Atau bisa juga menjadi bagian dari dialog “Apakah Anda benar-benar yakin?”. Selama ditekan, pengaturan diterapkan, tetapi sebelum menjentik masuk ke tempatnya, bisa dibatalkan dengan Escape
input:checked + .slider:active:before { transform: translateX(8px); transition: 1s; }input:not(:checked) + .slider:active:before { transform: translateX(18px); transition: 1s; }Saya suka tulisan ini. Saya pernah membuat teknik yang hampir persis sama sekitar 10 tahun lalu; waktu itu saya menyebutnya
lazy-easy, dan masih memakainya sampai sekarangKadang kita hanya ingin animasi yang halus tanpa perlu mengelola semua state: https://www.hailpixel.com/articles/lazy-animation-with-lazy-...
Tulisannya sendiri sangat bagus. Demonya tampaknya berjalan baik di Chrome, tetapi di Firefox ia berhenti saat menggulir, dan rendering halaman benar-benar terhenti
Sebenarnya ini pendekatan yang cukup bagus, dan juga bagus sebagai proof of concept untuk teknik animasi dan easing. Sangat mengingatkan pada 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/
Khususnya demo ini: https://codepen.io/desandro/pen/myXdej
Teknik ini tidak hanya berguna untuk switch. Dalam praktiknya, kita juga tidak akan memakai elemen seperti ini untuk switch, dan tidak akan menjalankan 20 loop
requestAnimationFramesecara paralel di seluruh situs. Kita juga tidak akan sengaja memasukkan elemen yang rusakSelain itu, tidak ada optimisasi untuk menghentikan rendering ketika delta sudah cukup kecil, dan kemungkinan ada puluhan penyesuaian kecil lain agar layak untuk production
Melihat respons di sini, orang-orang tampaknya berasumsi tanpa membaca tulisannya, hanya melihat pohon tanpa melihat hutannya, atau terlalu bias dan sinis sampai membagikan penilaian yang tidak profesional demi terlihat pintar
Sejak kapan HN jadi seperti Reddit?
Soal HN yang berubah seperti Reddit, mungkin memang ada suasana sinis yang samar terhadap teknologi secara umum. Ada ketidakpercayaan yang mendasari terhadap bagaimana pemerintah dan perusahaan ingin menggunakan teknologi kuat yang sedang muncul—apakah teknologi itu dibangun bukan untuk memperbesar kebebasan individu, melainkan untuk membatasi dan mengelola individu
Ini hampir seperti esensi dari emotional design(https://en.wikipedia.org/wiki/Emotional_Design). Bahkan di balik animasi yang sangat kecil pun ada banyak hal yang bisa dibicarakan