The descent-ripple package provides an easy way to add ripple animations to buttons and button-like UI elements and react-descent-ripple is a conveniently wrapped component. That means when you click on a button, or whatever UI-component you add this effect to, a short animation will start at the position of the click, which is bound by the borders of the button. This looks nice and also gives the user the feel of a responding UI. It is especially helpful for touch interfaces since it gives the user some feedback about the click position.
Getting started
Usage with React is easy. The package is written using Svelte and is already compiled and minimized, so there is no overhead or requirement.
You can import the library using:
npm install react-descent-ripple
Then, you can add the ripple:
// MyButton.tsx or MyButton.jsx import React from 'react'; import Ripple from 'react-descent-ripple'; export const MyButton = ()=>{ return ( <button> <Ripple/> click me </button> ) }
That's all you need to add the effect and all the examples below just adjust the ripple props.
>The ripple function usually adds the needed CSS to your button. If the result is not well-positioned, it could be that your framework is overriding the style property of your button/component. Try to manually add "position:relative" to the style of your button, if the ripple is not in place, and "overflow:hidden", if the ripple draws out of boundaries.
Options
nLines and nCircles
The ripple animates lines and/or circles. With
you say, how many lines you want. Lines are spaced equally around the center. You can have both circles and lines. More than one circle will make sense later when we know how to use delay.
nLines
nLines={3}
nCircles={0}
lineProps and circleProps
Lines and circles are svg-path and svg-circle elements. Colors are defined by the
and stroke
property. Other interesting attributes, especially for the lines, are fill
, stroke-linecap
, stroke-linejoin
, stroke-miterlimit
, stroke-opacity
. You can apply the same props to all circles and lines, or handle each individually. To do that, supply a function of the index:
stroke-width
nLines={6}
nCircles={1}
circleProps={{!p!}}
lineProps={(i) => {
fill: 'none',
stroke: 'hsl(' + ( 12 * i ) + ',90%,50%)',
"stroke-width": 40
}}
lineBreakDist and rotation
Each path is split into several segments via lineBreakDist. A larger distance means fewer breaks. With
, you can make the lines bend circularly. You can supply a single value or a function on the line-break index. rotation
is measured in degrees.
rotation
nLines={6}
nCircles={0}
lineProps={(i) => {
fill: 'none',
stroke: (i % 2)===0 ? 'black' : 'white',
"stroke-width": 40
}}
rotation={(i)=>10 * i}
lineBreakDist={10}
Animation and AnimationProps
By default descent-ripple supports the following animation types from Svelte:
, "draw"
, "scale"
, "fade"
and "blur"
plus "fly"
. All these animations can take props, which typically include "draw-reverse"
, you can look them up on Svelte. {delay:number, duration:number, easing: (t:number)=>number}
accepts the same props as "draw-reverse"
. You can change in and out animations for circles and lines and the props can be passed as a function of the circle/line index.
"draw"
nLines={4}
nCircles={0}
lineProps={(i) => {
fill: 'none',
stroke: (i % 2)===0 ? 'black' : 'white',
"stroke-width": 60
}}
rotation={(i)=>0 * i}
lineBreakDist={0}
linesOutAnimation={'draw-reverse'}
linesOutAnimationProps={(i) => ({
duration: Math.max(20, 700 - 120 * i),
delay: 120 * i
})}
nLines={0}
nCircles={4}
circleProps={{!p!}}
circleOutAnimation={'scale'}
circleInAnimationProps={(i) => ({
duration: Math.max(20, 500 - 100 * i),
delay: 100 * i
})}
circleOutAnimationProps={(i) => ({
duration: Math.max(20, 900 - 100 * i),
delay: 100 * i
})}
Randomness and timeToRemove
With the
option you can distort the line-forming randomly. This can add a more interesting look. Also, you can adjust the time point when the out animation should start with randomness
. Default is 500 milliseconds.
timeToRemove
nLines={10}
nCircles={1}
circleProps={{!p!}}
lineProps={(i) => {
fill: 'none',
stroke: 'red'
"stroke-width": 10
}}
rotation={(i)=>0 * i}
timeToRemove={900}
randomness={1.5}
lineBreakDist={20}
linesInAnimationProps={(i) => ({
duration:800,
easing: t=>t
})}
linesOutAnimationProps={(i) => ({
duration: Math.max(20, 700 - 120 * i),
delay: 120 * i
})}
circleInAnimationProps={(i) => ({
duration: Math.max(20, 500 - 100 * i),
delay: 100 * i
})}
circleOutAnimationProps={(i) => ({
duration: Math.max(20, 900 - 100 * i),
delay: 100 * i
})}
backstep
With the
option you can increase the edginess of the lines. It should usually be between 0 and 1. Glass UI designs are trendy right now, how about a cracking effect?
backstep
nLines={11}
nCircles={0}
lineProps={(i) => {
fill: 'none',
stroke: 'black'
"stroke-width": 2
}}
rotation={(i)=>0 * i}
timeToRemove={900}
randomness={1.5}
lineBreakDist={10}
backstep={0.3}
linesInAnimationProps={(i) => ({
duration:800,
easing: crackEasing()
})}
Here is the code for
:
crackEasing
const crackEasing = () => { const n = 3; const steps = Array(n).fill().map(() => 0.1 + Math.random()); const stepsLength = steps.reduce((p,v)=>p+v,0); const findheight = (t)=>{ if (t < .05){ return t / .05; } let p = 0, q = 0; for (let i = 0; i < n; i++){ q += steps[i] / stepsLength p += (i+1); if (t < q - .05){ return p; } if (t < q){ return p + (1 - (q-t)/.05)*(i+2) ; } } return (n+3)*(n+2)/2; } return (t) => 2 * findheight(t) / ((n+3)*(n+2)); };
Custom animation
The in and out animations also allow passing a custom animation. You can get some information about this in the Svelte tutorial. Javascript animations also work, use the
.
tick
nLines={20}
nCircles={0}
lineProps={(i) => {
fill: 'none',
stroke: (i % 2)===0 ? 'black' : 'white',
"stroke-width": 1
}}
rotation={(i)=>0 * i}
timeToRemove={400}
randomness={2.5}
lineBreakDist={30}
backstep={0.03}
linesInAnimation={electroshock}
linesOutAnimation={electroshock}
linesInAnimationProps={(i) => ({
duration:400,
easing: t => Math.random()
})}
linesOutAnimationProps={(i) => ({
duration: Math.max(20, 700 - 120 * i),
delay: 120 * i
})}
This is the custom animation:
const electroshock = (node, { duration, delay, easing }) => { return { duration, delay: delay ? delay : 0, easing: easing ? easing : (t) => t, css: (t) => { return ` stroke: rgba( ${Math.round(t * 255)}, ${Math.round(t * 255)}, ${Math.round(t * Math.random() * 255)}, ${t} );`; }, }; };
Conclusion
There are a ton of other possiblities to style the ripple effect. Feel free to use it on your page and if you like (fully optional) you can send me a link to ripple@gradientdescent.de. Also let me know, if you have improvement ideas via github. Thanks!
Share this article on twitter - Vote on next topics - Support me
This might also be interesting for you:
- How to Implement Custom React Hooks in Svelte
- React Hooks in Svelte
- tree-shake-css - project page
- Sveltes tick is my new best friend - porting react-textfit to Svelte
- How to merge cells with svelte-window
- Porting React to Svelte - From react-window 1:1 to svelte-window
- Poll: Which React library would you love to see in Svelte?
- Porting React components to Svelte - It might be easier than you think
- The Descent Ripple Effect - Showcase
- Svelte and Typescript
- How this blog is made - Basic Structure, Elder and Markdown
- How this blog is made part 2 - Make your website sketchy with Tailwind and Roughjs
- Why are component libraries so complicated with React compared to Svelte?