mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-09 19:41:32 +00:00
initial work
This commit is contained in:
1 parent
dbaa6e561b
commit
200d6659e8
74 files changed
+783
-156
No files matched your search
Generated
+8
@@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
Generated
+11
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="SonarLintModuleSettings">
|
||||
<option name="uniqueId" value="235600e7-719d-4c51-a8ab-0dad8280bf12" />
|
||||
</component>
|
||||
</module>
|
||||
Generated
+78
@@ -0,0 +1,78 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<HTMLCodeStyleSettings>
|
||||
<option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
|
||||
</HTMLCodeStyleSettings>
|
||||
<JSCodeStyleSettings version="0">
|
||||
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||
<option name="ENFORCE_TRAILING_COMMA" value="Remove" />
|
||||
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||
</JSCodeStyleSettings>
|
||||
<PHPCodeStyleSettings>
|
||||
<option name="LOWER_CASE_BOOLEAN_CONST" value="true" />
|
||||
<option name="LOWER_CASE_NULL_CONST" value="true" />
|
||||
<option name="ELSE_IF_STYLE" value="COMBINE" />
|
||||
<option name="KEEP_RPAREN_AND_LBRACE_ON_ONE_LINE" value="true" />
|
||||
</PHPCodeStyleSettings>
|
||||
<TypeScriptCodeStyleSettings version="0">
|
||||
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||
<option name="ENFORCE_TRAILING_COMMA" value="Remove" />
|
||||
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||
</TypeScriptCodeStyleSettings>
|
||||
<VueCodeStyleSettings>
|
||||
<option name="INTERPOLATION_NEW_LINE_AFTER_START_DELIMITER" value="false" />
|
||||
<option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" />
|
||||
</VueCodeStyleSettings>
|
||||
<codeStyleSettings language="HTML">
|
||||
<option name="SOFT_MARGINS" value="80" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JavaScript">
|
||||
<option name="SOFT_MARGINS" value="80" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="PHP">
|
||||
<option name="BLANK_LINES_AFTER_PACKAGE" value="1" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
|
||||
<option name="CALL_PARAMETERS_WRAP" value="1" />
|
||||
<option name="METHOD_PARAMETERS_WRAP" value="5" />
|
||||
<option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
|
||||
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
|
||||
<option name="ARRAY_INITIALIZER_WRAP" value="5" />
|
||||
<option name="ARRAY_INITIALIZER_LBRACE_ON_NEXT_LINE" value="true" />
|
||||
<option name="ARRAY_INITIALIZER_RBRACE_ON_NEXT_LINE" value="true" />
|
||||
<option name="IF_BRACE_FORCE" value="3" />
|
||||
<option name="DOWHILE_BRACE_FORCE" value="3" />
|
||||
<option name="WHILE_BRACE_FORCE" value="3" />
|
||||
<option name="FOR_BRACE_FORCE" value="3" />
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="TypeScript">
|
||||
<option name="SOFT_MARGINS" value="80" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="Vue">
|
||||
<option name="SOFT_MARGINS" value="80" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
Generated
+5
@@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
||||
Generated
+8
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/GottaGoFast.iml" filepath="$PROJECT_DIR$/.idea/GottaGoFast.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+19
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MessDetectorOptionsConfiguration">
|
||||
<option name="transferred" value="true" />
|
||||
</component>
|
||||
<component name="PHPCSFixerOptionsConfiguration">
|
||||
<option name="transferred" value="true" />
|
||||
</component>
|
||||
<component name="PHPCodeSnifferOptionsConfiguration">
|
||||
<option name="highlightLevel" value="WARNING" />
|
||||
<option name="transferred" value="true" />
|
||||
</component>
|
||||
<component name="PhpStanOptionsConfiguration">
|
||||
<option name="transferred" value="true" />
|
||||
</component>
|
||||
<component name="PsalmOptionsConfiguration">
|
||||
<option name="transferred" value="true" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+7
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PrettierConfiguration">
|
||||
<option name="myConfigurationMode" value="AUTOMATIC" />
|
||||
<option name="myRunOnSave" value="true" />
|
||||
</component>
|
||||
</project>
|
||||
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Generated
+55
@@ -0,0 +1,55 @@
|
||||
|
||||
9
|
||||
README.md,8/e/8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d
|
||||
L
|
||||
app/contexts/TopBarInner.tsx,b/1/b137962450c33edc117c975cf0bdf9f95b0cb188
|
||||
C
|
||||
app/styles/base.css,c/b/cbc16b6483d87b2c5af91b2f6ee5b84e644e9f88
|
||||
H
|
||||
app/styles/normalize.css,1/1/1193d972f0d2d8e13bf2a89c9f1b856c95dfd960
|
||||
G
|
||||
app/styles/tailwind.css,6/4/64b5dc72a41f7c1b594f0c4f67057c3fac409dba
|
||||
B
|
||||
tailwind.config.ts,5/2/52c961376029165b06e7861b1dc68c3b2455eeff
|
||||
=
|
||||
␍tsconfig.json,6/1/61ebb9fd6e8cf9082658121d5d81e297791dacd0
|
||||
>
|
||||
vite.config.ts,7/5/75ea7f3ea6d80d54bb5b2357996972d1632a5038
|
||||
J
|
||||
app/routes/IdealEnigma.tsx,5/d/5d3ccf1210662cac26b4f75e4524acb75a3e05c9
|
||||
E
|
||||
app/routes/_index.tsx,9/4/948fdf96da2c0e9325e8e3e8f8d14a3350da1b3e
|
||||
S
|
||||
#app/pages/PageEnigma/PageEnigma.tsx,1/4/1420acf507a4c451001a79a2943ae324a632cb7f
|
||||
`
|
||||
0app/pages/PageEnigma/comps/Timeline/Timeline.tsx,8/8/887452a6317b847c87ae01be3bf58418fdeb2e04
|
||||
U
|
||||
%app/modules/LowerPanel/LowerPanel.tsx,9/b/9b3cd0ade34737345060fc9dd9d369dbb2733205
|
||||
b
|
||||
2app/pages/PageEnigma/comps/Character/Character.tsx,a/e/ae6393cb0765a08a83849b56f401b2f8614aa21c
|
||||
]
|
||||
-app/pages/PageEnigma/comps/Timeline/index.tsx,b/a/ba2e6c6255fa02042bec9e54e0f73263850a1fdd
|
||||
^
|
||||
.app/pages/PageEnigma/comps/Character/index.tsx,0/1/01907caaf8abcd59e24d8d26bdd37f7a07238793
|
||||
j
|
||||
:app/pages/PageEnigma/comps/ControlsVideo/ControlsVideo.tsx,1/d/1d2d0feb3a048b584049c5ba95077f97e967bb36
|
||||
f
|
||||
6app/pages/PageEnigma/comps/Character/CharacterClip.tsx,c/2/c2403f3b1e36ca4357db2d4ba8d5cce7a637a0fc
|
||||
a
|
||||
1app/pages/PageEnigma/comps/Timeline/Character.tsx,4/0/4078c3af2bc43a60f7345e34a7230534f9f7b245
|
||||
a
|
||||
1app/pages/PageEnigma/comps/Timeline/TrackClip.tsx,4/f/4fbf6de228c6518666127eb4b8bf99bd053f99fe
|
||||
j
|
||||
:app/pages/PageEnigma/comps/Timeline/utils/onPointerDown.ts,c/7/c77be8c5fbeeea4a34fb2465336598ccff026866
|
||||
k
|
||||
;app/pages/PageEnigma/comps/Timeline/utils/useMouseEvents.ts,e/e/ee716328020ea5d007c5d60a9deed2c398037e16
|
||||
B
|
||||
app/models/clip.ts,8/1/81a15ef2fee7c3e161b66ac6f5bbd6523327059f
|
||||
M
|
||||
app/contexts/TrackContext.tsx,2/a/2ae173334be35fe61e7a084bd67ecfa5b05015e4
|
||||
C
|
||||
app/models/track.ts,f/c/fcfd183a1d0f4cd1a9e368888c85ea2fbe212d56
|
||||
M
|
||||
app/contexts/TrackProvider.ts,5/f/5f22b1bbace0f13f764b3c745759a6b7c4d6e5d9
|
||||
N
|
||||
app/contexts/TrackProvider.tsx,f/4/f4da9805e7b7c03da93082b44221693aec65a52e
|
||||
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
+55
@@ -0,0 +1,55 @@
|
||||
|
||||
9
|
||||
README.md,8/e/8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d
|
||||
L
|
||||
app/contexts/TopBarInner.tsx,b/1/b137962450c33edc117c975cf0bdf9f95b0cb188
|
||||
C
|
||||
app/styles/base.css,c/b/cbc16b6483d87b2c5af91b2f6ee5b84e644e9f88
|
||||
H
|
||||
app/styles/normalize.css,1/1/1193d972f0d2d8e13bf2a89c9f1b856c95dfd960
|
||||
G
|
||||
app/styles/tailwind.css,6/4/64b5dc72a41f7c1b594f0c4f67057c3fac409dba
|
||||
B
|
||||
tailwind.config.ts,5/2/52c961376029165b06e7861b1dc68c3b2455eeff
|
||||
=
|
||||
␍tsconfig.json,6/1/61ebb9fd6e8cf9082658121d5d81e297791dacd0
|
||||
>
|
||||
vite.config.ts,7/5/75ea7f3ea6d80d54bb5b2357996972d1632a5038
|
||||
J
|
||||
app/routes/IdealEnigma.tsx,5/d/5d3ccf1210662cac26b4f75e4524acb75a3e05c9
|
||||
E
|
||||
app/routes/_index.tsx,9/4/948fdf96da2c0e9325e8e3e8f8d14a3350da1b3e
|
||||
S
|
||||
#app/pages/PageEnigma/PageEnigma.tsx,1/4/1420acf507a4c451001a79a2943ae324a632cb7f
|
||||
`
|
||||
0app/pages/PageEnigma/comps/Timeline/Timeline.tsx,8/8/887452a6317b847c87ae01be3bf58418fdeb2e04
|
||||
U
|
||||
%app/modules/LowerPanel/LowerPanel.tsx,9/b/9b3cd0ade34737345060fc9dd9d369dbb2733205
|
||||
b
|
||||
2app/pages/PageEnigma/comps/Character/Character.tsx,a/e/ae6393cb0765a08a83849b56f401b2f8614aa21c
|
||||
]
|
||||
-app/pages/PageEnigma/comps/Timeline/index.tsx,b/a/ba2e6c6255fa02042bec9e54e0f73263850a1fdd
|
||||
^
|
||||
.app/pages/PageEnigma/comps/Character/index.tsx,0/1/01907caaf8abcd59e24d8d26bdd37f7a07238793
|
||||
j
|
||||
:app/pages/PageEnigma/comps/ControlsVideo/ControlsVideo.tsx,1/d/1d2d0feb3a048b584049c5ba95077f97e967bb36
|
||||
f
|
||||
6app/pages/PageEnigma/comps/Character/CharacterClip.tsx,c/2/c2403f3b1e36ca4357db2d4ba8d5cce7a637a0fc
|
||||
a
|
||||
1app/pages/PageEnigma/comps/Timeline/Character.tsx,4/0/4078c3af2bc43a60f7345e34a7230534f9f7b245
|
||||
a
|
||||
1app/pages/PageEnigma/comps/Timeline/TrackClip.tsx,4/f/4fbf6de228c6518666127eb4b8bf99bd053f99fe
|
||||
j
|
||||
:app/pages/PageEnigma/comps/Timeline/utils/onPointerDown.ts,c/7/c77be8c5fbeeea4a34fb2465336598ccff026866
|
||||
k
|
||||
;app/pages/PageEnigma/comps/Timeline/utils/useMouseEvents.ts,e/e/ee716328020ea5d007c5d60a9deed2c398037e16
|
||||
B
|
||||
app/models/clip.ts,8/1/81a15ef2fee7c3e161b66ac6f5bbd6523327059f
|
||||
M
|
||||
app/contexts/TrackContext.tsx,2/a/2ae173334be35fe61e7a084bd67ecfa5b05015e4
|
||||
C
|
||||
app/models/track.ts,f/c/fcfd183a1d0f4cd1a9e368888c85ea2fbe212d56
|
||||
M
|
||||
app/contexts/TrackProvider.ts,5/f/5f22b1bbace0f13f764b3c745759a6b7c4d6e5d9
|
||||
N
|
||||
app/contexts/TrackProvider.tsx,f/4/f4da9805e7b7c03da93082b44221693aec65a52e
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1,20 @@
|
||||
import { createContext } from "react";
|
||||
import { CharacterTrack } from "~/models/track";
|
||||
|
||||
export const TrackContext = createContext<{
|
||||
characters: CharacterTrack[];
|
||||
updateCharacters: (options: {
|
||||
type: "animations" | "positions" | "lipSync";
|
||||
id: string;
|
||||
length: number;
|
||||
offset: number;
|
||||
}) => void;
|
||||
selectClip: (options: {
|
||||
type: "animations" | "positions" | "lipSync";
|
||||
id: string;
|
||||
}) => void;
|
||||
}>({
|
||||
characters: [],
|
||||
updateCharacters: () => {},
|
||||
selectClip: () => {},
|
||||
});
|
||||
@@ -0,0 +1,133 @@
|
||||
import { TrackContext } from "~/contexts/TrackContext";
|
||||
import { ReactNode, useCallback, useMemo, useState } from "react";
|
||||
import { CharacterTrack } from "~/models/track";
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const TrackProvider = ({ children }: Props) => {
|
||||
const [characters, setCharacters] = useState<CharacterTrack[]>([
|
||||
{
|
||||
id: "t1",
|
||||
animationClips: [
|
||||
{
|
||||
id: "1",
|
||||
length: 200,
|
||||
offset: 0,
|
||||
name: "track 1",
|
||||
selected: false,
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
length: 180,
|
||||
offset: 300,
|
||||
name: "track 2",
|
||||
selected: false,
|
||||
},
|
||||
],
|
||||
positionClips: [],
|
||||
lipSyncClips: [],
|
||||
},
|
||||
]);
|
||||
|
||||
const updateCharacters = useCallback(
|
||||
({
|
||||
type,
|
||||
id,
|
||||
offset,
|
||||
length,
|
||||
}: {
|
||||
type: "animations" | "positions" | "lipSync";
|
||||
id: string;
|
||||
length: number;
|
||||
offset: number;
|
||||
}) => {
|
||||
if (type === "animations") {
|
||||
setCharacters((oldCharacters) => {
|
||||
return oldCharacters.map((character) => {
|
||||
const newAnimationClips = [...character.animationClips];
|
||||
const clipIndex = newAnimationClips.findIndex(
|
||||
(row) => row.id === id,
|
||||
);
|
||||
if (clipIndex === -1) {
|
||||
return { ...character };
|
||||
}
|
||||
const clip = newAnimationClips[clipIndex];
|
||||
clip.offset = offset;
|
||||
clip.length = length;
|
||||
return {
|
||||
...character,
|
||||
animationClips: newAnimationClips,
|
||||
};
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const selectClip = useCallback(
|
||||
({
|
||||
type,
|
||||
id,
|
||||
}: {
|
||||
type: "animations" | "positions" | "lipSync";
|
||||
id: string;
|
||||
}) => {
|
||||
if (type === "animations") {
|
||||
setCharacters((oldCharacters) => {
|
||||
return oldCharacters.map((character) => {
|
||||
const newAnimationClips = character.animationClips.map((clip) => ({
|
||||
...clip,
|
||||
selected: false,
|
||||
}));
|
||||
const newPositionClips = character.positionClips.map((clip) => ({
|
||||
...clip,
|
||||
selected: false,
|
||||
}));
|
||||
const newLipSyncClips = character.lipSyncClips.map((clip) => ({
|
||||
...clip,
|
||||
selected: false,
|
||||
}));
|
||||
const clipIndex = newAnimationClips.findIndex(
|
||||
(row) => row.id === id,
|
||||
);
|
||||
if (clipIndex === -1) {
|
||||
return {
|
||||
...character,
|
||||
animationClips: newAnimationClips,
|
||||
positionClips: newPositionClips,
|
||||
lipSyncClips: newLipSyncClips,
|
||||
};
|
||||
}
|
||||
const clip = newAnimationClips[clipIndex];
|
||||
clip.selected = true;
|
||||
return {
|
||||
...character,
|
||||
animationClips: newAnimationClips,
|
||||
positionClips: newPositionClips,
|
||||
lipSyncClips: newLipSyncClips,
|
||||
};
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
console.log(characters);
|
||||
|
||||
const values = useMemo(() => {
|
||||
return {
|
||||
characters,
|
||||
updateCharacters,
|
||||
selectClip,
|
||||
};
|
||||
}, [characters, updateCharacters]);
|
||||
return (
|
||||
<TrackContext.Provider value={values}>{children}</TrackContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
export interface BaseClip {
|
||||
id: string;
|
||||
name: string;
|
||||
offset: number;
|
||||
length: number;
|
||||
selected: boolean;
|
||||
}
|
||||
|
||||
export interface AnimationClip extends BaseClip {}
|
||||
|
||||
export interface PositionClip extends BaseClip {}
|
||||
|
||||
export interface LipSyncClip extends BaseClip {}
|
||||
|
||||
export interface CharacterTrack {
|
||||
id: string;
|
||||
animationClips: AnimationClip[];
|
||||
positionClips: PositionClip[];
|
||||
lipSyncClips: LipSyncClip[];
|
||||
}
|
||||
@@ -1,28 +1,22 @@
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { useCallback, useEffect, useRef } from "react";
|
||||
|
||||
import {
|
||||
faChevronLeft,
|
||||
faWandSparkles,
|
||||
} 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 { Tabs } from "~/modules/Tabs";
|
||||
import { Controls3D } from "./comps/Controls3D";
|
||||
import { ControlsVideo } from "./comps/ControlsVideo";
|
||||
import { Timeline } from "./comps/Timeline";
|
||||
|
||||
import {
|
||||
Button,
|
||||
ButtonLink,
|
||||
} from '~/components';
|
||||
import { ButtonDialogue } from '~/modules/ButtonDialogue';
|
||||
import { TopBarHelmet } from '~/modules/TopBarHelmet/TopBarHelmet';
|
||||
import { SidePanel } from '~/modules/SidePanel';
|
||||
import { Tabs } from '~/modules/Tabs';
|
||||
import { Controls3D } from './comps/Controls3D';
|
||||
import { ControlsVideo } from './comps/ControlsVideo';
|
||||
import { Timeline } from './comps/Timeline';
|
||||
|
||||
import Editor from './js/editor';
|
||||
import Editor from "./js/editor";
|
||||
import { TrackContext } from "~/contexts/TrackContext";
|
||||
import { TrackProvider } from "~/contexts/TrackProvider";
|
||||
|
||||
export const PageEnigma = () => {
|
||||
// const { setTopBarInner } = useContext(TopBarInnerContext) || {};
|
||||
@@ -30,61 +24,54 @@ export const PageEnigma = () => {
|
||||
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||
const editorRef = useRef<Editor | null>(null);
|
||||
|
||||
|
||||
const editorCallback = useCallback(() => {
|
||||
// handle editorCallback here
|
||||
},[]);
|
||||
}, []);
|
||||
|
||||
useEffect(() => { //componentDidMount
|
||||
useEffect(() => {
|
||||
//componentDidMount
|
||||
editorRef.current = new Editor();
|
||||
|
||||
function init(){
|
||||
if(editorRef.current!==null){
|
||||
|
||||
function init() {
|
||||
if (editorRef.current !== null) {
|
||||
//TODO: init with editorRef.current.initialize(editorCallback);
|
||||
editorRef.current.initialize();
|
||||
}
|
||||
}
|
||||
|
||||
if(canvasRef!==null){
|
||||
|
||||
if (canvasRef !== null) {
|
||||
init();
|
||||
}else{
|
||||
} else {
|
||||
setTimeout(init, 500);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleButtonSave = ()=>{
|
||||
const handleButtonSave = () => {
|
||||
editorRef.current?.save();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return(
|
||||
return (
|
||||
<div>
|
||||
<TopBarHelmet>
|
||||
<div className="flex grow justify-between">
|
||||
<ButtonLink
|
||||
to={"/"}
|
||||
variant='secondary'
|
||||
icon={faChevronLeft}
|
||||
>
|
||||
Back to Dashboard
|
||||
</ButtonLink>
|
||||
<Button
|
||||
icon={faWandSparkles}
|
||||
>
|
||||
Generate Movie
|
||||
</Button>
|
||||
<span className="w-8"/>
|
||||
</div>
|
||||
<ButtonLink to={"/"} variant="secondary" icon={faChevronLeft}>
|
||||
Back to Dashboard
|
||||
</ButtonLink>
|
||||
<Button icon={faWandSparkles}>Generate Movie</Button>
|
||||
<span className="w-8" />
|
||||
</div>
|
||||
</TopBarHelmet>
|
||||
<canvas ref={canvasRef} id="video-scene" width="1280px" height="720px" />
|
||||
<div className='fixed top-20 left-4'>
|
||||
<div className="flex mt-2 gap-2">
|
||||
<div className="fixed left-4 top-20">
|
||||
<div className="mt-2 flex gap-2">
|
||||
<Button variant="secondary">Toggle Camera View</Button>
|
||||
<Button variant="secondary" onClick={handleButtonSave}>Save Scene</Button>
|
||||
<Button variant="secondary" onClick={handleButtonSave}>
|
||||
Save Scene
|
||||
</Button>
|
||||
<ButtonDialogue
|
||||
buttonProps={{
|
||||
variant: 'secondary',
|
||||
label: 'Help'
|
||||
variant: "secondary",
|
||||
label: "Help",
|
||||
}}
|
||||
title="Help"
|
||||
>
|
||||
@@ -93,28 +80,35 @@ export const PageEnigma = () => {
|
||||
<p>Ask Miles about ThreeJS</p>
|
||||
<p>Ask Wil about React</p>
|
||||
</ButtonDialogue>
|
||||
<Controls3D/>
|
||||
<ControlsVideo/>
|
||||
<Controls3D />
|
||||
<ControlsVideo />
|
||||
</div>
|
||||
</div>
|
||||
<SidePanel>
|
||||
<Tabs tabs={[
|
||||
{
|
||||
header: 'Animation',
|
||||
children: <p>Animation Tab</p>
|
||||
},{
|
||||
header: 'Camera',
|
||||
children: <p>Camera Tab</p>
|
||||
},{
|
||||
header: 'Audio',
|
||||
children: <p>Audio Tab</p>
|
||||
},{
|
||||
header: 'Styling',
|
||||
children: <p>Styling Tab</p>
|
||||
}
|
||||
]}/>
|
||||
<Tabs
|
||||
tabs={[
|
||||
{
|
||||
header: "Animation",
|
||||
children: <p>Animation Tab</p>,
|
||||
},
|
||||
{
|
||||
header: "Camera",
|
||||
children: <p>Camera Tab</p>,
|
||||
},
|
||||
{
|
||||
header: "Audio",
|
||||
children: <p>Audio Tab</p>,
|
||||
},
|
||||
{
|
||||
header: "Styling",
|
||||
children: <p>Styling Tab</p>,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</SidePanel>
|
||||
<Timeline editorCurrent={editorRef.current}/>
|
||||
<TrackProvider>
|
||||
<Timeline editorCurrent={editorRef.current} />
|
||||
</TrackProvider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
import { TrackClip } from "./TrackClip";
|
||||
import { CharacterTrack } from "~/models/track";
|
||||
|
||||
interface Props {
|
||||
scale: number;
|
||||
time: number;
|
||||
character: CharacterTrack;
|
||||
updateClip: (id: string, offset: number, length: number) => void;
|
||||
}
|
||||
|
||||
export const Character = ({
|
||||
character: { animationClips },
|
||||
updateClip,
|
||||
}: Props) => {
|
||||
return (
|
||||
<div className="bg-charcter-groupBg block h-48 w-full rounded-lg p-2">
|
||||
<div className="mb-2 text-sm text-white">Character</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="px-4">
|
||||
<div className="bg-character-clip relative mt-4 block h-8 w-full rounded">
|
||||
{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
|
||||
: 1000
|
||||
}
|
||||
updateClip={updateClip}
|
||||
clip={clip}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-4">
|
||||
<div className="relative mt-4 block h-8 w-full rounded bg-brand-secondary-800"></div>
|
||||
</div>
|
||||
<div className="px-4">
|
||||
<div className="relative mt-4 block h-8 w-full rounded bg-brand-secondary-800"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,31 +1,43 @@
|
||||
import { LowerPanel } from "~/modules/LowerPanel";
|
||||
import {
|
||||
Button,
|
||||
} from '~/components';
|
||||
import Editor from '../../js/editor';
|
||||
import Editor from "../../js/editor";
|
||||
import { Character } from "./Character";
|
||||
import { useCallback, useContext } from "react";
|
||||
import { TrackContext } from "~/contexts/TrackContext";
|
||||
|
||||
export const Timeline = ({
|
||||
editorCurrent
|
||||
}:{
|
||||
editorCurrent: Editor|null;
|
||||
})=>{
|
||||
const handleButtonLoad = ()=>{
|
||||
document.getElementById("load-upload")?.click();
|
||||
}
|
||||
const handleButtonRender = ()=>{
|
||||
editorCurrent?.togglePlayback();
|
||||
}
|
||||
const handleButtonPlay = ()=>{
|
||||
editorCurrent,
|
||||
}: {
|
||||
editorCurrent: Editor | null;
|
||||
}) => {
|
||||
const { characters, updateCharacters } = useContext(TrackContext);
|
||||
|
||||
}
|
||||
const updateClip = useCallback(
|
||||
(id: string, offset: number, length: number) => {
|
||||
updateCharacters({ type: "animations", id, offset, length });
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
return(
|
||||
return (
|
||||
<LowerPanel>
|
||||
<div className='h-10 w-full border-b border-ui-panel-border'></div>
|
||||
<input style={{ display: 'none' }} type="file" id="load-upload" name="load-upload"></input>
|
||||
<Button onClick={handleButtonLoad}>Load</Button>
|
||||
<Button onClick={handleButtonRender}>Render</Button>
|
||||
<Button onClick={handleButtonPlay}>Play</Button>
|
||||
<div className="h-10 w-full border-b border-ui-panel-border"></div>
|
||||
<input
|
||||
style={{ display: "none" }}
|
||||
type="file"
|
||||
id="load-upload"
|
||||
name="load-upload"
|
||||
></input>
|
||||
<div className="mr-4 p-4">
|
||||
{characters.map((character) => (
|
||||
<Character
|
||||
key={character.id}
|
||||
scale={1}
|
||||
time={0}
|
||||
character={character}
|
||||
updateClip={updateClip}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</LowerPanel>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,65 @@
|
||||
import { useMouseEvents } from "./utils/useMouseEvents";
|
||||
import { BaseClip } from "~/models/track";
|
||||
import { TrackContext } from "~/contexts/TrackContext";
|
||||
import { useContext } from "react";
|
||||
|
||||
interface Props {
|
||||
min: number;
|
||||
max: number;
|
||||
clip: BaseClip;
|
||||
updateClip: (id: string, offset: number, length: number) => void;
|
||||
selected?: boolean;
|
||||
}
|
||||
|
||||
export const TrackClip = ({ clip, min, max, updateClip }: Props) => {
|
||||
const { selectClip } = useContext(TrackContext);
|
||||
const {
|
||||
onPointerDown,
|
||||
onPointerUp,
|
||||
onMouseLeave,
|
||||
onMouseMove,
|
||||
offset,
|
||||
length,
|
||||
} = useMouseEvents(clip, max, min, updateClip);
|
||||
|
||||
if (clip.selected) {
|
||||
console.log("selected", clip);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="absolute text-sm text-white"
|
||||
style={{ top: -20, left: offset }}
|
||||
>
|
||||
{clip.name}
|
||||
</div>
|
||||
<button
|
||||
className={`bg-character-${clip.selected ? "selected" : "unselected"} absolute block h-full rounded-l`}
|
||||
style={{ width: 15, left: offset, cursor: "w-resize" }}
|
||||
onPointerDown={(event) => onPointerDown(event, "left")}
|
||||
onPointerUp={onPointerUp}
|
||||
onMouseLeave={onMouseLeave}
|
||||
onMouseMove={onMouseMove}
|
||||
onClick={() => selectClip({ type: "animations", id: clip.id })}
|
||||
/>
|
||||
<button
|
||||
className={`bg-character-${clip.selected ? "selected" : "unselected"} absolute block h-full`}
|
||||
style={{ width: length - 30, left: offset + 15, cursor: "move" }}
|
||||
onPointerDown={(event) => onPointerDown(event, "drag")}
|
||||
onPointerUp={onPointerUp}
|
||||
onMouseLeave={onMouseLeave}
|
||||
onMouseMove={onMouseMove}
|
||||
onClick={() => selectClip({ type: "animations", id: clip.id })}
|
||||
/>
|
||||
<button
|
||||
className={`bg-character-${clip.selected ? "selected" : "unselected"} absolute block h-full rounded-r`}
|
||||
style={{ width: 15, left: offset + length - 15, cursor: "e-resize" }}
|
||||
onPointerDown={(event) => onPointerDown(event, "right")}
|
||||
onPointerUp={onPointerUp}
|
||||
onMouseLeave={onMouseLeave}
|
||||
onMouseMove={onMouseMove}
|
||||
onClick={() => selectClip({ type: "animations", id: clip.id })}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
import { useState, PointerEvent, MouseEvent } from "react";
|
||||
|
||||
export const useMouseEvents = (
|
||||
clip: { id: string; offset: number; length: number; name: string },
|
||||
max: number,
|
||||
min: number,
|
||||
updateClip: (id: string, offset: number, length: number) => void,
|
||||
) => {
|
||||
const [isActive, setIsActive] = useState("");
|
||||
const [clientX, setClientX] = useState(0);
|
||||
|
||||
const [offset, setOffset] = useState(clip.offset);
|
||||
const [length, setLength] = useState(clip.length);
|
||||
|
||||
return {
|
||||
onPointerDown: (event: PointerEvent<HTMLButtonElement>, type: string) => {
|
||||
if (event.button === 0) {
|
||||
const x = event.clientX - 40;
|
||||
setClientX(event.clientX);
|
||||
setIsActive(type);
|
||||
}
|
||||
},
|
||||
onPointerUp: () => {
|
||||
if (isActive) {
|
||||
updateClip(clip.id, offset, length);
|
||||
setIsActive("");
|
||||
}
|
||||
},
|
||||
onMouseLeave: () => {
|
||||
if (isActive) {
|
||||
updateClip(clip.id, offset, length);
|
||||
setIsActive("");
|
||||
}
|
||||
},
|
||||
onMouseMove: (event: MouseEvent) => {
|
||||
const deltaOffset = event.clientX - clientX + clip.offset;
|
||||
const delta = event.clientX - clientX;
|
||||
if (isActive === "drag") {
|
||||
if (deltaOffset < min || deltaOffset + length > max) {
|
||||
return;
|
||||
}
|
||||
setOffset(deltaOffset);
|
||||
return;
|
||||
}
|
||||
if (isActive === "left") {
|
||||
if (deltaOffset < min) {
|
||||
return;
|
||||
}
|
||||
setOffset(deltaOffset);
|
||||
setLength(clip.length - delta);
|
||||
return;
|
||||
}
|
||||
if (isActive === "right") {
|
||||
if (clip.length + delta < 30 || offset + clip.length + delta > max) {
|
||||
return;
|
||||
}
|
||||
setLength(clip.length + delta);
|
||||
}
|
||||
},
|
||||
length,
|
||||
offset,
|
||||
};
|
||||
};
|
||||
+75
-63
@@ -1,5 +1,5 @@
|
||||
import type { Config } from 'tailwindcss';
|
||||
import colors from 'tailwindcss/colors';
|
||||
import type { Config } from "tailwindcss";
|
||||
import colors from "tailwindcss/colors";
|
||||
|
||||
export default {
|
||||
content: [
|
||||
@@ -9,75 +9,87 @@ export default {
|
||||
],
|
||||
theme: {
|
||||
// extend: {},
|
||||
colors:{
|
||||
transparent: 'transparent',
|
||||
current: 'currentColor',
|
||||
colors: {
|
||||
transparent: "transparent",
|
||||
current: "currentColor",
|
||||
|
||||
'brand-primary': { //sunglo
|
||||
DEFAULT: '#e66462', //=500
|
||||
'50': '#fdf3f3',
|
||||
'100': '#fce4e4',
|
||||
'200': '#facfce',
|
||||
'300': '#f6acab',
|
||||
'400': '#ef7c7a',
|
||||
'500': '#e66462',
|
||||
'600': '#cf3533',
|
||||
'700': '#ae2927',
|
||||
'800': '#902624',
|
||||
'900': '#782524',
|
||||
'950': '#410f0e',
|
||||
"brand-primary": {
|
||||
//sunglo
|
||||
DEFAULT: "#e66462", //=500
|
||||
"50": "#fdf3f3",
|
||||
"100": "#fce4e4",
|
||||
"200": "#facfce",
|
||||
"300": "#f6acab",
|
||||
"400": "#ef7c7a",
|
||||
"500": "#e66462",
|
||||
"600": "#cf3533",
|
||||
"700": "#ae2927",
|
||||
"800": "#902624",
|
||||
"900": "#782524",
|
||||
"950": "#410f0e",
|
||||
},
|
||||
'brand-secondary': { //gunpowder
|
||||
DEFAULT: '#39394c', //=950
|
||||
'50': '#f5f6f9',
|
||||
'100': '#e8eaf1',
|
||||
'200': '#d7dbe6',
|
||||
'300': '#bbc1d5',
|
||||
'400': '#9aa2c0',
|
||||
'500': '#8188b0',
|
||||
'600': '#6f74a1',
|
||||
'700': '#636492',
|
||||
'800': '#545579',
|
||||
'900': '#464762',
|
||||
'950': '#39394c',
|
||||
"brand-secondary": {
|
||||
//gunpowder
|
||||
DEFAULT: "#39394c", //=950
|
||||
"50": "#f5f6f9",
|
||||
"100": "#e8eaf1",
|
||||
"200": "#d7dbe6",
|
||||
"300": "#bbc1d5",
|
||||
"400": "#9aa2c0",
|
||||
"500": "#8188b0",
|
||||
"600": "#6f74a1",
|
||||
"700": "#636492",
|
||||
"800": "#545579",
|
||||
"900": "#464762",
|
||||
"950": "#39394c",
|
||||
},
|
||||
danger: { //copper rust
|
||||
DEFAULT: '#8f4951', //=700
|
||||
'50': '#fbf5f5',
|
||||
'100': '#f7ecec',
|
||||
'200': '#efdcdc',
|
||||
'300': '#e2c0bf',
|
||||
'400': '#d19b9c',
|
||||
'500': '#bd7679',
|
||||
'600': '#a5595f',
|
||||
'700': '#8f4951',
|
||||
'800': '#743d45',
|
||||
'900': '#64373f',
|
||||
'950': '#361b1e',
|
||||
danger: {
|
||||
//copper rust
|
||||
DEFAULT: "#8f4951", //=700
|
||||
"50": "#fbf5f5",
|
||||
"100": "#f7ecec",
|
||||
"200": "#efdcdc",
|
||||
"300": "#e2c0bf",
|
||||
"400": "#d19b9c",
|
||||
"500": "#bd7679",
|
||||
"600": "#a5595f",
|
||||
"700": "#8f4951",
|
||||
"800": "#743d45",
|
||||
"900": "#64373f",
|
||||
"950": "#361b1e",
|
||||
},
|
||||
|
||||
success:{ //apple
|
||||
DEFAULT: '#40ad48', //=500
|
||||
'50': '#f2fbf2',
|
||||
'100': '#e2f6e3',
|
||||
'200': '#c5edc7',
|
||||
'300': '#98dd9d',
|
||||
'400': '#64c46b',
|
||||
'500': '#40ad48',
|
||||
'600': '#2f8a35',
|
||||
'700': '#286d2e',
|
||||
'800': '#245728',
|
||||
'900': '#1f4823',
|
||||
'950': '#0c270f',
|
||||
success: {
|
||||
//apple
|
||||
DEFAULT: "#40ad48", //=500
|
||||
"50": "#f2fbf2",
|
||||
"100": "#e2f6e3",
|
||||
"200": "#c5edc7",
|
||||
"300": "#98dd9d",
|
||||
"400": "#64c46b",
|
||||
"500": "#40ad48",
|
||||
"600": "#2f8a35",
|
||||
"700": "#286d2e",
|
||||
"800": "#245728",
|
||||
"900": "#1f4823",
|
||||
"950": "#0c270f",
|
||||
},
|
||||
|
||||
'ui-background': '#1a1a27',
|
||||
'ui-panel': '#242433',
|
||||
'ui-panel-border': '#5e6097',
|
||||
character: {
|
||||
unselected: "#46527C",
|
||||
selected: "#6384F4",
|
||||
clip: "#7E92DA",
|
||||
groupBg: "#2B3448",
|
||||
},
|
||||
|
||||
"ui-background": "#1a1a27",
|
||||
"ui-panel": "#242433",
|
||||
"ui-panel-border": "#5e6097",
|
||||
|
||||
gray: colors.gray,
|
||||
white: colors.white
|
||||
}
|
||||
white: colors.white,
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
} satisfies Config
|
||||
safelist: ["bg-character-selected", "bg-character-unselected"],
|
||||
} satisfies Config;
|
||||
Reference in new issue
Block a user