diff options
Diffstat (limited to 'src/client/views/nodes/trails/SlideEffect.tsx')
| -rw-r--r-- | src/client/views/nodes/trails/SlideEffect.tsx | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/src/client/views/nodes/trails/SlideEffect.tsx b/src/client/views/nodes/trails/SlideEffect.tsx new file mode 100644 index 000000000..00039e3cb --- /dev/null +++ b/src/client/views/nodes/trails/SlideEffect.tsx @@ -0,0 +1,120 @@ +/* eslint-disable react/require-default-props */ +import { animated, to, useInView, useSpring } from '@react-spring/web'; +import React, { useEffect } from 'react'; +import { Doc } from '../../../../fields/Doc'; +import { NumCast } from '../../../../fields/Types'; +import { PresEffect, PresEffectDirection } from './PresEnums'; +import './SlideEffect.scss'; +import { emptyFunction } from '../../../../Utils'; + +interface SlideEffectProps { + doc?: Doc; // pass in doc to extract width, height, bg + dir: PresEffectDirection; + presEffect: PresEffect; + springSettings: { + stiffness: number; + damping: number; + mass: number; + }; + children: React.ReactNode; + infinite?: boolean; + startOpacity?: number; // set to zero to linearly fade in while animating +} + +const DEFAULT_WIDTH = 40; +const PREVIEW_OFFSET = 60; +const ACTUAL_OFFSET = 200; + +/** + * This component wraps around the doc to create an effect animation, and also wraps the preview animations + * for the effects as well. + */ +export default function SpringAnimation({ doc, dir, springSettings, presEffect, children, infinite, startOpacity }: SlideEffectProps) { + const expandConfig = { + to: { scale: 1, x: 0, y: 0 }, + from: { scale: 0, x: 0, y: 0 }, + }; + const fadeConfig = { + to: { x: 0, y: 0 }, + from: { x: 0, y: 0 }, + }; + const rotateConfig = { + to: { x: 360, y: 0 }, + from: { x: 0, y: 0 }, + }; + const flipConfig = { + to: { x: 180, y: 0 }, + from: { x: 0, y: 0 }, + }; + const bounceConfig = { + to: { x: 0, y: 0 }, + from: (() => { + const offset = infinite ? PREVIEW_OFFSET : ACTUAL_OFFSET; + switch (dir) { + case PresEffectDirection.Left: return { x: -offset, y: 0, }; + case PresEffectDirection.Right: return { x: offset, y: 0, }; + case PresEffectDirection.Top: return { x: 0, y: -offset, }; + case PresEffectDirection.Bottom:return { x: 0, y: offset, }; + default: return { x: 0, y: 0, }; // no movement for center + }})(), // prettier-ignore + }; + const rollConfig = { + to: { x: 0, y: 0 }, + from: (() => { + switch (dir) { + case PresEffectDirection.Left: return { x: -100, y: -120, }; + case PresEffectDirection.Right: return { x: 100, y: 120, }; + case PresEffectDirection.Top: return { x: -100, y: -120, }; + case PresEffectDirection.Bottom: return { x: -100, y: -120, }; + default: return { x: 0, y: 0, }; // no movement for center + }})(), // prettier-ignore + }; + + // prettier-ignore + const effectConfig = (() => { + switch (presEffect) { + case PresEffect.Fade: return fadeConfig; + case PresEffect.Bounce: return bounceConfig; + case PresEffect.Rotate: return rotateConfig; + case PresEffect.Flip: return flipConfig; + case PresEffect.Roll: return rollConfig; + case PresEffect.Lightspeed: return { from: {}, to: {} }; + case PresEffect.Expand: + default: return expandConfig; + } // prettier-ignore + })(); + + const [springs, api] = useSpring( + () => ({ + to: { ...effectConfig.to, opacity: 1 }, + from: { ...effectConfig.from, opacity: startOpacity ?? 1 }, + config: { tension: springSettings.stiffness, friction: springSettings.damping, mass: springSettings.mass }, + onStart: emptyFunction, + onRest: emptyFunction, + }), + [springSettings] + ); + + const [ref, inView] = useInView({ + once: true, + }); + useEffect(() => { + if (inView) { + api.start({ loop: infinite, delay: infinite ? 500 : 0 }); + } + }, [inView]); + const animatedDiv = (style: any) => ( + <animated.div ref={ref} style={{ ...style, opacity: to(springs.opacity, val => `${val}`) }}> + {children} + </animated.div> + ); + const [width, height] = [NumCast(doc?.width, DEFAULT_WIDTH), NumCast(doc?.height, DEFAULT_WIDTH)]; + const flipAxis = dir === PresEffectDirection.Bottom || dir === PresEffectDirection.Top ? 'X' : 'Y'; + const [rotateX, rotateY] = flipAxis === 'X' ? ['180deg', undefined] : [undefined, '180deg']; + switch (presEffect) { + case PresEffect.Flip: return animatedDiv({ transform: to(springs.x, val => `perspective(600px) rotate${flipAxis}(${val}deg)`), width, height, rotateX, rotateY }) + case PresEffect.Rotate:return animatedDiv({ transform: to(springs.x, val => `rotate(${val}deg)`) }); + case PresEffect.Roll: return animatedDiv({ transform: to([springs.x, springs.y], (val, val2) => `translate3d(${val}%, 0, 0) rotate3d(0, 0, 1, ${val2}deg)`) }); + default: return animatedDiv(springs); + } // prettier-ignore +} |
