mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-06 02:00:46 +00:00
Begin work on dragging clips to add
This commit is contained in:
1 parent
09e6e565d5
commit
188728693e
14 files changed
+322
-350
No files matched your search
@@ -0,0 +1,18 @@
|
||||
import { createContext } from "react";
|
||||
import { AnimationClip, AudioClip } from "~/models/track";
|
||||
|
||||
export const ClipContext = createContext<{
|
||||
animationClips: AnimationClip[];
|
||||
audioClips: AudioClip[];
|
||||
dragType: "animation" | "audio" | null;
|
||||
dragId: string | null;
|
||||
startDrag: (type: "animation" | "audio", id: string) => void;
|
||||
endDrag: () => void;
|
||||
}>({
|
||||
animationClips: [],
|
||||
audioClips: [],
|
||||
dragType: null,
|
||||
dragId: null,
|
||||
startDrag: () => {},
|
||||
endDrag: () => {},
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
import { ReactNode, useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { ClipContext } from "./ClipContext";
|
||||
import { AnimationClip, AudioClip } from "~/models/track";
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const ClipProvider = ({ children }: Props) => {
|
||||
const [state, setState] = useState<{
|
||||
dragType: "animation" | "audio" | null;
|
||||
dragId: string | null;
|
||||
}>({ dragType: null, dragId: null });
|
||||
const [animationClips, setAnimationClips] = useState<AnimationClip[]>([]);
|
||||
const [audioClips, setAudioClips] = useState<AudioClip[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
setAnimationClips([]);
|
||||
setAudioClips([]);
|
||||
}, []);
|
||||
|
||||
const startDrag = useCallback((type: "animation" | "audio", id: string) => {
|
||||
setState({ dragId: id, dragType: type });
|
||||
}, []);
|
||||
|
||||
const endDrag = useCallback(() => {
|
||||
setState({ dragId: null, dragType: null });
|
||||
}, []);
|
||||
|
||||
const values = useMemo(() => {
|
||||
return {
|
||||
dragType: state.dragType,
|
||||
dragId: state.dragId,
|
||||
animationClips,
|
||||
audioClips,
|
||||
startDrag,
|
||||
endDrag,
|
||||
};
|
||||
}, [state, animationClips, audioClips, startDrag, endDrag]);
|
||||
|
||||
return <ClipContext.Provider value={values}>{children}</ClipContext.Provider>;
|
||||
};
|
||||
@@ -1,10 +1,11 @@
|
||||
import { useState } from "react";
|
||||
|
||||
interface LowerPanelPropsI {
|
||||
timelineHeight: number;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const LowerPanel = ({ children }: LowerPanelPropsI) => {
|
||||
export const LowerPanel = ({ children, timelineHeight }: LowerPanelPropsI) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const handleOpen = () => {
|
||||
setOpen(true);
|
||||
@@ -14,10 +15,11 @@ export const LowerPanel = ({ children }: LowerPanelPropsI) => {
|
||||
<div
|
||||
className={[
|
||||
"absolute bottom-0",
|
||||
"h-[260px] w-screen overflow-auto",
|
||||
"w-screen overflow-auto",
|
||||
"border-t border-ui-panel-border",
|
||||
"bg-ui-panel",
|
||||
].join(" ")}
|
||||
style={{ height: timelineHeight }}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -1,104 +1,10 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
import { faSparkles } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
import { Button, ButtonLink } from "~/components";
|
||||
// import { ButtonDialogue } from "~/modules/ButtonDialogue";
|
||||
import { TopBarHelmet } from "~/modules/TopBarHelmet/TopBarHelmet";
|
||||
import { SidePanel } from "~/modules/SidePanel";
|
||||
import { Controls3D } from "./comps/Controls3D";
|
||||
import { ControlsTopButtons } from "./comps/ControlsTopButtons";
|
||||
import { ControlsVideo } from "./comps/ControlsVideo";
|
||||
import { PreviewWindow } from "./comps/PreviewWindow";
|
||||
import { Timeline } from "./comps/Timeline";
|
||||
import { SidePanelTabs } from "./comps/SidePanelTabs";
|
||||
|
||||
import { TrackProvider } from "~/contexts/TrackContext/TrackProvider";
|
||||
import { EngineProvider } from "~/contexts/EngineProvider";
|
||||
import { ClipProvider } from "~/contexts/ClipContext/ClipProvider";
|
||||
import { PageEnigmaComponent } from "./PageEnigmaComponent";
|
||||
|
||||
export const PageEnigma = () => {
|
||||
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||
|
||||
const [timelineHeight, setTimelineHeight] = useState(0);
|
||||
const timelineRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
|
||||
const updateTimelineHeight = useCallback(() => {
|
||||
if (timelineRef.current) {
|
||||
setTimelineHeight(timelineRef.current.offsetHeight);
|
||||
}
|
||||
}, []);
|
||||
|
||||
//for updating timeline/engine div height (for resizing)
|
||||
useEffect(() => {
|
||||
const observer = new ResizeObserver((entries) => {
|
||||
for (let entry of entries) {
|
||||
updateTimelineHeight();
|
||||
}
|
||||
});
|
||||
|
||||
if (timelineRef.current) {
|
||||
observer.observe(timelineRef.current);
|
||||
}
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, [updateTimelineHeight]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TopBarHelmet>
|
||||
<Button icon={faSparkles}>Generate Movie</Button>
|
||||
</TopBarHelmet>
|
||||
<EngineProvider>
|
||||
<div style={{ height: "calc(100vh - 68px)" }}>
|
||||
{/* Engine section/side panel */}
|
||||
<div
|
||||
id="CanvasUiWrapper"
|
||||
className="flex"
|
||||
// style={{ height: `calc(100% - ${timelineHeight}px)` }}
|
||||
style={{ height: `calc(100% - 260px` }}
|
||||
>
|
||||
<div className="relative w-full overflow-hidden bg-gray-400">
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
id="video-scene"
|
||||
width="1280px"
|
||||
height="720px"
|
||||
/>
|
||||
|
||||
{/* Top controls */}
|
||||
<div className="absolute left-0 top-0 w-full">
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<ControlsTopButtons />
|
||||
<Controls3D />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom controls */}
|
||||
<div className="absolute bottom-0 left-0 w-full">
|
||||
<PreviewWindow />
|
||||
<ControlsVideo />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Side panel */}
|
||||
<SidePanel>
|
||||
<SidePanelTabs />
|
||||
</SidePanel>
|
||||
</div>
|
||||
|
||||
{/* Timeline */}
|
||||
<div className="min-h-[220px]" ref={timelineRef}>
|
||||
<TrackProvider>
|
||||
<Timeline
|
||||
// editorCurrent={editorRef.current}
|
||||
// time={20}
|
||||
// length={12}
|
||||
/>
|
||||
</TrackProvider>
|
||||
</div>
|
||||
</div>
|
||||
</EngineProvider>
|
||||
</div>
|
||||
<ClipProvider>
|
||||
<PageEnigmaComponent />
|
||||
</ClipProvider>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,98 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
import { faSparkles } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
import { Button } from "~/components";
|
||||
import { TopBarHelmet } from "~/modules/TopBarHelmet/TopBarHelmet";
|
||||
import { SidePanel } from "~/modules/SidePanel";
|
||||
import { Controls3D } from "./comps/Controls3D";
|
||||
import { ControlsTopButtons } from "./comps/ControlsTopButtons";
|
||||
import { ControlsVideo } from "./comps/ControlsVideo";
|
||||
import { PreviewWindow } from "./comps/PreviewWindow";
|
||||
import { Timeline } from "./comps/Timeline";
|
||||
import { SidePanelTabs } from "./comps/SidePanelTabs";
|
||||
|
||||
import { TrackProvider } from "~/contexts/TrackContext/TrackProvider";
|
||||
import { EngineProvider } from "~/contexts/EngineProvider";
|
||||
|
||||
export const PageEnigmaComponent = () => {
|
||||
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||
|
||||
const [timelineHeight, setTimelineHeight] = useState(260);
|
||||
const timelineRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const updateTimelineHeight = useCallback(() => {
|
||||
if (timelineRef.current) {
|
||||
setTimelineHeight(timelineRef.current.offsetHeight);
|
||||
}
|
||||
}, []);
|
||||
|
||||
//for updating timeline/engine div height (for resizing)
|
||||
useEffect(() => {
|
||||
const observer = new ResizeObserver((entries) => {
|
||||
for (let entry of entries) {
|
||||
updateTimelineHeight();
|
||||
}
|
||||
});
|
||||
|
||||
if (timelineRef.current) {
|
||||
observer.observe(timelineRef.current);
|
||||
}
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, [updateTimelineHeight]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TopBarHelmet>
|
||||
<Button icon={faSparkles}>Generate Movie</Button>
|
||||
</TopBarHelmet>
|
||||
<EngineProvider>
|
||||
<div style={{ height: "calc(100vh - 68px)" }}>
|
||||
{/* Engine section/side panel */}
|
||||
<div
|
||||
id="CanvasUiWrapper"
|
||||
className="flex"
|
||||
// style={{ height: `calc(100% - ${timelineHeight}px)` }}
|
||||
style={{ height: `calc(100% - 260px` }}
|
||||
>
|
||||
<div className="relative w-full overflow-hidden bg-gray-400">
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
id="video-scene"
|
||||
width="1280px"
|
||||
height="720px"
|
||||
/>
|
||||
|
||||
{/* Top controls */}
|
||||
<div className="absolute left-0 top-0 w-full">
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<ControlsTopButtons />
|
||||
<Controls3D />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom controls */}
|
||||
<div className="absolute bottom-0 left-0 w-full">
|
||||
<PreviewWindow />
|
||||
<ControlsVideo />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Side panel */}
|
||||
<SidePanel>
|
||||
<SidePanelTabs />
|
||||
</SidePanel>
|
||||
</div>
|
||||
|
||||
{/* Timeline */}
|
||||
<div className="min-h-[220px]" ref={timelineRef}>
|
||||
<TrackProvider>
|
||||
<Timeline timelineHeight={timelineHeight} />
|
||||
</TrackProvider>
|
||||
</div>
|
||||
</div>
|
||||
</EngineProvider>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,13 +1,20 @@
|
||||
import { Tabs } from "~/modules/Tabs";
|
||||
import { TabStyling } from "./TabStyling";
|
||||
|
||||
export const SidePanelTabs = ()=>{
|
||||
return(
|
||||
export const SidePanelTabs = () => {
|
||||
return (
|
||||
<Tabs
|
||||
tabs={[
|
||||
{
|
||||
header: "Animation",
|
||||
children: <p>Animation Tab</p>,
|
||||
children: (
|
||||
<div className="flex flex-wrap">
|
||||
<div
|
||||
id="ani-obj-1"
|
||||
className="block h-16 w-16 bg-brand-secondary-700"
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: "Camera",
|
||||
@@ -24,5 +31,4 @@ export const SidePanelTabs = ()=>{
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
}
|
||||
};
|
||||
@@ -1,8 +1,6 @@
|
||||
import { TrackClip } from "./TrackClip";
|
||||
import { useContext } from "react";
|
||||
import { TrackContext } from "~/contexts/TrackContext/TrackContext";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faVolume, faVolumeSlash } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { Track } from "~/pages/PageEnigma/comps/Timeline/Track";
|
||||
|
||||
export const Audio = () => {
|
||||
const { audio, updateAudio, length, scale, toggleAudioMute } =
|
||||
@@ -17,48 +15,14 @@ export const Audio = () => {
|
||||
>
|
||||
<div className="mb-2 text-sm text-white">Global Audio</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="pl-16">
|
||||
<div className="bg-audio-unselected relative mt-4 block h-8 w-full rounded">
|
||||
{clips.map((clip, index) => (
|
||||
<TrackClip
|
||||
key={clip.id}
|
||||
min={
|
||||
index > 0
|
||||
? clips[index - 1].offset + clips[index - 1].length
|
||||
: 0
|
||||
}
|
||||
max={
|
||||
index < clips.length - 1
|
||||
? clips[index + 1].offset
|
||||
: length * 60
|
||||
}
|
||||
style="audio"
|
||||
updateClip={updateAudio}
|
||||
clip={clip}
|
||||
/>
|
||||
))}
|
||||
<div
|
||||
className="absolute text-xs text-white"
|
||||
style={{ top: 6, left: 4 }}
|
||||
>
|
||||
Global Audio Track
|
||||
</div>
|
||||
<button
|
||||
className="absolute text-xl text-white"
|
||||
style={{ top: 2, left: -36 }}
|
||||
onClick={() => toggleAudioMute()}
|
||||
>
|
||||
{audio?.muted ? (
|
||||
<FontAwesomeIcon
|
||||
icon={faVolumeSlash}
|
||||
className="text-brand-primary"
|
||||
/>
|
||||
) : (
|
||||
<FontAwesomeIcon icon={faVolume} />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<Track
|
||||
clips={clips}
|
||||
title="Global Audio Track"
|
||||
updateClip={updateAudio}
|
||||
style="audio"
|
||||
muted={audio?.muted}
|
||||
toggleMute={toggleAudioMute}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { TrackClip } from "./TrackClip";
|
||||
import { useContext } from "react";
|
||||
import { TrackContext } from "~/contexts/TrackContext/TrackContext";
|
||||
import { Track } from "~/pages/PageEnigma/comps/Timeline/Track";
|
||||
|
||||
export const Camera = () => {
|
||||
const { camera, updateCamera, length, scale } = useContext(TrackContext);
|
||||
@@ -14,34 +14,12 @@ export const Camera = () => {
|
||||
>
|
||||
<div className="mb-2 text-sm text-white">Camera</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="pl-16">
|
||||
<div className="bg-camera-unselected relative mt-4 block h-8 w-full rounded">
|
||||
{clips.map((clip, index) => (
|
||||
<TrackClip
|
||||
key={clip.id}
|
||||
min={
|
||||
index > 0
|
||||
? clips[index - 1].offset + clips[index - 1].length
|
||||
: 0
|
||||
}
|
||||
max={
|
||||
index < clips.length - 1
|
||||
? clips[index + 1].offset
|
||||
: length * 60
|
||||
}
|
||||
style="camera"
|
||||
updateClip={updateCamera}
|
||||
clip={clip}
|
||||
/>
|
||||
))}
|
||||
<div
|
||||
className="absolute text-xs text-white"
|
||||
style={{ top: 6, left: 4 }}
|
||||
>
|
||||
Camera Position/Rotation
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Track
|
||||
clips={clips}
|
||||
title="Camera Position/Rotation"
|
||||
style="camera"
|
||||
updateClip={updateCamera}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { TrackClip } from "./TrackClip";
|
||||
import { useContext, useMemo } from "react";
|
||||
import { useCallback, useContext, useMemo } from "react";
|
||||
import { TrackContext } from "~/contexts/TrackContext/TrackContext";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faVolumeSlash, faVolume } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { Track } from "~/pages/PageEnigma/comps/Timeline/Track";
|
||||
|
||||
function buildUpdaters(
|
||||
updateCharacters: (options: {
|
||||
@@ -47,6 +45,9 @@ export const Character = ({ characterId }: Props) => {
|
||||
|
||||
const { updateClipLipSync, updateClipPosition, updateClipAnimations } =
|
||||
useMemo(() => buildUpdaters(updateCharacters), [updateCharacters]);
|
||||
const toggleCharacterLipSyncMute = useCallback(() => {
|
||||
toggleLipSyncMute(character?.id ?? "");
|
||||
}, []);
|
||||
|
||||
if (!character) {
|
||||
return false;
|
||||
@@ -60,107 +61,29 @@ export const Character = ({ characterId }: Props) => {
|
||||
>
|
||||
<div className="mb-2 text-xs text-white">Character</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="pl-16">
|
||||
<div className="relative mt-4 block h-8 rounded bg-character-unselected">
|
||||
{animationClips.map((clip, index) => (
|
||||
<TrackClip
|
||||
key={clip.id}
|
||||
min={
|
||||
index > 0
|
||||
? animationClips[index - 1].offset +
|
||||
animationClips[index - 1].length
|
||||
: 0
|
||||
}
|
||||
max={
|
||||
index < animationClips.length - 1
|
||||
? animationClips[index + 1].offset
|
||||
: length * 60
|
||||
}
|
||||
style="character"
|
||||
updateClip={updateClipAnimations}
|
||||
clip={clip}
|
||||
/>
|
||||
))}
|
||||
<div
|
||||
className="absolute text-xs text-white"
|
||||
style={{ top: 6, left: 4 }}
|
||||
>
|
||||
Animation
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pl-16">
|
||||
<div className="relative mt-4 block h-8 w-full rounded bg-character-unselected">
|
||||
{positionClips.map((clip, index) => (
|
||||
<TrackClip
|
||||
key={clip.id}
|
||||
min={
|
||||
index > 0
|
||||
? positionClips[index - 1].offset +
|
||||
positionClips[index - 1].length
|
||||
: 0
|
||||
}
|
||||
max={
|
||||
index < positionClips.length - 1
|
||||
? positionClips[index + 1].offset
|
||||
: length * 60
|
||||
}
|
||||
style="character"
|
||||
updateClip={updateClipPosition}
|
||||
clip={clip}
|
||||
/>
|
||||
))}
|
||||
<div
|
||||
className="absolute text-xs text-white"
|
||||
style={{ top: 6, left: 4 }}
|
||||
>
|
||||
Character Position/Rotation
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pl-16">
|
||||
<div className="relative mt-4 block h-8 w-full rounded bg-character-unselected">
|
||||
{lipSyncClips.map((clip, index) => (
|
||||
<TrackClip
|
||||
key={clip.id}
|
||||
min={
|
||||
index > 0
|
||||
? lipSyncClips[index - 1].offset +
|
||||
lipSyncClips[index - 1].length
|
||||
: 0
|
||||
}
|
||||
max={
|
||||
index < lipSyncClips.length - 1
|
||||
? lipSyncClips[index + 1].offset
|
||||
: length * 60
|
||||
}
|
||||
style="character"
|
||||
updateClip={updateClipLipSync}
|
||||
clip={clip}
|
||||
/>
|
||||
))}
|
||||
<div
|
||||
className="absolute text-xs text-white"
|
||||
style={{ top: 6, left: 4 }}
|
||||
>
|
||||
Lipsync Audio Track
|
||||
</div>
|
||||
<button
|
||||
className="absolute text-xl text-white"
|
||||
style={{ top: 2, left: -36 }}
|
||||
onClick={() => toggleLipSyncMute(character?.id)}
|
||||
>
|
||||
{character.muted ? (
|
||||
<FontAwesomeIcon
|
||||
icon={faVolumeSlash}
|
||||
className="text-brand-primary"
|
||||
/>
|
||||
) : (
|
||||
<FontAwesomeIcon icon={faVolume} />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<Track
|
||||
clips={animationClips}
|
||||
title="Animation"
|
||||
updateClip={updateClipAnimations}
|
||||
style="character"
|
||||
type="animations"
|
||||
/>
|
||||
<Track
|
||||
clips={positionClips}
|
||||
title="Character Position/Rotation"
|
||||
updateClip={updateClipPosition}
|
||||
style="character"
|
||||
type="positions"
|
||||
/>
|
||||
<Track
|
||||
clips={lipSyncClips}
|
||||
title="Lipsync Audio Track"
|
||||
updateClip={updateClipLipSync}
|
||||
muted={character.muted}
|
||||
toggleMute={toggleCharacterLipSyncMute}
|
||||
style="character"
|
||||
type="lipSync"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { TrackClip } from "./TrackClip";
|
||||
import { useContext } from "react";
|
||||
import { TrackContext } from "~/contexts/TrackContext/TrackContext";
|
||||
import { Track } from "~/pages/PageEnigma/comps/Timeline/Track";
|
||||
|
||||
export const Objects = () => {
|
||||
const { objects, updateObject, length, scale } = useContext(TrackContext);
|
||||
@@ -14,35 +14,12 @@ export const Objects = () => {
|
||||
<div className="mb-2 text-sm text-white">Objects</div>
|
||||
{objects.objects.map((object) => (
|
||||
<div key={object.id} className="flex flex-col gap-2">
|
||||
<div className="pl-16">
|
||||
<div className="bg-objects-unselected relative mt-4 block h-8 w-full rounded">
|
||||
{object.clips.map((clip, index) => (
|
||||
<TrackClip
|
||||
key={clip.id}
|
||||
min={
|
||||
index > 0
|
||||
? object.clips[index - 1].offset +
|
||||
object.clips[index - 1].length
|
||||
: 0
|
||||
}
|
||||
max={
|
||||
index < object.clips.length - 1
|
||||
? object.clips[index + 1].offset
|
||||
: length * 60
|
||||
}
|
||||
style="objects"
|
||||
updateClip={updateObject}
|
||||
clip={clip}
|
||||
/>
|
||||
))}
|
||||
<div
|
||||
className="absolute text-xs text-white"
|
||||
style={{ top: 6, left: 4 }}
|
||||
>
|
||||
Mask Position/Rotation
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Track
|
||||
clips={object.clips}
|
||||
title="Mask Position/Rotation"
|
||||
updateClip={updateObject}
|
||||
style="objects"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -1,20 +1,7 @@
|
||||
import {
|
||||
Fragment,
|
||||
// useCallback,
|
||||
useContext
|
||||
} from "react";
|
||||
|
||||
import { Button } from "~/components";
|
||||
|
||||
// import Editor from "../../js/editor";
|
||||
import { EngineContext } from "~/contexts/EngineContext";
|
||||
import { Fragment, useContext } from "react";
|
||||
import { TrackContext } from "~/contexts/TrackContext/TrackContext";
|
||||
|
||||
import { LowerPanel } from "~/modules/LowerPanel";
|
||||
import { Character } from "./Character";
|
||||
|
||||
|
||||
|
||||
import { Camera } from "./Camera";
|
||||
import { Audio } from "./Audio";
|
||||
import { Objects } from "./Objects";
|
||||
@@ -22,16 +9,11 @@ import { useMouseEventsAnimation } from "./utils/useMouseEventsAnimation";
|
||||
import { faSortDown } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
export const Timeline = ({
|
||||
// editorCurrent,
|
||||
// time,
|
||||
}: {
|
||||
// editorCurrent: Editor | null;
|
||||
// time: number;
|
||||
}) => {
|
||||
const editorEngine = useContext(EngineContext);
|
||||
// editorEngine replaced the engineRef.current passed as a prop
|
||||
// const { characters, updateCharacters } = useContext(TrackContext);
|
||||
interface Props {
|
||||
timelineHeight: number;
|
||||
}
|
||||
|
||||
export const Timeline = ({ timelineHeight }: Props) => {
|
||||
const { characters, objects, scale, length } = useContext(TrackContext);
|
||||
const { onPointerDown, time } = useMouseEventsAnimation();
|
||||
|
||||
@@ -41,7 +23,7 @@ export const Timeline = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<LowerPanel>
|
||||
<LowerPanel timelineHeight={timelineHeight}>
|
||||
<div className="mt-4 flex h-3 text-sm text-white">
|
||||
{Array(length)
|
||||
.fill(0)
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import { TrackClip } from "~/pages/PageEnigma/comps/Timeline/TrackClip";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faVolume, faVolumeSlash } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { BaseClip } from "~/models/track";
|
||||
import { useContext } from "react";
|
||||
import { TrackContext } from "~/contexts/TrackContext/TrackContext";
|
||||
import { ClipContext } from "~/contexts/ClipContext/ClipContext";
|
||||
|
||||
interface Props {
|
||||
clips: BaseClip[];
|
||||
title: string;
|
||||
style: "character" | "audio" | "camera" | "objects";
|
||||
type?: "animations" | "positions" | "lipSync";
|
||||
toggleMute?: () => void;
|
||||
muted?: boolean;
|
||||
updateClip: (options: { id: string; length: number; offset: number }) => void;
|
||||
}
|
||||
|
||||
export const Track = ({
|
||||
clips,
|
||||
toggleMute,
|
||||
updateClip,
|
||||
muted,
|
||||
title,
|
||||
style,
|
||||
type,
|
||||
}: Props) => {
|
||||
const { length } = useContext(TrackContext);
|
||||
const { dragType } = useContext(ClipContext);
|
||||
const trackType = type ?? style;
|
||||
|
||||
const className = dragType === trackType ? "highlight" : "";
|
||||
|
||||
return (
|
||||
<div className="pl-16">
|
||||
<div
|
||||
className={`relative mt-4 block h-8 w-full rounded bg-${style}-unselected`}
|
||||
>
|
||||
{clips.map((clip, index) => (
|
||||
<TrackClip
|
||||
key={clip.id}
|
||||
min={
|
||||
index > 0 ? clips[index - 1].offset + clips[index - 1].length : 0
|
||||
}
|
||||
max={
|
||||
index < clips.length - 1 ? clips[index + 1].offset : length * 60
|
||||
}
|
||||
style={style}
|
||||
updateClip={updateClip}
|
||||
clip={clip}
|
||||
/>
|
||||
))}
|
||||
<div
|
||||
className="absolute text-xs text-white"
|
||||
style={{ top: 6, left: 4 }}
|
||||
>
|
||||
{title}
|
||||
</div>
|
||||
{!!toggleMute && (
|
||||
<button
|
||||
className="absolute text-xl text-white"
|
||||
style={{ top: 2, left: -36 }}
|
||||
onClick={toggleMute}
|
||||
>
|
||||
{muted ? (
|
||||
<FontAwesomeIcon
|
||||
icon={faVolumeSlash}
|
||||
className="text-brand-primary"
|
||||
/>
|
||||
) : (
|
||||
<FontAwesomeIcon icon={faVolume} />
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -59,7 +59,7 @@ export const useMouseEventsClip = (
|
||||
) {
|
||||
return;
|
||||
}
|
||||
currLength.current = initLength.current - delta;
|
||||
currLength.current = initLength.current + delta;
|
||||
}
|
||||
setState({ length: currLength.current, offset: currOffset.current });
|
||||
},
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useState } from 'react';
|
||||
import { useState } from "react";
|
||||
import { PageEnigma } from "../pages/PageEnigma";
|
||||
|
||||
import { Button } from '~/components/Button';
|
||||
import { Button } from "~/components/Button";
|
||||
|
||||
export default function IdealEnigma(){
|
||||
export default function IdealEnigma() {
|
||||
//const [riddle, setRiddle] = useState<string>("");
|
||||
//const [submitted, setSubmitted] = useState<boolean>(false);
|
||||
|
||||
@@ -16,14 +16,13 @@ export default function IdealEnigma(){
|
||||
// setSubmitted(state);
|
||||
//}
|
||||
|
||||
|
||||
return <PageEnigma />
|
||||
return <PageEnigma />;
|
||||
// if(submitted){
|
||||
// return <PageEnigma />
|
||||
// return <PageEnigmaComponent />
|
||||
// }else{
|
||||
// return(
|
||||
// <div className="w-full flex justify-center content-center">
|
||||
// <form
|
||||
// <form
|
||||
// className="mt-20 flex flex-col content-center"
|
||||
// onSubmit={handleSubmit}
|
||||
// >
|
||||
@@ -38,5 +37,4 @@ export default function IdealEnigma(){
|
||||
|
||||
// );
|
||||
// }
|
||||
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user