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.tsx120
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
+}