Багато з нас бачили анімацію хвильового ефекту, яка була частиною Material Design. Хвиля являє собою коло, яке з'являється в точці клацання, а потім збільшується і зникає. Як інструмент для користувача інтерфейсу, це знайомий спосіб повідомити користувачеві, що сталося клацання.
В React найпростіший спосіб - використовувати Material-UI, який є популярною UI бібліотекою. Загалом, це дуже хороша ідея, якщо тобі потрібна солідна UI бібліотека, яка генерує UI з коробки. Однак, для невеликого проекту не має сенсу вчитися працювати з великою бібліотекою тільки для досягнення одного ефекту.
Я переглянув безліч проектів, що реалізують щось подібне в Github, Codepen і Codesandbox, і черпав натхнення в деяких з кращих. Хвильовий можливий на будь-який веб-платформі, так як він досягається за допомогою CSS.
Ось як виглядає моя імплементація CSS.
<button class = "button" >
<div class = "ripple-container" >
<span class = "ripple" > </ span>
</ Div>
</ Button>
.button {
overflow : hidden ;
position : relative ;
}
.ripple-container {
top : 0 ;
left : 0 ;
right : 0 ;
bottom : 0 ;
z-index : 0 ;
overflow : hidden ;
position : absolute ;
}
.ripple-container span {
position : absolute ;
top : . . . ;
left : . . . ;
height : . . . ;
width : . . . ;
transform : scale ( 0 ) ;
border-radius : 100% ;
opacity : 0. 25 ;
background-color : #fff ;
animation-name : ripple ;
animation-duration : 850ms ;
}
@keyframes ripple {
to {
opacity : 0 ;
transform : scale ( 2 ) ;
}
}
Властивість overflow: hidden запобігає виходу хвилі з контейнера. Брижі (хвилі) - це коло ( border-radius: 100% ), яке починається з невеликого розміру і стає більше по мірі зникнення. Зростаюча і зникає анімація досягається шляхом маніпулювання transform: scale і opacity .
Однак, нам потрібно буде динамічно надати кілька стилів з використанням Javascript. Знайти позиційні координати: тобто top і left, які засновані на тому, де користувач клацнув, а також height і width, які залежать від розміру контейнера.
Почнемо
Для своїх стилів мені зручно використовувати стильові компоненти. Ти ж можеш використовувати те, що вважаєш за краще. Перше, що ми зробимо - додамо вищевказаний CSS в наші компоненти. Стилі будуть знаходитися в окремому файлі.
// file => styles.ts
import styled from "styled-components" ;
export const Button = styled . button `
position : relative ;
padding : 5px 30px ;
overflow : hidden ;
cursor : pointer ;
background - color : $ { ( props ) = > ( props . color ? props . color : "tomato" ) } ;
color : #fff ;
font - size : 20px ;
border - radius : 20px ;
border : 1px solid #fff ;
text - align : center ;
box - shadow : 0 0 5px rgba ( 0 , 0 , 0 , 0.4 ) ;
box - sizing : border - box ;
` ;
export const RippleContainer = styled . div `
top : 0 ;
left : 0 ;
right : 0 ;
bottom : 0 ;
z - index : 0 ;
overflow : hidden ;
position : absolute ;
border - radius : inherit ;
span {
position : absolute ;
transform : scale ( 0 ) ;
border - radius : 100 % ;
opacity : 0.25 ;
background - color : $ { ( props ) = > props . color } ;
animation - name : ripple ;
animation - duration : $ { ( props ) = > props . duration } ms ;
}
@ Keyframes ripple {
to {
opacity : 0 ;
transform : scale ( 2 ) ;
}
}
` ;
Зверни увагу, що для кнопки background-color залежить від props color самого стильового компонента, але якщо він не буде переданий, то за замовчуванням буде фон tomato. Для контейнера теж саме. Плюс тривалість анімації залежить від props . Це зроблено для того, щоб ми могли динамічно встановлювати ці значення пізніше. Давай визначимо їх зараз:
import React from 'react'
. . .
export function Ripple ( { duration = 850 , color = "#ffffff" } ) {
. . .
}
Далі для нашої брижі ми визначаємо масив і створюємо функцію для додавання мерехтіння в цей масив. Кожен елемент масиву буде об'єктом з властивостями x, y і size, які є інформацією, необхідною для стилізації хвилі. Щоб обчислити ці значення, ми винесемо їх з події mousedown.
export function Ripple ( { duration = 850 , color = '#ffffff' } ) {
const [ rippleArray , setRippleArray ] = React . useState ( [ ] ) ;
const addRipple = ( event ) = > {
// Координати контейнера
const rippleContainer = event . currentTarget . getBoundingClientRect ( ) ;
// Вибираємо найдовшу сторону
const size =
rippleContainer . width > rippleContainer . height
? rippleContainer . width
: RippleContainer . height ;
// Координати клацання миші
const x = event . pageX - rippleContainer . left - size / 2 ;
const y = event . pageY - rippleContainer . top - size / 2 ;
// Нова хвиля
const newRipple = {
x ,
y ,
size
} ;
setRippleArray ( [ . . . rippleArray , newRipple ] ) ;
} ;
return (
. . .
) ;
}
Наведений вище код використовує API браузера getBoundClientRect(), який дозволяє нам отримати найдовший край контейнера і координати x (left) і y (top), щодо документа. Разом з MouseEvent.pageX і MouseEvent.pageY він дозволяє нам обчислювати координати x і y миші (клацання) щодо контейнера.
Тепер можемо візуалізувати масив хвилі (брижі).
return (
< RippleContainer duration = { duration } color = { color } onMouseDown = { addRipple } >
{ RippleArray . length & &
rippleArray . map ( ( ripple , index ) = > {
return (
< span
key = { "span" + index }
style = { {
top : ripple . y ,
left : ripple . x ,
width : ripple . size ,
height : ripple . size ,
} }
/ >
) ;
} ) }
< / RippleContainer >
) ;
RippleContainer - це стилізований компонент, який приймає duration і color як props, разом з addRipple в якості обробника події onMouseDown. Усередині нього ми відображаємо всі наші хвилі і додаємо розраховані параметри top, left, width і height .
З цим ми закінчили. Тим не менш, є ще одна маленька річ, яку потрібно зробити, а саме: очистити брижі після того, як вони зробили анімацію. Це зроблено для того, щоб застарілі елементи не захаращували DOM.
Нижче приведена реалізація і використання useEffect хука. По суті, кожен раз, коли ми створюємо нову хвилю, таймер скидається. Зверни увагу, що тривалість тайм-ауту набагато більше, ніж наша брижа.
React . useEffect ( ( ) = > {
let bounce ;
if ( rippleArray . length > 0 ) {
window . clearTimeout ( bounce ) ;
bounce = window . setTimeout ( ( ) = > {
setRippleArray ( [ ] ) ;
window . clearTimeout ( bounce ) ;
} , Duration * 4 ) ;
}
return ( ) = > window . clearTimeout ( bounce ) ;
} , [ RippleArray . length , duration ] ) ;
Тепер ми закінчили з нашим компонентом Ripple. Давай створимо кнопки.
import React from "react" ;
import "./App.css" ;
import { Ripple } from "./RippleButton" ;
import { Button } from "./RippleButton/styles" ;
function App ( ) {
return (
<div className = "App" >
<header className = "App-header" >
<Button>
Learn React
<Ripple duration = { 850 } color = "#fff" />
</ Button>
<Button>
Learn React
<Ripple duration = { 1000 } color = "#fff" />
</ Button>
<Button>
Learn React
<Ripple duration = { 2000 } color = "#fff" />
</ Button>
</ Header>
</ Div>
) ;
}
export default App;
Тепер у нас є брижі у всіх відтінках і швидкостях. Більш того, наш компонент Ripple, якщо він має overflow: hidden і position: relative в своїх стилях, може бути повторно використаний практично в будь-якому контейнері.
0 комментариев
Добавить комментарий