Skip to content

Commit 7cb3c2f

Browse files
committed
Finish up UI for looping keyframe
1 parent f47820e commit 7cb3c2f

File tree

7 files changed

+42
-17
lines changed

7 files changed

+42
-17
lines changed

apps/studio/src/contexts/CreatePortalContext.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const CreatePortalContext = ({ children }: PropsWithChildren<{}>) => {
1111
const { darkMode } = useOptions()
1212
return (
1313
<Context.Provider value={node => overlay.current === null ? null : createPortal(node, overlay.current)}>
14-
<div ref={overlay} className={"absolute z-10 pointer-events-none overflow-hidden h-full w-full " + (darkMode ? "dark" : "")}></div>
14+
<div ref={overlay} className={"absolute z-30 pointer-events-none overflow-hidden h-full w-full " + (darkMode ? "dark" : "")}></div>
1515
{children}
1616
</Context.Provider>
1717
)

apps/studio/src/contexts/TooltipContext.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ const TooltipContextProvider = ({ children }: PropsWithChildren<{}>) => {
7979
{tooltipValue !== null && tooltipValue !== undefined && createPortal(
8080
<div
8181
ref={containerRef}
82-
className="absolute text-center border border-black p-0.5 dark:text-gray-300 dark:bg-gray-800 bg-gray-300"
82+
className="z-50 absolute text-center border border-black p-0.5 dark:text-gray-300 dark:bg-gray-800 bg-gray-300"
8383
>
8484
{tooltipValue}
8585
</div>

apps/studio/src/studio/formats/animations/DcaAnimation.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ export default class DcaAnimation extends AnimatorGumballConsumer {
102102
displayTimeMatch: boolean = true
103103

104104
readonly loopData: KeyframeLoopData
105+
readonly shouldContinueLooping = new LO(false)
106+
105107
readonly keyframeLayers = new LO<readonly KeyframeLayerData[]>([], this.onDirty)
106108

107109
readonly scroll = new LO(0, this.onDirty)

apps/studio/src/studio/keycombos/KeyCombos.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ const keyCombos = {
108108
pause_or_play: new KeyCombo('Pause/Play', "Pause or play the animation", 'Space', false),
109109
restart_animation: new KeyCombo('Restart Animation', "Restart the animation", 'Space', true),
110110
stop_animation: new KeyCombo('Stop Animation', "Stop the animation", 'Space', false, true),
111+
toggle_looping: new KeyCombo('Toggle Looping', "Toggle looping", 'L', true),
111112
}
112113
},
113114
}

apps/studio/src/views/animator/components/AnimatorScrubBar.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { SVGPause, SVGPlay, SVGRestart, SVGStop } from "@dumbcode/shared/icons";
1+
import { SVGLink, SVGPause, SVGPlay, SVGRestart, SVGStop } from "@dumbcode/shared/icons";
22
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
33
import NumericInput from "../../../components/NumericInput";
44
import { ButtonWithTooltip } from "../../../components/Tooltips";
@@ -22,6 +22,8 @@ const AnimatorScrubBar = ({ animation }: { animation: DcaAnimation | null }) =>
2222
const [maxGiven] = useListenableObjectNullable(animation?.maxTime)
2323
const max = maxGiven ?? 1
2424

25+
const [shouldContinueLooping, setShouldContinueLooping] = useListenableObjectNullable(animation?.shouldContinueLooping)
26+
2527
const ref = useRef<HTMLDivElement>(null)
2628

2729
const isMoving = isHovering || isDragging
@@ -80,18 +82,26 @@ const AnimatorScrubBar = ({ animation }: { animation: DcaAnimation | null }) =>
8082

8183
const onToggle = useCallback(() => {
8284
setPlaying(!isPlaying)
85+
if (!isPlaying && animation?.time.value === 0) {
86+
setShouldContinueLooping(true)
87+
}
8388
}, [isPlaying, setPlaying])
8489

8590
const onRestart = useCallback(() => {
8691
setTimeAt(0)
8792
setPlaying(true)
93+
setShouldContinueLooping(true)
8894
}, [setTimeAt, setPlaying])
8995

9096
const onStop = useCallback(() => {
9197
setTimeAt(0)
9298
setPlaying(false)
9399
}, [setTimeAt, setPlaying])
94100

101+
const toggleLooping = () => {
102+
setShouldContinueLooping(!shouldContinueLooping)
103+
}
104+
95105
useKeyComboPressed(
96106
useMemo(() => ({
97107
animator: {
@@ -108,6 +118,8 @@ const AnimatorScrubBar = ({ animation }: { animation: DcaAnimation | null }) =>
108118
const [stopName] = useListenableObject(keycombos.stop_animation.displayName)
109119
const [pauseName] = useListenableObject(keycombos.pause_or_play.displayName)
110120
const [resetName] = useListenableObject(keycombos.restart_animation.displayName)
121+
const [loopingName] = useListenableObject(keycombos.toggle_looping.displayName)
122+
111123

112124
return (
113125
<div className="flex flex-col items-center justify-end h-full dark:bg-gray-800 bg-white">
@@ -123,9 +135,13 @@ const AnimatorScrubBar = ({ animation }: { animation: DcaAnimation | null }) =>
123135
: <SVGPlay className="h-8 w-8" />
124136
}
125137
</ButtonWithTooltip>
126-
<ButtonWithTooltip tooltip={`Restart Animation (${resetName})`} onClick={onRestart} className="z-10 dark:bg-gray-900 bg-gray-200 px-1 rounded-tr-md pt-1 dark:text-gray-400 text-black hover:text-yellow-400 border-r-2 border-t-2 dark:border-black border-white">
138+
<ButtonWithTooltip tooltip={`Restart Animation (${resetName})`} onClick={onRestart} className="z-10 dark:bg-gray-900 bg-gray-200 px-1 pt-1 dark:text-gray-400 text-black hover:text-yellow-400 border-t-2 dark:border-black border-white">
127139
<SVGRestart className="h-6 w-6" />
128140
</ButtonWithTooltip>
141+
<ButtonWithTooltip tooltip={`Toggle Looping (${loopingName})`} onClick={toggleLooping}
142+
className={"z-10 dark:bg-gray-900 bg-gray-200 px-1 rounded-tr-md pt-1 border-r-2 border-t-2 dark:border-black border-white " + (shouldContinueLooping ? "text-blue-500" : "dark:text-gray-400 text-black")}>
143+
<SVGLink className="h-6 w-6" />
144+
</ButtonWithTooltip>
129145
<div className="flex-grow"></div>
130146
</div>
131147
</div>

apps/studio/src/views/animator/components/AnimatorTimeline.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -644,25 +644,25 @@ const LoopingMarker = ({ lo }: { lo: LO<number> }) => {
644644
const [entry, setEntry] = useListenableObject(lo)
645645
const { getPixelsPerSecond, getScroll, addAndRunListener, removeListener } = useContext(ScrollZoomContext)
646646

647+
const ref = useDraggbleRef<HTMLDivElement, number>(
648+
useCallback(() => entry, [entry]),
649+
useCallback(({ dx, initial }) => {
650+
setEntry(Math.max(initial + dx / getPixelsPerSecond(), 0))
651+
}, [setEntry, getPixelsPerSecond]),
652+
useCallback(() => { }, [])
653+
)
654+
647655
const updateRefStyle = useCallback((scroll = getScroll(), pixelsPerSecond = getPixelsPerSecond()) => {
648656
if (ref.current !== null) {
649657
ref.current.style.left = `${entry * pixelsPerSecond - scroll}px`
650658
}
651-
}, [entry, getPixelsPerSecond, getScroll])
659+
}, [entry, getPixelsPerSecond, getScroll, ref])
652660

653661
useEffect(() => {
654662
addAndRunListener(updateRefStyle)
655663
return () => removeListener(updateRefStyle)
656664
}, [addAndRunListener, removeListener, updateRefStyle])
657665

658-
const ref = useDraggbleRef<HTMLDivElement, number>(
659-
useCallback(() => entry, [entry]),
660-
useCallback(({ dx, initial }) => {
661-
setEntry(Math.max(initial + dx / getPixelsPerSecond(), 0))
662-
}, [setEntry, getPixelsPerSecond]),
663-
useCallback(() => { }, [])
664-
)
665-
666666
return (
667667
<div
668668
ref={ref}

packages/shared/tsconfig.json

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
{
2-
"extends": "tsconfig/react-library.json",
3-
"include": ["."],
4-
"exclude": ["dist", "build", "node_modules"]
5-
}
2+
"extends": "@dumbcode/tsconfig/react-library.json",
3+
"include": [
4+
"."
5+
],
6+
"exclude": [
7+
"dist",
8+
"build",
9+
"node_modules"
10+
]
11+
}

0 commit comments

Comments
 (0)