From 8ccc55cfdd4ffc868ccf8f8f92fea67697bcaf78 Mon Sep 17 00:00:00 2001 From: Sophie Zhang Date: Thu, 21 Mar 2024 02:18:33 -0400 Subject: more springs --- src/client/views/nodes/DocumentView.tsx | 24 ++- src/client/views/nodes/trails/PresBox.tsx | 173 ++++++++++++++++++++- src/client/views/nodes/trails/SlideEffect.tsx | 55 ++++++- .../views/nodes/trails/SlideEffectPreview.tsx | 134 ++++++++++++++++ 4 files changed, 373 insertions(+), 13 deletions(-) create mode 100644 src/client/views/nodes/trails/SlideEffectPreview.tsx (limited to 'src') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 3c1896474..92f66f2cd 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -47,7 +47,7 @@ import { FieldViewProps, FieldViewSharedProps } from './FieldView'; import { KeyValueBox } from './KeyValueBox'; import { LinkAnchorBox } from './LinkAnchorBox'; import { FormattedTextBox } from './formattedText/FormattedTextBox'; -import { PresEffect, PresEffectDirection } from './trails'; +import { PresEffect, PresEffectDirection, SpringSettings, SpringType } from './trails'; import SlideEffect, { EffectType } from './trails/SlideEffect'; interface Window { MediaRecorder: MediaRecorder; @@ -996,6 +996,7 @@ export class DocumentViewInternal extends DocComponent, root: Doc) { const dir = presEffectDoc?.presentation_effectDirection ?? presEffectDoc?.followLinkAnimDirection; + console.log(dir); const effectProps = { left: dir === PresEffectDirection.Left, right: dir === PresEffectDirection.Right, @@ -1005,19 +1006,32 @@ export class DocumentViewInternal extends DocComponent{renderDoc} + case PresEffect.Zoom: return {renderDoc} case PresEffect.Fade: return {renderDoc}; case PresEffect.Flip: return {renderDoc}; case PresEffect.Rotate: return {renderDoc}; - case PresEffect.Bounce: return {renderDoc} + case PresEffect.Bounce: return {renderDoc} case PresEffect.Roll: return {renderDoc}; case PresEffect.Lightspeed: return {renderDoc}; } diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index d330c8157..deb59d03f 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -46,6 +46,7 @@ import { DictationManager } from '../../../util/DictationManager'; import CubicBezierEditor, { TIMING_DEFAULT_MAPPINGS } from './CubicBezierEditor'; import Slider from '@mui/material/Slider'; import { FaArrowDown, FaArrowLeft, FaArrowRight, FaArrowUp, FaCompressArrowsAlt } from 'react-icons/fa'; +import SpringAnimationPreview from './SlideEffectPreview'; export interface pinDataTypes { scrollable?: boolean; @@ -74,6 +75,23 @@ export interface PinProps { pinData?: pinDataTypes; } +// the type of slide effect timing (spring-driven) +export enum SpringType { + DEFAULT = 'default', + GENTLE = 'gentle', + BOUNCY = 'bouncy', + CUSTOM = 'custom', + QUICK = 'quick', +} + +// settings that control slide effect spring settings +export interface SpringSettings { + type: SpringType; + stiffness: number; + damping: number; + mass: number; +} + const easeItems = [ { text: 'Ease', @@ -116,6 +134,59 @@ const effectItems = Object.values(PresEffect) val: effect, })); +const effectTimings = [ + { text: 'Default', val: SpringType.DEFAULT }, + { + text: 'Gentle', + val: SpringType.GENTLE, + }, + { + text: 'Quick', + val: SpringType.QUICK, + }, + { + text: 'Bouncy', + val: SpringType.BOUNCY, + }, + { + text: 'Custom', + val: SpringType.CUSTOM, + }, +]; + +const springMappings: { + [key: string]: { stiffness: number; damping: number; mass: number }; +} = { + default: { + // stiffness: 300, + // damping: 12, + // mass: 2, + stiffness: 600, + damping: 15, + mass: 1, + }, + gentle: { + stiffness: 100, + damping: 15, + mass: 1, + }, + quick: { + stiffness: 300, + damping: 20, + mass: 1, + }, + bouncy: { + stiffness: 600, + damping: 15, + mass: 1, + }, + custom: { + stiffness: 100, + damping: 10, + mass: 1, + }, +}; + @observer export class PresBox extends ViewBoxBaseComponent() { public static LayoutString(fieldKey: string) { @@ -1689,6 +1760,12 @@ export class PresBox extends ViewBoxBaseComponent() { @undoBatch updateEffect = (effect: PresEffect, bullet: boolean, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (bullet ? (doc.presBulletEffect = effect) : (doc.presentation_effect = effect))); + @undoBatch + updateEffectTiming = (activeItem: Doc, timing: SpringSettings) => { + activeItem.presEffectTiming = JSON.stringify(timing); + this.selectedArray.forEach(doc => (doc.presEffectTiming = activeItem.presEffectTiming)); + }; + static _sliderBatch: any; static endBatch = () => { PresBox._sliderBatch.end(); @@ -1935,6 +2012,25 @@ export class PresBox extends ViewBoxBaseComponent() { @computed get transitionDropdown() { const activeItem = this.activeItem; + // Retrieving spring timing properties + let timing = StrCast(activeItem.presEffectTiming); + let timingConfig: SpringSettings | undefined; + if (timing) { + timingConfig = JSON.parse(timing); + } + + if (!timingConfig) { + timingConfig = { + type: SpringType.DEFAULT, + // stiffness: 300, + // damping: 12, + // mass: 2, + stiffness: 600, + damping: 15, + mass: 1, + }; + } + const preseEffect = (effect: PresEffect) => (
() {
Slow
{/* Easing function */} -
+
() {
-
+
Effects () { {presDirection(PresEffectDirection.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
*/} {/* Effect spring settings */} + { + console.log('effect timing', val); + this.updateEffectTiming(activeItem, { + type: val as SpringType, + ...springMappings[val], + }); + }} + dropdownType={DropdownType.SELECT} + type={Type.TERT} + /> +
Tension
+
{ + e.stopPropagation(); + }}> + { + if (!timingConfig) return; + this.updateEffectTiming(activeItem, { ...timingConfig, stiffness: val as number }); + }} + valueLabelDisplay="auto" + /> +
+
Damping
+
{ + e.stopPropagation(); + }}> + { + if (!timingConfig) return; + this.updateEffectTiming(activeItem, { ...timingConfig, damping: val as number }); + }} + valueLabelDisplay="auto" + /> +
+
Mass
+
{ + e.stopPropagation(); + }}> + { + if (!timingConfig) return; + this.updateEffectTiming(activeItem, { ...timingConfig, mass: val as number }); + }} + valueLabelDisplay="auto" + /> +
+ +
+
this.applyTo(this.childDocs)}> diff --git a/src/client/views/nodes/trails/SlideEffect.tsx b/src/client/views/nodes/trails/SlideEffect.tsx index 918ea93bd..ae25bfb90 100644 --- a/src/client/views/nodes/trails/SlideEffect.tsx +++ b/src/client/views/nodes/trails/SlideEffect.tsx @@ -1,6 +1,7 @@ import { Button } from '@mui/material'; import { useSpring, animated, easings } from '@react-spring/web'; import React, { useEffect, useState } from 'react'; +import { PresEffectDirection } from './PresEnums'; export enum EffectType { ZOOM = 'zoom', @@ -9,6 +10,7 @@ export enum EffectType { } interface Props { + dir: PresEffectDirection; effectType: EffectType; friction: number; tension: number; @@ -16,7 +18,7 @@ interface Props { children: React.ReactNode; } -export default function SpringAnimation({ friction, tension, mass, effectType, children }: Props) { +export default function SpringAnimation({ dir, friction, tension, mass, effectType, children }: Props) { const [springs, api] = useSpring( () => ({ from: { @@ -63,12 +65,53 @@ export default function SpringAnimation({ friction, tension, mass, effectType, c }, }; + const getBounceConfigFrom = () => { + switch (dir) { + case PresEffectDirection.Left: + return { + from: { + opacity: 0, + x: -200, + y: 0, + }, + }; + case PresEffectDirection.Right: + return { + from: { + opacity: 0, + x: 200, + y: 0, + }, + }; + case PresEffectDirection.Top: + return { + from: { + opacity: 0, + x: 0, + y: -200, + }, + }; + case PresEffectDirection.Bottom: + return { + from: { + opacity: 0, + x: 0, + y: 200, + }, + }; + default: + return { + from: { + opacity: 0, + x: 0, + y: 0, + }, + }; + } + }; + const bounceConfig = { - from: { - opacity: 0, - x: -200, - y: 0, - }, + ...getBounceConfigFrom(), to: [ { opacity: 1, diff --git a/src/client/views/nodes/trails/SlideEffectPreview.tsx b/src/client/views/nodes/trails/SlideEffectPreview.tsx new file mode 100644 index 000000000..aacb37b48 --- /dev/null +++ b/src/client/views/nodes/trails/SlideEffectPreview.tsx @@ -0,0 +1,134 @@ +import { Button } from '@mui/material'; +import { useSpring, animated, easings } from '@react-spring/web'; +import React, { useEffect, useState } from 'react'; + +interface Props { + friction: number; + tension: number; + mass: number; + children: React.ReactNode; +} + +export default function SpringAnimationPreview({ friction, tension, mass, children }: Props) { + const [springs, api] = useSpring( + () => ({ + from: { + x: 0, + y: 0, + opacity: 1, + scale: 1, + }, + config: { + tension: tension, + friction: friction, + mass: mass, + }, + onStart: () => { + console.log('started'); + }, + onRest: () => { + console.log('resting'); + }, + }), + [tension, friction, mass] + ); + + // Whether the animation is currently playing + const [animating, setAnimating] = useState(false); + + const zoomConfig = { + from: { + x: 0, + y: 0, + opacity: 0, + scale: 0, + }, + to: [ + { + x: 0, + y: 0, + opacity: 1, + scale: 1, + config: { + tension: tension, + friction: friction, + mass: mass, + }, + }, + { + opacity: 0, + scale: 0, + x: 0, + y: 0, + config: { + duration: 500, + easing: easings.easeInOutCubic, + }, + }, + ], + }; + + const bounceConfig = { + from: { + x: -50, + y: 0, + }, + to: [ + { + x: 50, + y: 0, + config: { + tension: tension, + friction: friction, + mass: mass, + }, + }, + { + x: -50, + y: 0, + config: { + duration: 500, + easing: easings.easeInOutCubic, + }, + }, + ], + }; + + const animate = () => { + api.start(bounceConfig); + }; + + useEffect(() => { + animate(); + }, []); + + return ( +
{ + animate(); + }}> + {/* style={{ + width: "50px", + height: "50px", + backgroundColor: "#ff6d6d", + borderRadius: "4px", + ...springs, + }} */} + + {children} + +
+ ); +} -- cgit v1.2.3-70-g09d2