React-Spring-2 useTransition() update style, useGesture() drag, SVG display and useSpring() animation
The useTrasition() style props can be rendered only in an animated.div component.
The Enter and Leave prop only animates the useTransition() elements being added/removed to the DOM, both will be rendered when the element is .
//useTransition() leaves-> enter on useState() change
//exitBeforeEnter finishes the leave before the enter
const [mio, setMio] = useState(["another"])
const estilo = useTransition(mio, {
from: {
back: "transparent", long: "0%", lefto: "0%",
color: "black", opacity: 0, width: "20%"
},
enter: [
{ back: "pink", color: "green", long: "90%", lefto: "10%", width: "100%", opacity: 1 },
{ back: "orange", color: "blue", long: "50%", lefto: "50%" },
{ back: "red", color: "red", long: "0%", lefto: "100%" }
],
leave: [
{ width: "20%" ,opacity: 0 }
],
exitBeforeEnter: true,
config:{ duration: 1000 }
})
//We deconstruct and ...rest the custom style properties
//We get the translate--centered-text effect animating width/text-center
<>
{estilo(( {back, long, lefto, ...resto} , item) => (
<animated.div className="centra d-flex align-items-center" >
<animated.div
className="sfondo"
style={{backgroundColor: back, width: long, left: lefto}}
>
</animated.div>
<animated.div className="testo" style={resto}>
{item}
</animated.div>
</animated.div>
))}
</>
The update useTransition() styles the elements not updated during the animation but doesn't trigger if the array remains the same.
//update() if added styles after the leave{} any remaining elements
//Any new transitions need to be included in the from:{} even at default 0
const transitions = useTransition(testi, {
from: { transform: 'perspective(600px) rotateX(0deg) rotateX(0deg)'},
enter: { transform: 'perspective(600px) rotateX(180deg) translateX(0px)' },
leave: { },
update: [
{ color: 'pink' },
{ transform: "perspective(600px) rotateX(0deg) translateX(150px)" }
]
})

