mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 17:38:10 +00:00
Start keyframes
This commit is contained in:
1 parent
902fdd5adc
commit
786fccd303
13 files changed
+223
-203
No files matched your search
@@ -15,4 +15,6 @@ export enum toEngineActions {
|
||||
// data=QueueClip
|
||||
UPDATE_TIME = "update_time",
|
||||
// data={currentTime: number}
|
||||
ADD_KEYFRAME = "add_keyframe",
|
||||
// data= QueueKeyframe
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { useCallback, useContext, useMemo } from "react";
|
||||
import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext";
|
||||
import { TrackClips } from "~/pages/PageEnigma/comps/Timeline/TrackClips";
|
||||
import { fullWidth } from "~/pages/PageEnigma/store";
|
||||
import { TrackKeyFrames } from "~/pages/PageEnigma/comps/Timeline/TrackKeyFrames";
|
||||
|
||||
function buildUpdaters(
|
||||
updateCharacters: (options: {
|
||||
@@ -52,7 +53,7 @@ export const Character = ({ characterId }: Props) => {
|
||||
if (!character) {
|
||||
return false;
|
||||
}
|
||||
const { animationClips, positionClips, lipSyncClips } = character;
|
||||
const { animationClips, positionKeyframes, lipSyncClips } = character;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -71,9 +72,9 @@ export const Character = ({ characterId }: Props) => {
|
||||
style="character"
|
||||
type="animations"
|
||||
/>
|
||||
<TrackClips
|
||||
<TrackKeyFrames
|
||||
id={character.id}
|
||||
clips={positionClips}
|
||||
keyFrames={positionKeyframes}
|
||||
title="Character Position/Rotation"
|
||||
updateClip={updateClipPosition}
|
||||
style="character"
|
||||
|
||||
@@ -22,7 +22,7 @@ export const Timeline = () => {
|
||||
const {
|
||||
characters,
|
||||
objects,
|
||||
selectedClip,
|
||||
selectedItem,
|
||||
deleteCharacterClip,
|
||||
deleteAudioClip,
|
||||
deleteCameraClip,
|
||||
@@ -43,20 +43,20 @@ export const Timeline = () => {
|
||||
|
||||
const onDeleteAsk = useCallback(
|
||||
(event: KeyboardEvent) => {
|
||||
if (["Backspace", "Delete"].indexOf(event.key) > -1 && selectedClip) {
|
||||
if (["Backspace", "Delete"].indexOf(event.key) > -1 && selectedItem) {
|
||||
setDialogOpen(true);
|
||||
}
|
||||
},
|
||||
[selectedClip],
|
||||
[selectedItem],
|
||||
);
|
||||
|
||||
const displayTime = time === -1 ? currentTime.value : time;
|
||||
|
||||
const onDelete = useCallback(() => {
|
||||
deleteCharacterClip(selectedClip!);
|
||||
deleteCameraClip(selectedClip!);
|
||||
deleteAudioClip(selectedClip!);
|
||||
}, [selectedClip, deleteAudioClip, deleteCharacterClip, deleteCameraClip]);
|
||||
deleteCharacterClip(selectedItem!);
|
||||
deleteCameraClip(selectedItem!);
|
||||
deleteAudioClip(selectedItem!);
|
||||
}, [selectedItem, deleteAudioClip, deleteCharacterClip, deleteCameraClip]);
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener("keydown", onDeleteAsk);
|
||||
|
||||
@@ -18,7 +18,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export const TrackClip = ({ clip, min, max, style, updateClip }: Props) => {
|
||||
const { selectClip, selectedClip } = useContext(TrackContext);
|
||||
const { selectItem, selectedItem } = useContext(TrackContext);
|
||||
const [state, setState] = useState({
|
||||
length: clip.length,
|
||||
offset: clip.offset,
|
||||
@@ -48,7 +48,7 @@ export const TrackClip = ({ clip, min, max, style, updateClip }: Props) => {
|
||||
|
||||
const classes = [
|
||||
"absolute",
|
||||
clip.clip_uuid === selectedClip
|
||||
clip.clip_uuid === selectedItem
|
||||
? `bg-${style}-selected`
|
||||
: `bg-${style}-clip`,
|
||||
];
|
||||
@@ -71,7 +71,7 @@ export const TrackClip = ({ clip, min, max, style, updateClip }: Props) => {
|
||||
...classes,
|
||||
"rounded-l-lg",
|
||||
"block h-full",
|
||||
clip.clip_uuid === selectedClip
|
||||
clip.clip_uuid === selectedItem
|
||||
? "border border-b-2 border-l-2 border-r-0 border-t-2 border-white focus-visible:outline-0"
|
||||
: "",
|
||||
].join(" ")}
|
||||
@@ -81,13 +81,13 @@ export const TrackClip = ({ clip, min, max, style, updateClip }: Props) => {
|
||||
cursor: "w-resize",
|
||||
}}
|
||||
onPointerDown={(event) => onPointerDown(event, "left")}
|
||||
onClick={() => selectClip(clip.clip_uuid)}
|
||||
onClick={() => selectItem(clip.clip_uuid)}
|
||||
/>
|
||||
<button
|
||||
className={[
|
||||
...classes,
|
||||
"block h-full",
|
||||
clip.clip_uuid === selectedClip
|
||||
clip.clip_uuid === selectedItem
|
||||
? "border border-b-2 border-l-0 border-r-0 border-t-2 border-white focus-visible:outline-0"
|
||||
: "",
|
||||
].join(" ")}
|
||||
@@ -97,14 +97,14 @@ export const TrackClip = ({ clip, min, max, style, updateClip }: Props) => {
|
||||
cursor: "move",
|
||||
}}
|
||||
onPointerDown={(event) => onPointerDown(event, "drag")}
|
||||
onClick={() => selectClip(clip.clip_uuid)}
|
||||
onClick={() => selectItem(clip.clip_uuid)}
|
||||
/>
|
||||
<button
|
||||
className={[
|
||||
...classes,
|
||||
"rounded-r-lg",
|
||||
"block h-full",
|
||||
clip.clip_uuid === selectedClip
|
||||
clip.clip_uuid === selectedItem
|
||||
? "border border-b-2 border-l-0 border-r-2 border-t-2 border-white focus-visible:outline-0"
|
||||
: "",
|
||||
].join(" ")}
|
||||
@@ -114,9 +114,9 @@ export const TrackClip = ({ clip, min, max, style, updateClip }: Props) => {
|
||||
cursor: "e-resize",
|
||||
}}
|
||||
onPointerDown={(event) => onPointerDown(event, "right")}
|
||||
onClick={() => selectClip(clip.clip_uuid)}
|
||||
onClick={() => selectItem(clip.clip_uuid)}
|
||||
/>
|
||||
{selectedClip === clip.clip_uuid && (
|
||||
{selectedItem === clip.clip_uuid && (
|
||||
<button
|
||||
className="absolute flex h-full items-center justify-center"
|
||||
style={{
|
||||
|
||||
@@ -1,62 +1,52 @@
|
||||
import { BaseKeyFrame } from "~/pages/PageEnigma/models/track";
|
||||
import { KeyFrame } from "~/pages/PageEnigma/models/track";
|
||||
import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext";
|
||||
import { useContext, useState } from "react";
|
||||
import { scale } from "~/pages/PageEnigma/store";
|
||||
import { useMouseEventsKeyframe } from "~/pages/PageEnigma/comps/Timeline/utils/useMouseEventsKeyframe";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faSortUpDown } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
interface Props {
|
||||
min: number;
|
||||
max: number;
|
||||
style: "character" | "camera" | "audio" | "objects";
|
||||
keyFrame: BaseKeyFrame;
|
||||
updateClip: (options: { id: string; offset: number; length: number }) => void;
|
||||
keyframe: KeyFrame;
|
||||
updateKeyframe: (options: { id: string; offset: number }) => void;
|
||||
}
|
||||
|
||||
export const TrackKeyFrame = ({
|
||||
keyFrame,
|
||||
// min,
|
||||
// max,
|
||||
keyframe,
|
||||
min,
|
||||
max,
|
||||
style,
|
||||
// updateClip,
|
||||
updateKeyframe,
|
||||
}: Props) => {
|
||||
const { selectedClip } = useContext(TrackContext);
|
||||
// const { selectClip, selectedClip } = useContext(TrackContext);
|
||||
const [state, setState] = useState({
|
||||
offset: keyFrame.offset,
|
||||
const { selectedItem, selectItem } = useContext(TrackContext);
|
||||
// const { selectItem, selectedItem } = useContext(TrackContext);
|
||||
const [offset, setOffset] = useState(keyframe.offset);
|
||||
const { onPointerDown } = useMouseEventsKeyframe({
|
||||
keyframe,
|
||||
max,
|
||||
min,
|
||||
updateKeyframe,
|
||||
setOffset,
|
||||
});
|
||||
// const { onPointerDown } = useMouseEventsClip(
|
||||
// keyFrame,
|
||||
// max,
|
||||
// min,
|
||||
// updateClip,
|
||||
// setState,
|
||||
// );
|
||||
|
||||
const { offset } = state;
|
||||
|
||||
const classes = [
|
||||
"absolute",
|
||||
keyFrame.id === selectedClip ? `bg-${style}-selected` : `bg-${style}-clip`,
|
||||
keyframe.keyframe_uuid === selectedItem
|
||||
? `bg-${style}-selected`
|
||||
: `bg-${style}-clip`,
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={[
|
||||
...classes,
|
||||
"px-[6px] py-[3px]",
|
||||
"rounded",
|
||||
"prevent-select text-xs font-medium text-white",
|
||||
].join(" ")}
|
||||
style={{ top: -28, left: offset * 4 * scale.value + 2 }}
|
||||
>
|
||||
{keyFrame.name}
|
||||
</div>
|
||||
<button
|
||||
className={[
|
||||
...classes,
|
||||
"rounded-l-lg",
|
||||
"block h-full",
|
||||
keyFrame.id === selectedClip
|
||||
keyframe.keyframe_uuid === selectedItem
|
||||
? "border border-b-2 border-l-2 border-r-0 border-t-2 border-white focus-visible:outline-0"
|
||||
: "",
|
||||
].join(" ")}
|
||||
@@ -65,42 +55,11 @@ export const TrackKeyFrame = ({
|
||||
left: offset * 4 * scale.value,
|
||||
cursor: "w-resize",
|
||||
}}
|
||||
// onPointerDown={(event) => onPointerDown(event, "left")}
|
||||
// onClick={() => selectClip(clip.id)}
|
||||
/>
|
||||
<button
|
||||
className={[
|
||||
...classes,
|
||||
"block h-full",
|
||||
keyFrame.id === selectedClip
|
||||
? "border border-b-2 border-l-0 border-r-0 border-t-2 border-white focus-visible:outline-0"
|
||||
: "",
|
||||
].join(" ")}
|
||||
style={{
|
||||
width: length * 4 * scale.value - 30,
|
||||
left: offset * 4 * scale.value + 15,
|
||||
cursor: "move",
|
||||
}}
|
||||
// onPointerDown={(event) => onPointerDown(event, "drag")}
|
||||
// onClick={() => selectClip(clip.id)}
|
||||
/>
|
||||
<button
|
||||
className={[
|
||||
...classes,
|
||||
"rounded-r-lg",
|
||||
"block h-full",
|
||||
keyFrame.id === selectedClip
|
||||
? "border border-b-2 border-l-0 border-r-2 border-t-2 border-white focus-visible:outline-0"
|
||||
: "",
|
||||
].join(" ")}
|
||||
style={{
|
||||
width: 15,
|
||||
left: offset * 4 * scale.value + length * 4 * scale.value - 15,
|
||||
cursor: "e-resize",
|
||||
}}
|
||||
// onPointerDown={(event) => onPointerDown(event, "right")}
|
||||
// onClick={() => selectClip(clip.id)}
|
||||
/>
|
||||
onPointerDown={(event) => onPointerDown(event, "left")}
|
||||
onClick={() => selectItem(keyframe.keyframe_uuid)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faSortUpDown} />
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,24 +1,12 @@
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faVolume, faVolumeSlash } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { BaseKeyFrame } from "~/pages/PageEnigma/models/track";
|
||||
import { PointerEvent } from "react";
|
||||
import {
|
||||
canDrop,
|
||||
clipLength,
|
||||
dragType,
|
||||
dropId,
|
||||
dropOffset,
|
||||
filmLength,
|
||||
scale,
|
||||
} from "~/pages/PageEnigma/store";
|
||||
import { KeyFrame } from "~/pages/PageEnigma/models/track";
|
||||
import { filmLength } from "~/pages/PageEnigma/store";
|
||||
import { TrackKeyFrame } from "~/pages/PageEnigma/comps/Timeline/TrackKeyFrame";
|
||||
|
||||
interface Props {
|
||||
id: string;
|
||||
keyFrames: BaseKeyFrame[];
|
||||
keyFrames: KeyFrame[];
|
||||
title: string;
|
||||
style: "character" | "audio" | "camera" | "objects";
|
||||
type?: "animations" | "positions" | "lipSync";
|
||||
toggleMute?: () => void;
|
||||
muted?: boolean;
|
||||
updateClip: (options: { id: string; length: number; offset: number }) => void;
|
||||
@@ -27,62 +15,19 @@ interface Props {
|
||||
export const TrackKeyFrames = ({
|
||||
id,
|
||||
keyFrames,
|
||||
toggleMute,
|
||||
updateClip,
|
||||
muted,
|
||||
title,
|
||||
style,
|
||||
type,
|
||||
}: Props) => {
|
||||
const trackType = type ?? style;
|
||||
|
||||
function onPointerOver() {
|
||||
if (dragType.value !== trackType) {
|
||||
return;
|
||||
}
|
||||
dropId.value = id;
|
||||
}
|
||||
|
||||
function onPointerMove(event: PointerEvent<HTMLDivElement>) {
|
||||
if (dragType.value !== trackType) {
|
||||
return;
|
||||
}
|
||||
const track = document.getElementById(`track-${trackType}-${id}`);
|
||||
if (!track) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Now check the the clip fits
|
||||
const position = track.getBoundingClientRect();
|
||||
const clipOffset = (event.clientX - position.x) / 4 / scale.value;
|
||||
|
||||
if (clipOffset + clipLength.value > filmLength.value * 60) {
|
||||
canDrop.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
canDrop.value = true;
|
||||
dropOffset.value = clipOffset;
|
||||
}
|
||||
function onPointerLeave() {
|
||||
if (dragType.value !== trackType) {
|
||||
return;
|
||||
}
|
||||
canDrop.value = false;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="pl-16">
|
||||
<div
|
||||
id={`track-${trackType}-${id}`}
|
||||
id={`track-${style}-${id}`}
|
||||
className={`relative mt-4 block h-9 w-full rounded-lg bg-${style}-unselected`}
|
||||
onPointerOver={onPointerOver}
|
||||
onPointerLeave={onPointerLeave}
|
||||
onPointerMove={onPointerMove}
|
||||
>
|
||||
{keyFrames.map((keyFrame, index) => (
|
||||
<TrackKeyFrame
|
||||
key={keyFrame.id}
|
||||
key={keyFrame.keyframe_uuid}
|
||||
min={index > 0 ? keyFrames[index - 1].offset + 1 : 0}
|
||||
max={
|
||||
index < keyFrames.length - 1
|
||||
@@ -97,22 +42,6 @@ export const TrackKeyFrames = ({
|
||||
<div className="prevent-select absolute ps-2 pt-1 text-xs font-medium text-white">
|
||||
{title}
|
||||
</div>
|
||||
{!!toggleMute && (
|
||||
<button
|
||||
className="text-md absolute text-white transition-colors duration-100 hover:text-white/80"
|
||||
style={{ top: 6, left: -28 }}
|
||||
onClick={toggleMute}
|
||||
>
|
||||
{muted ? (
|
||||
<FontAwesomeIcon
|
||||
icon={faVolumeSlash}
|
||||
className="text-brand-primary transition-colors duration-100 hover:text-brand-primary/80"
|
||||
/>
|
||||
) : (
|
||||
<FontAwesomeIcon icon={faVolume} />
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
import React, { Dispatch, useCallback, useEffect, useRef } from "react";
|
||||
import { KeyFrame } from "~/pages/PageEnigma/models/track";
|
||||
import { canDrop, scale } from "~/pages/PageEnigma/store";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
|
||||
export const useMouseEventsKeyframe = ({
|
||||
keyframe,
|
||||
max,
|
||||
min,
|
||||
updateKeyframe,
|
||||
setOffset,
|
||||
}: {
|
||||
keyframe: KeyFrame;
|
||||
max: number;
|
||||
min: number;
|
||||
updateKeyframe: (args: { id: string; offset: number }) => void;
|
||||
setOffset: Dispatch<number>;
|
||||
}) => {
|
||||
useSignals();
|
||||
const currOffset = useRef(keyframe.offset);
|
||||
const initOffset = useRef(keyframe.offset);
|
||||
const isActive = useRef("");
|
||||
const clientX = useRef(0);
|
||||
|
||||
const onPointerUp = useCallback(() => {
|
||||
if (isActive.current) {
|
||||
updateKeyframe({
|
||||
id: keyframe.keyframe_uuid,
|
||||
offset: Math.round(currOffset.current),
|
||||
});
|
||||
isActive.current = "";
|
||||
canDrop.value = false;
|
||||
}
|
||||
}, [updateKeyframe, keyframe.keyframe_uuid]);
|
||||
|
||||
const onMouseMove = useCallback(
|
||||
(event: MouseEvent) => {
|
||||
const delta = (event.clientX - clientX.current) / 4 / scale.value;
|
||||
const deltaOffset = delta + initOffset.current;
|
||||
if (isActive.current === "drag") {
|
||||
if (deltaOffset < min) {
|
||||
currOffset.current = min;
|
||||
return;
|
||||
}
|
||||
if (deltaOffset > max) {
|
||||
currOffset.current = max;
|
||||
return;
|
||||
}
|
||||
currOffset.current = deltaOffset;
|
||||
}
|
||||
setOffset(currOffset.current);
|
||||
},
|
||||
[max, min, setOffset],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
currOffset.current = keyframe.offset;
|
||||
initOffset.current = keyframe.offset;
|
||||
}, [keyframe.offset]);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("pointerup", onPointerUp);
|
||||
window.addEventListener("pointermove", onMouseMove);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("pointerup", onPointerUp);
|
||||
window.removeEventListener("pointermove", onMouseMove);
|
||||
};
|
||||
}, [onPointerUp, onMouseMove]);
|
||||
|
||||
return {
|
||||
onPointerDown: (
|
||||
event: React.PointerEvent<HTMLButtonElement>,
|
||||
type: string,
|
||||
) => {
|
||||
if (event.button === 0) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
clientX.current = event.clientX;
|
||||
isActive.current = type;
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,11 +1,12 @@
|
||||
import { fromEngineActions } from "~/pages/PageEnigma/Queue/fromEngineActions";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import { useCallback, useEffect } from "react";
|
||||
import { useCallback, useContext, useEffect } from "react";
|
||||
import { currentTime } from "~/pages/PageEnigma/store";
|
||||
import Queue from "~/pages/PageEnigma/Queue/Queue";
|
||||
import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames";
|
||||
import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions";
|
||||
import { QueueClip } from "~/pages/PageEnigma/models/track";
|
||||
import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext";
|
||||
|
||||
interface UpdateTime {
|
||||
currentTime: number;
|
||||
@@ -18,8 +19,10 @@ interface Arguments {
|
||||
|
||||
export function useQueueHandler() {
|
||||
useSignals();
|
||||
const handleActions = useCallback(({ action, data }: Arguments) => {
|
||||
console.log(`FROM ENGINE:${action} ${data}`)
|
||||
const { addKeyframe } = useContext(TrackContext);
|
||||
|
||||
const handleFromEngineActions = useCallback(({ action, data }: Arguments) => {
|
||||
console.log(`FROM ENGINE:${action} ${data}`);
|
||||
switch (action) {
|
||||
case fromEngineActions.UPDATE_TIME:
|
||||
currentTime.value = (data as UpdateTime).currentTime;
|
||||
@@ -29,7 +32,19 @@ export function useQueueHandler() {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleToTimelineActions = useCallback(({ action, data }: Arguments) => {
|
||||
console.log(`FROM ENGINE:${action} ${data}`);
|
||||
switch (action) {
|
||||
case toTimelineActions.ADD_KEYFRAME:
|
||||
addKeyframe(data);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown action ${action}`);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
Queue.subscribe(QueueNames.FROM_ENGINE, handleActions);
|
||||
}, [handleActions]);
|
||||
Queue.subscribe(QueueNames.FROM_ENGINE, handleFromEngineActions);
|
||||
Queue.subscribe(QueueNames.TO_TIMELINE, handleToTimelineActions);
|
||||
}, [handleFromEngineActions, handleToTimelineActions]);
|
||||
}
|
||||
@@ -4,8 +4,8 @@ import {
|
||||
CameraGroup,
|
||||
CharacterGroup,
|
||||
ObjectGroup,
|
||||
ObjectTrack,
|
||||
MediaClip,
|
||||
KeyFrame,
|
||||
} from "~/pages/PageEnigma/models/track";
|
||||
|
||||
export const TrackContext = createContext<{
|
||||
@@ -62,11 +62,14 @@ export const TrackContext = createContext<{
|
||||
// timeline objects group
|
||||
objects: ObjectGroup;
|
||||
updateObject: (options: { id: string; offset: number }) => void;
|
||||
addObject: (options: ObjectTrack) => void;
|
||||
|
||||
// current - only select one item - will be replaced
|
||||
selectedClip: string | null;
|
||||
selectClip: (clipId: string) => void;
|
||||
selectedItem: string | null;
|
||||
selectItem: (itemId: string) => void;
|
||||
|
||||
// keyframes
|
||||
addKeyframe: (options: { object_uuid: string }) => void;
|
||||
deleteKeyframe: (options: KeyFrame) => void;
|
||||
|
||||
// sidebar clips
|
||||
animationClips: MediaClip[];
|
||||
@@ -102,10 +105,12 @@ export const TrackContext = createContext<{
|
||||
|
||||
objects: { id: "", objects: [] },
|
||||
updateObject: () => {},
|
||||
addObject: () => {},
|
||||
|
||||
selectedClip: null,
|
||||
selectClip: () => {},
|
||||
addKeyframe: () => {},
|
||||
deleteKeyframe: () => {},
|
||||
|
||||
selectedItem: null,
|
||||
selectItem: () => {},
|
||||
|
||||
animationClips: [],
|
||||
audioClips: [],
|
||||
|
||||
@@ -27,8 +27,8 @@ export const TrackProvider = ({ children }: Props) => {
|
||||
|
||||
const { endDrag, ...dragDrop } = useUpdateDragDrop();
|
||||
|
||||
const [selectedClip, setSelectedClip] = useState<string | null>(null);
|
||||
const selectClip = useCallback((clipId: string | null) => {
|
||||
const [selectedItem, setSelectedClip] = useState<string | null>(null);
|
||||
const selectItem = useCallback((clipId: string | null) => {
|
||||
setSelectedClip(clipId);
|
||||
}, []);
|
||||
|
||||
@@ -115,8 +115,8 @@ export const TrackProvider = ({ children }: Props) => {
|
||||
...audio,
|
||||
...objects,
|
||||
|
||||
selectClip,
|
||||
selectedClip,
|
||||
selectItem,
|
||||
selectedItem,
|
||||
|
||||
...dragDrop,
|
||||
endDrag: dropClip,
|
||||
@@ -138,8 +138,8 @@ export const TrackProvider = ({ children }: Props) => {
|
||||
audio,
|
||||
objects,
|
||||
|
||||
selectClip,
|
||||
selectedClip,
|
||||
selectItem,
|
||||
selectedItem,
|
||||
|
||||
dragDrop,
|
||||
dropClip,
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { useCallback } from "react";
|
||||
import { ObjectTrack } from "~/pages/PageEnigma/models/track";
|
||||
|
||||
export default function useUpdateKeyframe() {
|
||||
const addKeyframe = useCallback((obj: ObjectTrack) => {
|
||||
setObjects((oldObjectGroup) => {
|
||||
return {
|
||||
...oldObjectGroup,
|
||||
objects: [...oldObjectGroup.objects, obj],
|
||||
};
|
||||
});
|
||||
}, []);
|
||||
|
||||
return {
|
||||
addKeyframe,
|
||||
};
|
||||
}
|
||||
@@ -1,21 +1,11 @@
|
||||
import { useCallback, useState } from "react";
|
||||
import { ObjectGroup, ObjectTrack } from "~/pages/PageEnigma/models/track";
|
||||
import * as uuid from "uuid";
|
||||
|
||||
export default function useUpdateObject() {
|
||||
const [objects, setObjects] = useState<ObjectGroup>({
|
||||
id: "OB1",
|
||||
objects: [
|
||||
// {
|
||||
// id: "OB1-O1",
|
||||
// keyFrames: [
|
||||
// {
|
||||
// id: "OB1-O1-1",
|
||||
// offset: 0,
|
||||
// name: "obj 1",
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
],
|
||||
objects: [],
|
||||
});
|
||||
|
||||
const updateObject = useCallback(
|
||||
@@ -27,7 +17,7 @@ export default function useUpdateObject() {
|
||||
id: object.id,
|
||||
keyFrames: object.keyFrames.map((keyFrame) => ({
|
||||
...keyFrame,
|
||||
offset: keyFrame.id === id ? offset : keyFrame.offset,
|
||||
offset: keyFrame.keyframe_uuid === id ? offset : keyFrame.offset,
|
||||
})),
|
||||
})),
|
||||
};
|
||||
@@ -36,6 +26,18 @@ export default function useUpdateObject() {
|
||||
[],
|
||||
);
|
||||
|
||||
const addObject = useCallback((name: string) => {
|
||||
setObjects((oldObjectGroup) => {
|
||||
return {
|
||||
...oldObjectGroup,
|
||||
objects: [
|
||||
...oldObjectGroup.objects,
|
||||
{ object_uuid: uuid.v4(), name: name, keyframes: [] },
|
||||
],
|
||||
};
|
||||
});
|
||||
}, []);
|
||||
|
||||
const addObject = useCallback((obj: ObjectTrack) => {
|
||||
setObjects((oldObjectGroup) => {
|
||||
return {
|
||||
@@ -49,5 +51,6 @@ export default function useUpdateObject() {
|
||||
objects,
|
||||
updateObject,
|
||||
addObject,
|
||||
addKeyframe,
|
||||
};
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import { XYZ } from "../datastructures/common";
|
||||
|
||||
export interface QueueClip {
|
||||
version: number;
|
||||
type: ClipType;
|
||||
@@ -45,10 +47,15 @@ export enum ClipGroup {
|
||||
OBJECT = "object",
|
||||
}
|
||||
|
||||
export interface BaseKeyFrame {
|
||||
id: string;
|
||||
name: string;
|
||||
export interface KeyFrame {
|
||||
version: number;
|
||||
keyframe_uuid: string;
|
||||
group: ClipGroup;
|
||||
object_uuid?: string;
|
||||
offset: number;
|
||||
position: XYZ;
|
||||
rotation: XYZ;
|
||||
scale: XYZ;
|
||||
selected?: boolean;
|
||||
}
|
||||
|
||||
@@ -56,13 +63,13 @@ export interface CharacterGroup {
|
||||
id: string;
|
||||
muted: boolean;
|
||||
animationClips: Clip[];
|
||||
positionClips: Clip[];
|
||||
positionKeyframes: KeyFrame[];
|
||||
lipSyncClips: Clip[];
|
||||
}
|
||||
|
||||
export interface CameraGroup {
|
||||
id: string;
|
||||
clips: Clip[];
|
||||
clips: KeyFrame[];
|
||||
}
|
||||
|
||||
export interface AudioGroup {
|
||||
@@ -78,7 +85,5 @@ export interface ObjectGroup {
|
||||
|
||||
export interface ObjectTrack {
|
||||
id: string;
|
||||
keyFrames: ObjectKeyFrame[];
|
||||
keyFrames: KeyFrame[];
|
||||
}
|
||||
|
||||
export interface ObjectKeyFrame extends BaseKeyFrame {}
|
||||
Reference in new issue
Block a user