aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/views/nodes/DocumentView.tsx24
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx173
-rw-r--r--src/client/views/nodes/trails/SlideEffect.tsx55
-rw-r--r--src/client/views/nodes/trails/SlideEffectPreview.tsx134
4 files changed, 373 insertions, 13 deletions
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<FieldViewProps & Document
*/
public static AnimationEffect(renderDoc: JSX.Element, presEffectDoc: Opt<Doc>, 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<FieldViewProps & Document
delay: 0,
duration: Cast(presEffectDoc?.presentation_transition, 'number', Cast(presEffectDoc?.followLinkTransitionTime, 'number', null)),
};
- console.log('dir', dir);
- console.log('effect', StrCast(presEffectDoc?.presentation_effect, StrCast(presEffectDoc?.followLinkAnimEffect)));
+
+ let timing = StrCast(presEffectDoc?.presEffectTiming);
+ let timingConfig: SpringSettings | undefined;
+ if (timing) {
+ timingConfig = JSON.parse(timing);
+ }
+
+ if (!timingConfig) {
+ timingConfig = {
+ type: SpringType.DEFAULT,
+ stiffness: 600,
+ damping: 15,
+ mass: 1,
+ };
+ }
//prettier-ignore
switch (StrCast(presEffectDoc?.presentation_effect, StrCast(presEffectDoc?.followLinkAnimEffect))) {
default:
// package used: react-awesome-reveal
case PresEffect.None: return renderDoc;
- case PresEffect.Zoom: return <SlideEffect effectType={EffectType.ZOOM} tension={300} friction={12} mass={2}>{renderDoc}</SlideEffect>
+ case PresEffect.Zoom: return <SlideEffect dir={dir as PresEffectDirection} effectType={EffectType.ZOOM} tension={timingConfig.stiffness} friction={timingConfig.damping} mass={timingConfig.mass}>{renderDoc}</SlideEffect>
case PresEffect.Fade: return <Fade {...effectProps}>{renderDoc}</Fade>;
case PresEffect.Flip: return <Flip {...effectProps}>{renderDoc}</Flip>;
case PresEffect.Rotate: return <Rotate {...effectProps}>{renderDoc}</Rotate>;
- case PresEffect.Bounce: return <SlideEffect effectType={EffectType.BOUNCE} tension={300} friction={12} mass={2}>{renderDoc}</SlideEffect>
+ case PresEffect.Bounce: return <SlideEffect dir={dir as PresEffectDirection} effectType={EffectType.BOUNCE} tension={timingConfig.stiffness} friction={timingConfig.damping} mass={timingConfig.mass}>{renderDoc}</SlideEffect>
case PresEffect.Roll: return <Roll {...effectProps}>{renderDoc}</Roll>;
case PresEffect.Lightspeed: return <JackInTheBox {...effectProps}>{renderDoc}</JackInTheBox>;
}
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<FieldViewProps>() {
public static LayoutString(fieldKey: string) {
@@ -1689,6 +1760,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@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<FieldViewProps>() {
@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) => (
<div
className={`presBox-dropdownOption ${activeItem.presentation_effect === effect || (effect === PresEffect.None && !activeItem.presentation_effect) ? 'active' : ''}`}
@@ -2105,7 +2201,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<div className="slider-text">Slow</div>
</div>
{/* Easing function */}
- <div className="presBox-option-block presBox-option-cente">
+ <div className="presBox-option-block presBox-option-center">
<Dropdown
color={StrCast(Doc.UserDoc().userColor)}
formLabel={'Easing Function'}
@@ -2129,7 +2225,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<CubicBezierEditor setFunc={this.setBezierControlPoints} currPoints={this.currCPoints} easeFunc={StrCast(this.activeItem.presEaseFunc)} />
</div>
</div>
- <div className="presBox-option-block">
+ <div className="presBox-option-block" style={{ padding: '16px' }}>
Effects
<Toggle
formLabel={'Play Audio Annotation'}
@@ -2258,6 +2354,79 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
{presDirection(PresEffectDirection.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
</div> */}
{/* Effect spring settings */}
+ <Dropdown
+ color={StrCast(Doc.UserDoc().userColor)}
+ formLabel={'Effect Timing'}
+ closeOnSelect={true}
+ items={effectTimings}
+ selectedVal={timingConfig.type}
+ setSelectedVal={val => {
+ console.log('effect timing', val);
+ this.updateEffectTiming(activeItem, {
+ type: val as SpringType,
+ ...springMappings[val],
+ });
+ }}
+ dropdownType={DropdownType.SELECT}
+ type={Type.TERT}
+ />
+ <div>Tension</div>
+ <div
+ onPointerDown={e => {
+ e.stopPropagation();
+ }}>
+ <Slider
+ min={1}
+ max={1000}
+ step={5}
+ size="small"
+ value={timingConfig.stiffness}
+ onChange={(e, val) => {
+ if (!timingConfig) return;
+ this.updateEffectTiming(activeItem, { ...timingConfig, stiffness: val as number });
+ }}
+ valueLabelDisplay="auto"
+ />
+ </div>
+ <div>Damping</div>
+ <div
+ onPointerDown={e => {
+ e.stopPropagation();
+ }}>
+ <Slider
+ min={1}
+ max={100}
+ step={1}
+ size="small"
+ value={timingConfig.damping}
+ onChange={(e, val) => {
+ if (!timingConfig) return;
+ this.updateEffectTiming(activeItem, { ...timingConfig, damping: val as number });
+ }}
+ valueLabelDisplay="auto"
+ />
+ </div>
+ <div>Mass</div>
+ <div
+ onPointerDown={e => {
+ e.stopPropagation();
+ }}>
+ <Slider
+ min={1}
+ max={10}
+ step={1}
+ size="small"
+ value={timingConfig.mass}
+ onChange={(e, val) => {
+ if (!timingConfig) return;
+ this.updateEffectTiming(activeItem, { ...timingConfig, mass: val as number });
+ }}
+ valueLabelDisplay="auto"
+ />
+ </div>
+ <SpringAnimationPreview tension={timingConfig.stiffness} friction={timingConfig.damping} mass={timingConfig.mass}>
+ <div style={{ width: '40px', height: '40px', backgroundColor: '#2e96ff', borderRadius: '4px' }}></div>
+ </SpringAnimationPreview>
</div>
<div className="ribbon-final-box">
<div className="ribbon-final-button-hidden" onClick={() => 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 (
+ <div
+ style={{
+ width: '200px',
+ height: '150px',
+ borderRadius: '4px',
+ border: '1px solid #696969',
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ }}
+ onPointerEnter={() => {
+ animate();
+ }}>
+ {/* style={{
+ width: "50px",
+ height: "50px",
+ backgroundColor: "#ff6d6d",
+ borderRadius: "4px",
+ ...springs,
+ }} */}
+ <animated.div
+ style={{
+ ...springs,
+ }}>
+ {children}
+ </animated.div>
+ </div>
+ );
+}