Lompat ke konten
dokumentasi.id
Commit 83fc07e
v2
Star3,5 rb

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.
Fig. 1: Siklus hidup div.waves-rippleEffect.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-108 dan src/js/waves.js:467-478.

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-122.

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-147.

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-154 dan src/js/waves.js:259-271.

AtributNilai saat ditulisDibaca lagi di fase fade-out
data-holdDate.now() saat node dibuatbahan diff = Date.now() - Number(data-hold)
data-xrelativeX dalam piksel dari tepi kiri elemennilai left pada style fade
data-yrelativeY dalam piksel dari tepi atas elemennilai top pada style fade
data-scalescale(element.clientWidth / 100 * 3)bagian pertama transform saat fade
data-translatetranslate(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-164 dan src/less/waves.less:81-83. 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-180. 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-61.

NilaiAsal di kodePeran dalam siklus
750Effect.durationdurasi scale-in dan fade-out untuk event non-mousemove
200Effect.delayjeda sebelum Effect.show dipanggil pada touchstart
2500duration saat e.type === 'mousemove'transition-duration node yang mengikuti gerakan mouse
350delay = 350 - diffbatas minimum node bertahan sebelum fade
150delay = 150 untuk mousemovedelay 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-199. 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-271.

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-283.

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.
Fig. 2: Perhitungan delay dan duration di removeRippleremoveRipple 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-317. Total waktu satu node di DOM kira-kira delay + 2 * duration, kecuali node sudah tidak ada.

⚠️ el.removeChild(ripple) dibungkus try / catch yang mengembalikan false tanpa logging. Bila node sudah dipindahkan atau induknya diganti selama fade, ripple hilang tanpa jejak dan tanpa error yang terlihat. Pemindahan induk memang terjadi pada elemen input dan img; 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-562. 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-589. Pemicu touchstart beserta jeda Effect.delay dijelaskan di Penanganan Perangkat Touch, sedangkan asal aturan .waves-ripple dijelaskan di Sistem Gaya Less, SCSS, dan SASS.