The exitBeforeEnter property first animates the Leave element, then removes it and renders the Enter element in the same position.
The expires property animates and doesn't remove the Leave element while rendering the enter one.
let animato = useTransition(element, {
from: {},
expires: false,
exitBeforeEnter: true,
}
The useTransition() events can be linked to a specific styler property.
//onStart() triggers at the start of each frame.
//onChange() on each pixel of animation
//onRest(), onPause(), and onResume() for paused/resumed animations
//onProps() triggers on updates by new props, even if it remains idle
//onDestroyed() when the element is un-mounted.
//The TO property renders the target prop, not the current style.
let animato = useTransition(element, {
from:{},
onStart: {
color: (x) => {console.log( "triggers on color" )},
backgroundColor: (x) => {console.log( "back is " + (x.value.animation.to) )}
},
onProps: () => {console.log("will cover each updated property")}
}
React use-gesture and useSpring()
We npm install @use-gesture/react, a library that binds mouse and touch events to animate useSpring() NODE elements.
//We extract the useDrag() hook to animate the useSpring() and useState()
import { useDrag } from '@use-gesture/react'
const [{x, dietro}, api] = useSpring(()=>({
x: 0,
dietro: "brown"
}))
let [mosso, setMosso] = useState(0)
The useDrag() returns an object that applies event handlers to animated.div components. Its parameters are the drag action and the movement coordinates, and its gesture data animates the useSpring() style properties.
//The useDrag() includes the onPointerUp, onPointerDown, and onPointerMove events.
//active/down event is not a variable, the movement's array is [X-axis, Y-axis]
//We need the immediate property for a smooth property transition later
const muovi = useDrag( ({active, movement: [mx]}) =>(
api.start({
x: active ? mx : 0,
dietro: active ? "orange" : "brown",
immediate: name => active && name === "mx"
}),
setMosso( Math.abs(mx) )
))
//We set a variable based on the useDrag() updated X-style property
//The map property is a filter function for the input value
//The extrapolate property used for the range/output breakpoints
let kolor = x.to({
map: Math.abs, //we absolute the negative x coordinates
range: [10, 300],
output: [1, 0.2],
extrapolate: "clamp" //extends, extrapolate, extrapolateLeft, or extrapolateRight
})
We spread the useDrag() variable in the animated.div element we want to interact with.
//The useState()/style props are updated on useDrag() movement
<div className="d-block">
<animated.div className="backo" style={{ backgroundColor: dietro }}>
</animated.div>
<div className="d-flex justify-content-center mt-3">
<animated.div className="boxo" {...muovi()} style={{ x, opacity: kolor }} >
</animated.div>
</div>
<animated.p className="text-center">
We moved it by {Math.floor(mosso/13)} em
</animated.p>
</div>


The useDrag() state attribute velocity matches the drag speed of the element, the config.decay triggers the momentum at the drag stop.
The useDrag() state attribute movement records the gesture change of position relative to its starter useSpring(), which gets reset at the end of useDrag()
//decay:false returns the element to its useSpring(), no momentum tho
const [{x1, y1}, api1] = useSpring(()=>({
x1: 100, y1: 0,
}))
let pinto = useDrag(({ down, movement: [mx, my], velocity, direction}) =>{
api1.start({
x1: down ? mx: 100, y1: down ? my : 0,
config: { velocity , decay: false },
immediate: down,
})
})
<div>
<animated.div className="rocket" {...pinto()}
style={{ x: x1, y: y1}}
/>
</div>

The arcTangent (Math.atan2) of the coordinates returns the current rotation position.
//We keep the position but no momentum
const [{x, y, transform}, apri] = useSpring(()=> ({
x: 0, y: 0,
transform: "rotate(0rad)",
}))
const bind4 = useDrag(({ down, offset: [x, y], velocity, direction }) => {
apri.start({
x, y,
transform: `rotate(${ Math.atan2(direction[0], -direction[1]) }rad)`,
immediate: down,
config: { velocity, decay: false },
})
})
<div>
<animated.div className="rocket" {...bind4()}
style= {{ x, y, transform }}
/>
</div>
Waving transitions with animated SVG filters and useSpring()
SVG, short for Scalable Vector Graphics, uses the viewBox attribute to define a visible window area within the SVG element.
The viewBox can scale or pan its element proportionally to the container size (while maintaining its aspect ratio).
//The zoom depends on the proportion between the container and the viewBox
//50/100 = 0.5x scale on the SVG, while 50/25 = 2x scale.
<div>
<svg width="100" height="100">
<ellipse fill="orange" cx="50" cy="50" rx="50" ry="50"></ellipse>
</svg>
<svg width="50" height="50">
<ellipse fill="orange" cx="50" cy="50" rx="50" ry="50"></ellipse>
</svg>
<svg width="50" height="50" viewBox='0 0 100 100'>
<ellipse fill="orange" cx="50" cy="50" rx="50" ry="50"></ellipse>
</svg>
<svg width="50" height="50" viewBox='0 0 25 25'>
<ellipse fill="orange" cx="50" cy="50" rx="50" ry="50"></ellipse>
</svg>
</div>

The <filter> tag contains the filter primitive elements, used to create svg visual effects. The <g> tag can render multiple svg elements, its d(ata) attribute defines the path and shape of the element.
We animate the useSpring() between its starting/ending state with the useState() reverse property and dependency. We use the React-spring animated() function to create animatable filter primitive components.
To animate the SVG wave effect, we use the baseFrequency attribute for its frequency and direction (X/Y values) and the scale (factor) for the strength of the displacement effect.
//The filter primitives <feTurbulence> and <feDisplacementMap> create the wave effect
//We can use animated.feDisplacementMap
//A +/- factor changes the direction of the waves
//frequency is 0 at the ending state to not have a distorted image
const AnimFeTurbulence = animated('feTurbulence')
const AnimFeDisplacementMap = animated('feDisplacementMap')
const [aperto, setAperto] = useState(false)
const [{ freq, factor, scale, opacity }] = useSpring(() => ({
reverse: aperto,
from: { factor: 10, opacity: 0, scale: 0.9, freq: '0.0, 0.025' },
to: { factor: 150, opacity: 1, scale: 1, freq: '0.0, 0.0' },
config: { duration: 3000 },
}),
[aperto]
)
<div onClick={() => setAperto(!aperto)}>
<animated.svg className="svg" style={{ scale, opacity }} viewBox="0 0 850 480">
<div>
<filter id="acqua">
<AnimFeTurbulence type="fractalNoise" baseFrequency={freq} />
<AnimFeDisplacementMap in="SourceGraphic" scale={factor} />
</filter>
</div>
<g filter="url(#acqua)" >
<path fill="orange" d="svg-path..."></path>
</g>
</animated.svg>
</div>

Last updated
Was this helpful?