aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/trails/SlideEffect.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/trails/SlideEffect.tsx')
-rw-r--r--src/client/views/nodes/trails/SlideEffect.tsx371
1 files changed, 371 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..bad1fd8fa
--- /dev/null
+++ b/src/client/views/nodes/trails/SlideEffect.tsx
@@ -0,0 +1,371 @@
+import { useSpring, animated, easings, to, useInView } from '@react-spring/web';
+import React, { useEffect, useState } from 'react';
+import { PresEffect, PresEffectDirection } from './PresEnums';
+import './SlideEffect.scss';
+import { Doc } from '../../../../fields/Doc';
+import { NumCast } from '../../../../fields/Types';
+
+interface SlideEffectProps {
+ // pass in doc to extract width, height, bg
+ doc?: Doc;
+ dir: PresEffectDirection;
+ presEffect: PresEffect;
+ // stiffness (figma) = tension (react-spring)
+ tension: number;
+ // damping (figma) = friction (react-spring)
+ friction: number;
+ mass: number;
+ children: React.ReactNode;
+ infinite?: boolean;
+}
+
+const DEFAULT_WIDTH = 40;
+const PREVIEW_OFFSET = 60;
+const ACTUAL_OFFSET = 200;
+const infiniteOptions = {
+ loop: true,
+ delay: 500,
+};
+
+/**
+ * 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, friction, tension, mass, presEffect, children, infinite }: SlideEffectProps) {
+ const [springs, api] = useSpring(
+ () => ({
+ from: {
+ x: 0,
+ y: 0,
+ opacity: 0,
+ scale: 1,
+ },
+ config: {
+ tension,
+ friction,
+ mass,
+ },
+ onStart: () => {},
+ onRest: () => {},
+ }),
+ [tension, friction, mass]
+ );
+ const [ref, inView] = useInView({
+ once: true,
+ });
+
+ // Whether the animation is currently playing
+ const [animating, setAnimating] = useState(false);
+
+ const zoomConfig = {
+ from: {
+ scale: 0,
+ x: 0,
+ y: 0,
+ opacity: 1,
+ },
+ to: {
+ scale: 1,
+ x: 0,
+ y: 0,
+ opacity: 1,
+ config: {
+ tension: tension,
+ friction: friction,
+ mass: mass,
+ },
+ },
+ };
+
+ const fadeConfig = {
+ from: {
+ opacity: 0,
+ scale: 1,
+ x: 0,
+ y: 0,
+ },
+ to: {
+ opacity: 1,
+ scale: 1,
+ x: 0,
+ y: 0,
+ config: {
+ tension: tension,
+ friction: friction,
+ mass: mass,
+ },
+ },
+ };
+
+ const rotateConfig = {
+ from: {
+ x: 0,
+ },
+ to: {
+ x: 360,
+ config: {
+ tension: tension,
+ friction: friction,
+ mass: mass,
+ },
+ },
+ };
+
+ const getBounceConfigFrom = () => {
+ switch (dir) {
+ case PresEffectDirection.Left:
+ return {
+ from: {
+ opacity: 0,
+ x: infinite ? -PREVIEW_OFFSET : -ACTUAL_OFFSET,
+ y: 0,
+ },
+ };
+ case PresEffectDirection.Right:
+ return {
+ from: {
+ opacity: 0,
+ x: infinite ? PREVIEW_OFFSET : ACTUAL_OFFSET,
+ y: 0,
+ },
+ };
+ case PresEffectDirection.Top:
+ return {
+ from: {
+ opacity: 0,
+ x: 0,
+ y: infinite ? -PREVIEW_OFFSET : -ACTUAL_OFFSET,
+ },
+ };
+ case PresEffectDirection.Bottom:
+ return {
+ from: {
+ opacity: 0,
+ x: 0,
+ y: infinite ? PREVIEW_OFFSET : ACTUAL_OFFSET,
+ },
+ };
+ default:
+ // no movement for center
+ return {
+ from: {
+ opacity: 0,
+ x: 0,
+ y: 0,
+ },
+ };
+ }
+ };
+
+ const bounceConfig = {
+ ...getBounceConfigFrom(),
+ to: [
+ {
+ opacity: 1,
+ x: 0,
+ y: 0,
+ config: {
+ tension: tension,
+ friction: friction,
+ mass: mass,
+ },
+ },
+ ],
+ };
+
+ const flipConfig = {
+ from: {
+ x: 0,
+ },
+ to: {
+ x: 180,
+ config: {
+ tension: tension,
+ friction: friction,
+ mass: mass,
+ },
+ },
+ };
+
+ // only left and right for now
+ const getRollConfigFrom = () => {
+ switch (dir) {
+ case PresEffectDirection.Left:
+ return {
+ from: {
+ opacity: 0,
+ x: -100,
+ y: -120,
+ },
+ };
+ case PresEffectDirection.Right:
+ return {
+ from: {
+ opacity: 0,
+ x: 100,
+ y: 120,
+ },
+ };
+ case PresEffectDirection.Top:
+ return {
+ from: {
+ opacity: 0,
+ x: -100,
+ y: -120,
+ },
+ };
+ case PresEffectDirection.Bottom:
+ return {
+ from: {
+ opacity: 0,
+ x: -100,
+ y: -120,
+ },
+ };
+ default:
+ // no movement for center
+ return {
+ from: {
+ opacity: 0,
+ x: 0,
+ y: 0,
+ },
+ };
+ }
+ };
+
+ const rollConfig = {
+ ...getRollConfigFrom(),
+ to: {
+ opacity: 1,
+ x: 0,
+ y: 0,
+ config: {
+ tension: tension,
+ friction: friction,
+ mass: mass,
+ },
+ },
+ };
+
+ const lightspeedConfig = {
+ from: {
+ opacity: 0,
+ },
+ to: [],
+ };
+
+ // Switch animation depending on slide effect
+ const startAnimation = () => {
+ api.stop();
+ let config: any = zoomConfig;
+ switch (presEffect) {
+ case PresEffect.Bounce:
+ config = bounceConfig;
+ break;
+ case PresEffect.Zoom:
+ config = zoomConfig;
+ break;
+ case PresEffect.Rotate:
+ config = rotateConfig;
+ break;
+ case PresEffect.Fade:
+ config = fadeConfig;
+ break;
+ case PresEffect.Flip:
+ config = flipConfig;
+ break;
+ case PresEffect.Roll:
+ config = rollConfig;
+ break;
+ case PresEffect.Lightspeed:
+ break;
+ default:
+ break;
+ }
+
+ if (infinite) {
+ config = { ...config, ...infiniteOptions };
+ }
+
+ api.start(config);
+ };
+
+ const getRenderDoc = () => {
+ switch (presEffect) {
+ case PresEffect.Rotate:
+ return (
+ <animated.div ref={ref} style={{ transform: to(springs.x, val => `rotate(${val}deg)`) }}>
+ {children}
+ </animated.div>
+ );
+ case PresEffect.Flip:
+ return (
+ // Pass in doc dimensions
+ <div className="flip-container" ref={ref}>
+ {dir === PresEffectDirection.Bottom || dir === PresEffectDirection.Top ? (
+ <>
+ <animated.div
+ className={'flip-side flip-back'}
+ style={{
+ transform: to(springs.x, val => `perspective(600px) rotateX(${val}deg)`),
+ width: doc ? NumCast(doc.width) : DEFAULT_WIDTH,
+ height: doc ? NumCast(doc.height) : DEFAULT_WIDTH,
+ backgroundColor: infinite ? '#a825ff' : 'rgb(223, 223, 223);',
+ }}
+ />
+ <animated.div
+ className={'flip-side flip-front'}
+ style={{ transform: to(springs.x, val => `perspective(600px) rotateX(${val}deg)`), rotateX: '180deg', width: doc ? NumCast(doc.width) : DEFAULT_WIDTH, height: doc ? NumCast(doc.height) : DEFAULT_WIDTH }}>
+ {children}
+ </animated.div>
+ </>
+ ) : (
+ <>
+ <animated.div
+ className={'flip-side flip-back'}
+ style={{ transform: to(springs.x, val => `perspective(600px) rotateY(${val}deg)`), width: doc ? NumCast(doc.width) : DEFAULT_WIDTH, height: doc ? NumCast(doc.height) : DEFAULT_WIDTH }}
+ />
+ <animated.div
+ className={'flip-side flip-front'}
+ style={{ transform: to(springs.x, val => `perspective(600px) rotateY(${val}deg)`), rotateY: '180deg', width: doc ? NumCast(doc.width) : DEFAULT_WIDTH, height: doc ? NumCast(doc.height) : DEFAULT_WIDTH }}>
+ {children}
+ </animated.div>
+ </>
+ )}
+ </div>
+ );
+ case PresEffect.Roll:
+ return (
+ <animated.div ref={ref} style={{ opacity: springs.opacity, transform: to([springs.x, springs.y], (val, val2) => `translate3d(${val}%, 0, 0) rotate3d(0, 0, 1, ${val2}deg)`) }}>
+ {children}
+ </animated.div>
+ );
+ default:
+ return (
+ <animated.div
+ ref={ref}
+ style={{
+ ...springs,
+ }}>
+ {children}
+ </animated.div>
+ );
+ }
+ };
+
+ useEffect(() => {
+ if (infinite || !inView) return;
+ setTimeout(() => {
+ startAnimation();
+ }, 100);
+ }, [inView]);
+
+ useEffect(() => {
+ if (infinite) {
+ startAnimation();
+ }
+ }, [presEffect, tension, friction, mass]);
+
+ return <div>{getRenderDoc()}</div>;
+}