feat: button add wave

This commit is contained in:
luoxiao 2023-11-02 10:16:31 +08:00
parent 4597b823e7
commit ec551cd2e6
7 changed files with 118 additions and 55 deletions

View file

@ -5,7 +5,7 @@
color: var(--font-color);
border: 1px solid var(--border-color);
border-radius: 5px;
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
@ -54,3 +54,34 @@
transform: rotate(360deg);
}
}
.melt-button .melt-wave {
pointer-events: none;
animation-iteration-count: 1;
animation-duration: 0.6s;
animation-timing-function: cubic-bezier(0, 0, 0.2, 1),
cubic-bezier(0, 0, 0.2, 1);
}
.melt-button .melt-wave.melt-wave--active {
z-index: 1;
animation-name: meltButtonWaveSpread, meltButtonWaveOpacity;
}
@keyframes meltButtonWaveSpread {
from {
box-shadow: 0 0 0.5px 0 var(--melt-ripple-color);
}
to {
box-shadow: 0 0 0.5px 6px var(--melt-ripple-color);
}
}
@keyframes meltButtonWaveOpacity {
from {
opacity: 0.6;
}
to {
opacity: 0;
}
}

View file

@ -1,6 +1,12 @@
mod theme;
use crate::{components::*, icon::*, theme::*, utils::mount_style::mount_style};
use crate::{
components::*,
icon::*,
theme::*,
utils::{mount_style::mount_style, ComponentRef},
wave::{Wave, WaveRef},
};
use leptos::*;
pub use theme::ButtonTheme;
@ -76,6 +82,7 @@ pub fn Button(
css_vars.push_str("--font-color: #fff;");
css_vars.push_str(&format!("--border-color: {bg_color};"));
css_vars.push_str(&format!("--border-color-hover: {bg_color};"));
css_vars.push_str(&format!("--melt-ripple-color: {bg_color};"));
} else if variant.get() == ButtonVariant::Text {
css_vars.push_str(&format!("--font-color-hover: {bg_color};"));
css_vars.push_str(&format!(
@ -86,10 +93,12 @@ pub fn Button(
"--background-color-active: {};",
theme.button.color_text_active
));
css_vars.push_str(&format!("--melt-ripple-color: #0000;"));
} else {
css_vars.push_str(&format!("--font-color-hover: {bg_color};"));
css_vars.push_str("--border-color: #555a;");
css_vars.push_str("--border-color-hover: #555;");
css_vars.push_str(&format!("--melt-ripple-color: #0000;"));
}
});
@ -111,10 +120,15 @@ pub fn Button(
disabled.get()
});
let wave_ref = ComponentRef::<WaveRef>::default();
let on_click = move |event| {
if disabled.get() {
return;
}
if let Some(wave_ref) = wave_ref.get_untracked() {
wave_ref.play();
}
let Some(callback) = on_click.as_ref() else {
return;
};
@ -131,6 +145,7 @@ pub fn Button(
style=move || format!("{}{}", css_vars.get(), style.get())
on:click=on_click
>
<Wave comp_ref=wave_ref/>
{move || {
if loading.get() {
view! {

View file

@ -69,4 +69,3 @@ pub use tag::*;
pub use theme::*;
pub use upload::*;
pub use utils::{mount_style::mount_style, signal::SignalWatch};
pub use wave::*;

View file

@ -0,0 +1,30 @@
use leptos::{create_rw_signal, RwSignal, SignalGetUntracked, SignalSet};
pub struct ComponentRef<T: 'static>(RwSignal<Option<T>>);
impl<T> Default for ComponentRef<T> {
fn default() -> Self {
Self(create_rw_signal(None))
}
}
impl<T> Clone for ComponentRef<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: 'static> Copy for ComponentRef<T> {}
impl<T> ComponentRef<T> {
pub fn get_untracked(&self) -> Option<T>
where
T: Clone,
{
self.0.get_untracked()
}
pub fn load(&self, comp_ref: T) {
self.0.set(Some(comp_ref));
}
}

View file

@ -1,8 +1,10 @@
// mod callback;
mod component_ref;
pub mod maybe_rw_signal;
mod maybe_signal_store;
pub mod mount_style;
pub mod signal;
// pub use callback::AsyncCallback;
pub use component_ref::ComponentRef;
pub use maybe_signal_store::*;

View file

@ -1,28 +1,44 @@
use crate::utils::mount_style::mount_style;
use leptos::*;
use crate::utils::{mount_style::mount_style, ComponentRef};
use leptos::{leptos_dom::helpers::TimeoutHandle, *};
use std::time::Duration;
#[derive(Clone)]
pub struct WaveRef {
play: Callback<()>,
}
impl WaveRef {
pub fn play(&self) {
self.play.call(());
}
}
#[component]
pub fn Wave(children: Children) -> impl IntoView {
pub fn Wave(#[prop(optional)] comp_ref: ComponentRef<WaveRef>) -> impl IntoView {
mount_style("wave", include_str!("./wave.css"));
let (css_vars, set_css_vars) = create_signal(String::new());
let wave_ref = create_node_ref::<html::Div>();
wave_ref.on_load(move |wave| {
_ = wave.on(ev::mousedown, move |ev| {
wave_ref.on_load(move |wave| {
let rect = wave.get_bounding_client_rect();
let client_x = f64::from(ev.client_x());
let client_y = f64::from(ev.client_y());
set_css_vars.set(format!(
"--x: {}px; --y: {}px",
client_x - rect.left(),
client_y - rect.top()
));
})
});
let animation_timeout_handle = create_rw_signal(None::<TimeoutHandle>);
let play = Callback::new(move |_: ()| {
if let Some(handle) = animation_timeout_handle.get() {
handle.clear();
animation_timeout_handle.set(None);
}
if let Some(wave_ref) = wave_ref.get() {
_ = wave_ref.offset_height();
}
let handle = set_timeout_with_handle(
move || {
animation_timeout_handle.set(None);
},
Duration::from_secs(1),
);
if let Ok(handle) = handle {
animation_timeout_handle.set(Some(handle))
}
});
comp_ref.load(WaveRef { play });
view! {
<div class="melt-wave" ref=wave_ref style=move || css_vars.get()>
{children()}
<div class="melt-wave" class=("melt-wave--active", move || animation_timeout_handle.with(|handle| handle.is_some())) ref=wave_ref>
</div>
}
}

View file

@ -1,38 +1,8 @@
.melt-wave {
position: relative;
}
.melt-wave::before {
content: "";
display: block;
position: absolute;
width: 100%;
height: 100%;
left: 0;
right: 0;
top: 0;
transition: 0.2s;
background: #fff;
opacity: 0;
}
.melt-wave:active::before {
opacity: 0.2;
}
.melt-wave::after {
content: "";
display: block;
position: absolute;
width: 200%;
height: 100%;
left: var(--x, 0);
top: var(--y, 0);
background-image: radial-gradient(circle, #fff 10%, transparent 10.01%);
background-repeat: no-repeat;
background-position: 50%;
transform: translate(-50%, -50%) scale(10);
opacity: 0;
transition: transform 0.8s, opacity 0.8s;
}
.melt-wave:active::after {
transform: translate(-50%, -50%) scale(0);
opacity: 0.3;
transition: 0s;
bottom: 0;
border-radius: inherit;
}