5
Siklus Hidup Ripple
Setiap ripple adalah satu node div sekali pakai. Effect.show membuat node itu, menempelkan lima atribut data-*, lalu menulis style dua kali; removeRipple membaca kembali atribut tersebut, menjalankan fade-out, dan akhirnya memanggil el.removeChild(ripple). Halaman ini hanya membahas siklus hidup node tersebut; pemicu event dan pencarian elemen .waves-effect dibahas di Alur Efek Klik dari Event ke Elemen.
Diagram keadaan ripple
stateDiagram-v2
state "Tidak ada node ripple" as Absent
state "div.waves-ripple.waves-rippling" as Rippling
state "Fade-out, opacity 0" as Fading
state "Node dilepas dari DOM" as Removed
state "Dibatalkan sebelum node dibuat" as Aborted
[*] --> Absent
Absent --> Rippling: Effect.show membuat div dan lima data attribute
Absent --> Aborted: e.button === 2
Rippling --> Fading: Effect.hide lalu removeRipple
Rippling --> Fading: Waves.calm
Fading --> Removed: setTimeout duration lalu el.removeChild
Removed --> [*]
style Absent fill:#f59e0b,color:#000
style Rippling fill:#336791,color:#fff
style Fading fill:#8b5cf6,color:#fff
style Removed fill:#2d7d46,color:#fff
style Aborted fill:#dc2626,color:#fff
%% fig: Siklus hidup div.waves-ripple
%% desc: Effect.show membuat node ripple berisi lima data attribute, removeRipple mengubahnya menjadi fade-out, lalu node dilepas dari DOM; klik kanan membatalkan sebelum node dibuat.Keadaan Absent memakai warna konfigurasi karena durasi yang membentuk seluruh siklus berasal dari Effect.duration: 750 dan Effect.delay: 200, dua nilai awal yang dapat ditimpa lewat opsi Waves.init src/js/waves.js:104-1081 dan src/js/waves.js:467-4782.
Pembuatan node oleh Effect.show
Effect.show menolak klik kanan lebih dulu: bila e.button === 2 fungsi itu mengembalikan false dan tidak ada node yang lahir. Bila lolos, node dibuat dengan document.createElement('div'), class waves-ripple waves-rippling, lalu langsung di-appendChild ke elemen induknya src/js/waves.js:110-1223.
Koordinat ripple dihitung dari offset(element). Perangkat touch memakai e.touches[0].pageY dan e.touches[0].pageX, sedangkan mouse memakai e.pageY dan e.pageX; hasil negatif dipaksa menjadi 0 untuk mendukung synthetic event. Dari element.clientWidth lahir scale = 'scale(' + ((element.clientWidth / 100) * 3) + ')' dan translate awal 'translate(0,0)' yang diganti translate(<velocity.x>px, <velocity.y>px) bila argumen velocity diberikan src/js/waves.js:124-1474.
Lima atribut yang menempel pada node
Sebelum digayakan, Effect.show menulis lima atribut pada node; removeRipple membacanya kembali beberapa ratus milidetik kemudian src/js/waves.js:149-1545 dan src/js/waves.js:259-2716.
| Atribut | Nilai saat ditulis | Dibaca lagi di fase fade-out |
|---|---|---|
data-hold | Date.now() saat node dibuat | bahan diff = Date.now() - Number(data-hold) |
data-x | relativeX dalam piksel dari tepi kiri elemen | nilai left pada style fade |
data-y | relativeY dalam piksel dari tepi atas elemen | nilai top pada style fade |
data-scale | scale(element.clientWidth / 100 * 3) | bagian pertama transform saat fade |
data-translate | translate(0,0) atau translate(velocity.x px, velocity.y px) | bagian kedua transform saat fade |
Atribut ini adalah satu-satunya memori ripple. Tidak ada objek JavaScript yang menyimpan keadaan node, sehingga removeRipple wajib membaca DOM untuk merekonstruksi posisi dan skala akhir.
Dua penulisan style
Penulisan pertama hanya berisi top dan left, dan dibungkus class waves-notransition yang menambahkan transition: none !important supaya node tidak beranimasi dari scale(0) ke posisi barunya src/js/waves.js:156-1647 dan src/less/waves.less:81-838. Penulisan kedua menggantikan seluruh atribut style: transform berprefix vendor, opacity: 1, dan transition-duration sebesar 2500 ms untuk mousemove atau Effect.duration untuk event lain src/js/waves.js:166-1809. Aturan .waves-ripple di waves.less sudah menyiapkan node berukuran 100px kali 100px dengan margin-top dan margin-left -50px, border-radius: 50%, opacity: 0, pointer-events: none, serta transition-property: transform, opacity src/less/waves.less:40-6110.
| Nilai | Asal di kode | Peran dalam siklus |
|---|---|---|
750 | Effect.duration | durasi scale-in dan fade-out untuk event non-mousemove |
200 | Effect.delay | jeda sebelum Effect.show dipanggil pada touchstart |
2500 | duration saat e.type === 'mousemove' | transition-duration node yang mengikuti gerakan mouse |
350 | delay = 350 - diff | batas minimum node bertahan sebelum fade |
150 | delay = 150 untuk mousemove | delay tetap ketika pointer bergerak |
Fade-out dan pelepasan node
Effect.hide mengumpulkan element.getElementsByClassName('waves-rippling') lalu memanggil removeRipple untuk setiap node, kemudian mencabut listener touchend, touchcancel, mouseup, dan mouseleave dari elemen src/js/waves.js:183-19911. Langkah pertama removeRipple adalah melepas class waves-rippling, sehingga node yang sedang fade tidak lagi ikut terpilih oleh Effect.hide berikutnya src/js/waves.js:259-2716.
Perhitungan delay memakai data-hold: diff = Date.now() - Number(data-hold), lalu delay = 350 - diff. Nilai negatif dipaksa menjadi 0, dan khusus e.type === 'mousemove' delay ditetapkan 150 apa pun nilai diff src/js/waves.js:274-28312.
graph LR
A["removeRipple(e, el, ripple)"] --> B["lepas class waves-rippling"]
B --> C["diff = Date.now() - Number(data-hold)"]
C --> D["delay = 350 - diff"]
D --> E{"delay negatif?"}
E -->|ya| F["delay = 0"]
E -->|tidak| G["delay tetap 350 - diff"]
F --> H{"e.type mousemove?"}
G --> H
H -->|ya| I["delay = 150 dan duration = 2500"]
H -->|tidak| J["delay tetap dan duration = Effect.duration"]
I --> K["setTimeout menulis style opacity 0"]
J --> K
K --> L["setTimeout duration lalu el.removeChild"]
style A fill:#2d7d46,color:#fff
style F fill:#dc2626,color:#fff
style I fill:#f59e0b,color:#000
style J fill:#f59e0b,color:#000
style K fill:#8b5cf6,color:#fff
style L fill:#336791,color:#fff
%% fig: Perhitungan delay dan duration di removeRipple
%% desc: removeRipple melepas class penanda, menghitung delay dari data-hold, lalu menjadwalkan fade dan pemanggilan el.removeChild.Setelah delay habis, setTimeout pertama menulis style baru: top dan left dari data-y dan data-x, opacity: 0, transition-duration sebesar duration, serta transform dari data-scale dan data-translate. Di dalam callback itu ada setTimeout kedua selama duration yang memanggil el.removeChild(ripple) src/js/waves.js:285-31713. Total waktu satu node di DOM kira-kira delay + 2 * duration, kecuali node sudah tidak ada.
⚠️
el.removeChild(ripple)dibungkustry/catchyang mengembalikanfalsetanpalogging. Bila node sudah dipindahkan atau induknya diganti selama fade, ripple hilang tanpa jejak dan tanpa error yang terlihat. Pemindahan induk memang terjadi pada elemeninputdanimg; lihat Pembungkusan Elemen input dan img.
Titik masuk yang memulai dan mengakhiri siklus
Selain showEffect, ada dua pintu masuk publik. Waves.ripple(elements, options) menyusun objek mousedown sintetis { type: 'mousedown', button: 1 }, mengisi pageX dan pageY dari titik tengah elemen atau dari options.position, memanggil Effect.show, lalu menjadwalkan Effect.hide dengan mouseup sintetis setelah options.wait src/js/waves.js:538-56214. Karena data-hold baru saja ditulis, diff mendekati nol sehingga fade dari Waves.ripple praktis menunggu hampir 350 ms. Waves.calm(elements) tidak membuat node baru: fungsi itu memanggil Effect.hide dengan mouseup sintetis untuk setiap elemen yang diberikan src/js/waves.js:579-58915. Pemicu touchstart beserta jeda Effect.delay dijelaskan di Penanganan Perangkat Touch, sedangkan asal aturan .waves-ripple dijelaskan di Sistem Gaya Less, SCSS, dan SASS.