mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-14 05:42:32 +00:00
Scroll timeline (#299)
scrolls selected item in the editor into view in the timeline --------- Co-authored-by: Christopher Oliphant <RALifeCoach@users.noreply.github.com>
This commit is contained in:
1 parent
b235b7bec7
commit
1a0f471f54
7 files changed
+85
-24
No files matched your search
@@ -33,8 +33,6 @@ export function StyleButtons() {
|
||||
});
|
||||
};
|
||||
|
||||
console.log("state", editorState.value);
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-col justify-center gap-4 rounded-b-lg bg-ui-panel px-4">
|
||||
<div className="w-full">
|
||||
|
||||
@@ -18,6 +18,7 @@ export const Audio = () => {
|
||||
if (audioMinimized.value) {
|
||||
return (
|
||||
<div
|
||||
id="track-global-audio"
|
||||
className="relative flex h-[35px] items-center justify-end rounded-r-lg bg-global_audio-groupBg pr-4"
|
||||
style={{ width: fullWidth.value + 16 }}>
|
||||
<button
|
||||
@@ -38,6 +39,7 @@ export const Audio = () => {
|
||||
|
||||
return (
|
||||
<div
|
||||
id="track-global-audio"
|
||||
className="relative block rounded-r-lg bg-global_audio-groupBg pb-5 pr-4"
|
||||
style={{ width: fullWidth.value + 16 }}>
|
||||
<button
|
||||
|
||||
@@ -18,6 +18,7 @@ export const Camera = () => {
|
||||
if (cameraMinimized.value) {
|
||||
return (
|
||||
<div
|
||||
id="track-camera"
|
||||
className="relative flex h-[35px] items-center justify-end rounded-r-lg bg-camera-groupBg pr-4"
|
||||
style={{ width: fullWidth.value + 16 }}>
|
||||
<button
|
||||
@@ -38,6 +39,7 @@ export const Camera = () => {
|
||||
|
||||
return (
|
||||
<div
|
||||
id="track-camera"
|
||||
className="relative block rounded-r-lg bg-camera-groupBg pb-5 pr-4"
|
||||
style={{ width: fullWidth.value + 16 }}>
|
||||
<div className="flex justify-end">
|
||||
|
||||
@@ -62,7 +62,7 @@ export const Character = ({ character }: Props) => {
|
||||
if (minimized) {
|
||||
return (
|
||||
<div
|
||||
id={`character-${character.object_uuid}`}
|
||||
id={`track-character-${character.object_uuid}`}
|
||||
className="relative flex h-[35px] items-center justify-end rounded-r-lg bg-character-groupBg pr-4"
|
||||
style={{ width: fullWidth.value + 16 }}>
|
||||
<button
|
||||
@@ -83,6 +83,7 @@ export const Character = ({ character }: Props) => {
|
||||
|
||||
return (
|
||||
<div
|
||||
id={`track-character-${character.object_uuid}`}
|
||||
className="relative block rounded-r-lg bg-character-groupBg pb-5 pr-4"
|
||||
style={{ width: fullWidth.value + 16 }}>
|
||||
<div className="flex h-[35px] items-center justify-end">
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import {
|
||||
audioMinimized,
|
||||
cameraMinimized,
|
||||
fullWidth,
|
||||
minimizeIconPosition,
|
||||
objectGroup,
|
||||
objectsMinimized,
|
||||
selectedObject,
|
||||
} from "~/pages/PageEnigma/store";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import { ObjectTrackComponent } from "~/pages/PageEnigma/comps/Timeline/ObjectTrack";
|
||||
@@ -21,6 +20,7 @@ export const ObjectGroups = () => {
|
||||
if (objectsMinimized.value) {
|
||||
return (
|
||||
<div
|
||||
id="track-objects"
|
||||
className="relative mb-4 flex h-[35px] items-center justify-end rounded-r-lg bg-object-groupBg pr-4"
|
||||
style={{ width: fullWidth.value + 16 }}>
|
||||
<button
|
||||
@@ -41,6 +41,7 @@ export const ObjectGroups = () => {
|
||||
|
||||
return (
|
||||
<div
|
||||
id="track-objects"
|
||||
className="relative mb-5 block rounded-r-lg bg-object-groupBg pr-4"
|
||||
style={{ width: fullWidth.value + 16 }}>
|
||||
<button
|
||||
@@ -57,7 +58,10 @@ export const ObjectGroups = () => {
|
||||
</button>
|
||||
<div className="pt-[47px]">
|
||||
{objectGroup.value.objects.map((object) => (
|
||||
<div key={object.object_uuid} className="pb-4 pr-4">
|
||||
<div
|
||||
key={object.object_uuid}
|
||||
className="pb-4 pr-4"
|
||||
id={`track-object-${selectedObject.value?.id}`}>
|
||||
<ObjectTrackComponent object={object} />
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UIEvent, useCallback, useEffect, useState } from "react";
|
||||
import { UIEvent, useCallback, useEffect, useRef, useState } from "react";
|
||||
import { LowerPanel } from "~/modules/LowerPanel";
|
||||
|
||||
import { Camera } from "./Camera";
|
||||
@@ -9,12 +9,14 @@ import {
|
||||
deleteCharacterClip,
|
||||
filmLength,
|
||||
ignoreKeyDelete,
|
||||
isHotkeyDisabled,
|
||||
objectsMinimized,
|
||||
scale,
|
||||
selectedItem,
|
||||
selectedObject,
|
||||
timelineHeight,
|
||||
timelineScrollX,
|
||||
timelineScrollY,
|
||||
isHotkeyDisabled,
|
||||
} from "~/pages/PageEnigma/store";
|
||||
import { useQueueHandler } from "~/pages/PageEnigma/comps/Timeline/utils/useQueueHandler";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
@@ -23,7 +25,7 @@ import { Scrubber } from "~/pages/PageEnigma/comps/Timeline/Scrubber";
|
||||
import { Characters } from "~/pages/PageEnigma/comps/Timeline/Characters";
|
||||
import { ObjectGroups } from "~/pages/PageEnigma/comps/Timeline/ObjectGroups";
|
||||
import useUpdateKeyframe from "~/pages/PageEnigma/contexts/TrackContext/utils/useUpdateKeyframe";
|
||||
import { Clip, Keyframe } from "~/pages/PageEnigma/models";
|
||||
import { AssetType, Clip, Keyframe } from "~/pages/PageEnigma/models";
|
||||
import { RowHeaders } from "~/pages/PageEnigma/comps/Timeline/RowHeaders/RowHeaders";
|
||||
import { pageWidth } from "~/store";
|
||||
import { Pages } from "~/pages/PageEnigma/constants/page";
|
||||
@@ -36,10 +38,38 @@ function getItemType(item: Clip | Keyframe | null) {
|
||||
return (item as Clip).clip_uuid ? "clip" : "keyframe";
|
||||
}
|
||||
|
||||
function scrollItem(itemId: string) {
|
||||
const element = document.getElementById(itemId);
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
element.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
||||
}
|
||||
|
||||
export const Timeline = () => {
|
||||
useSignals();
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const { deleteKeyframe } = useUpdateKeyframe();
|
||||
const lastSelectedObject = useRef(selectedObject.value);
|
||||
|
||||
if (selectedObject.value !== lastSelectedObject.current) {
|
||||
lastSelectedObject.current = selectedObject.value;
|
||||
switch (selectedObject.value?.type) {
|
||||
case AssetType.CHARACTER:
|
||||
scrollItem(`track-character-${selectedObject.value?.id}`);
|
||||
break;
|
||||
case AssetType.CAMERA:
|
||||
scrollItem("track-camera");
|
||||
break;
|
||||
case AssetType.OBJECT:
|
||||
if (objectsMinimized.value) {
|
||||
scrollItem("track-objects");
|
||||
} else {
|
||||
scrollItem(`track-object-${selectedObject.value?.id}`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const onScroll = useCallback((event: UIEvent<HTMLDivElement>) => {
|
||||
timelineScrollX.value = event.currentTarget.scrollLeft;
|
||||
|
||||
@@ -35,7 +35,11 @@ import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames";
|
||||
import { fromEngineActions } from "~/pages/PageEnigma/Queue/fromEngineActions";
|
||||
import { AssetType, MediaItem } from "~/pages/PageEnigma/models";
|
||||
import { loadingBarData, loadingBarIsShowing } from "~/store/loadingBar";
|
||||
import { editorState, EditorStates, previewSrc } from "~/pages/PageEnigma/store/engine";
|
||||
import {
|
||||
editorState,
|
||||
EditorStates,
|
||||
previewSrc,
|
||||
} from "~/pages/PageEnigma/store/engine";
|
||||
|
||||
// Main editor class that will call everything else all you need to call is " initialize() ".
|
||||
class Editor {
|
||||
@@ -317,7 +321,10 @@ class Editor {
|
||||
this.renderer.setPixelRatio(window.devicePixelRatio);
|
||||
//document.body.appendChild(this.renderer.domElement)
|
||||
window.addEventListener("resize", this.onWindowResize.bind(this));
|
||||
this.renderer.domElement.addEventListener("resize", this.onWindowResize.bind(this));
|
||||
this.renderer.domElement.addEventListener(
|
||||
"resize",
|
||||
this.onWindowResize.bind(this),
|
||||
);
|
||||
|
||||
this._configurePostProcessing();
|
||||
// Controls and movement.
|
||||
@@ -479,7 +486,7 @@ class Editor {
|
||||
]);
|
||||
}
|
||||
|
||||
public async testTestTimelineEvents() { }
|
||||
public async testTestTimelineEvents() {}
|
||||
|
||||
public async loadScene(scene_media_token: string) {
|
||||
this.dispatchAppUiState({
|
||||
@@ -564,7 +571,11 @@ class Editor {
|
||||
);
|
||||
const timeline_json = await proxyTimeline.saveToJson();
|
||||
|
||||
const save_data = { version: this.version, scene: scene_json, timeline: timeline_json };
|
||||
const save_data = {
|
||||
version: this.version,
|
||||
scene: scene_json,
|
||||
timeline: timeline_json,
|
||||
};
|
||||
|
||||
// TODO turn scene information into and object ...
|
||||
const result = await this.api_manager.saveSceneState(
|
||||
@@ -833,23 +844,29 @@ class Editor {
|
||||
|
||||
// Basicly Unity 3D's update loop.
|
||||
async updateLoop() {
|
||||
setTimeout(() => {
|
||||
requestAnimationFrame(this.updateLoop.bind(this));
|
||||
}, 1000 / (this.cap_fps * 2)); // Get the most FPS we can out of the renderer.
|
||||
setTimeout(
|
||||
() => {
|
||||
requestAnimationFrame(this.updateLoop.bind(this));
|
||||
},
|
||||
1000 / (this.cap_fps * 2),
|
||||
); // Get the most FPS we can out of the renderer.
|
||||
|
||||
if (this.container === undefined) {
|
||||
this.container = document.getElementById("video-scene-container");
|
||||
}
|
||||
if (!this.rendering && this.container !== undefined) {
|
||||
if (this.container.clientWidth + this.container.clientHeight !== this.lastCanvasSize) {
|
||||
if (
|
||||
this.container.clientWidth + this.container.clientHeight !==
|
||||
this.lastCanvasSize
|
||||
) {
|
||||
this.onWindowResize();
|
||||
this.lastCanvasSize = this.container.clientWidth + this.container.clientHeight;
|
||||
this.lastCanvasSize =
|
||||
this.container.clientWidth + this.container.clientHeight;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.cam_obj == undefined) {
|
||||
this.cam_obj = this.activeScene.get_object_by_name(this.camera_name);
|
||||
|
||||
}
|
||||
|
||||
// Updates debug stats.
|
||||
@@ -969,10 +986,10 @@ class Editor {
|
||||
audioSegment,
|
||||
"-filter_complex",
|
||||
"[1:a]adelay=" +
|
||||
startTime * 1000 +
|
||||
"|" +
|
||||
startTime * 1000 +
|
||||
"[a1];[0:a][a1]amix=inputs=2[a]",
|
||||
startTime * 1000 +
|
||||
"|" +
|
||||
startTime * 1000 +
|
||||
"[a1];[0:a][a1]amix=inputs=2[a]",
|
||||
"-map",
|
||||
"[a]",
|
||||
`${itteration}final_tmp.wav`,
|
||||
@@ -1461,6 +1478,13 @@ class Editor {
|
||||
}
|
||||
}
|
||||
|
||||
getAssetType(selected: THREE.Object3D<THREE.Object3DEventMap>): AssetType {
|
||||
if (selected.type === "Mesh") {
|
||||
return selected.name === "::CAM::" ? AssetType.CAMERA : AssetType.OBJECT;
|
||||
}
|
||||
return AssetType.CHARACTER;
|
||||
}
|
||||
|
||||
publishSelect() {
|
||||
if (this.selected) {
|
||||
console.log("publish", this.selected);
|
||||
@@ -1468,7 +1492,7 @@ class Editor {
|
||||
queueName: QueueNames.FROM_ENGINE,
|
||||
action: fromEngineActions.SELECT_OBJECT,
|
||||
data: {
|
||||
type: this.selected.type,
|
||||
type: this.getAssetType(this.selected),
|
||||
object_uuid: this.selected.uuid,
|
||||
version: 1,
|
||||
media_id: this.selected.id.toString(),
|
||||
|
||||
Reference in new issue
Block a user