reacthub-pixitweenjs 0.2.3 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/{main.js → main.ts} +9 -1
- package/.storybook/{preview.jsx → preview.tsx} +3 -7
- package/CHANGELOG.md +40 -0
- package/README.md +21 -2
- package/build-storybook.log +94 -0
- package/index.html +1 -1
- package/package.json +68 -36
- package/src/App.tsx +29 -0
- package/src/components/PixiAnimatedSprite/PixiAnimatedSprite.tsx +51 -0
- package/src/components/PixiAnimatedSprite/index.ts +2 -0
- package/src/components/PixiAudioSprite/PixiAudioSprite.stories.tsx +61 -0
- package/src/components/PixiAudioSprite/PixiAudioSprite.tsx +212 -0
- package/src/components/PixiAudioSprite/index.ts +1 -0
- package/src/components/PixiGifSprite/PixiGifSprite.stories.tsx +135 -0
- package/src/components/PixiGifSprite/PixiGifSprite.tsx +451 -0
- package/src/components/PixiGifSprite/index.ts +2 -0
- package/src/{stories/PixiImageSprite/PixiImageSprite.stories.js → components/PixiImageSprite/PixiImageSprite.stories.tsx} +36 -8
- package/src/components/PixiImageSprite/PixiImageSprite.tsx +340 -0
- package/src/components/PixiImageSprite/index.ts +1 -0
- package/src/components/PixiSVGSprite/PixiSvgPathSprite.stories.tsx +137 -0
- package/src/components/PixiSVGSprite/PixiSvgPathSprite.tsx +378 -0
- package/src/components/PixiSVGSprite/index.ts +1 -0
- package/src/components/PixiVideoSprite/PixiVideoSprite.stories.tsx +147 -0
- package/src/components/PixiVideoSprite/PixiVideoSprite.tsx +684 -0
- package/src/components/PixiVideoSprite/PixiVideoSpriteBackup.txt +755 -0
- package/src/components/PixiVideoSprite/index.ts +1 -0
- package/src/declare.module.ts +5 -0
- package/src/hooks/usePixiTransformer.tsx +168 -0
- package/src/index.ts +3 -0
- package/src/providers/GsapPixieContextProvider.tsx +251 -0
- package/src/providers/index.ts +1 -0
- package/src/reacthub-react-bindings.d.ts +27 -0
- package/src/types/transformation.tsx +13 -0
- package/src/utils/AppStateProvider.tsx +59 -0
- package/src/utils/AppStateStage.tsx +27 -0
- package/src/utils/AppWrapper.tsx +43 -0
- package/src/utils/ContextBridge.tsx +25 -0
- package/src/{providers/GsapAnim.jsx → utils/GsapAnim.tsx} +35 -4
- package/src/utils/PixiStage.tsx +29 -0
- package/src/utils/PixiTransformer.tsx +100 -0
- package/src/utils/VideoSeekBar.tsx +129 -0
- package/src/utils/useDebounce.tsx +15 -0
- package/src/utils/useDeepEffect.tsx +23 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +37 -0
- package/tsconfig.node.json +10 -0
- package/{vite.config.js → vite.config.ts} +4 -2
- package/vitest.config.ts +11 -0
- package/src/App.jsx +0 -35
- package/src/index.js +0 -3
- package/src/providers/ContextBridge.jsx +0 -67
- package/src/providers/GsapPixieContextProvider.jsx +0 -162
- package/src/providers/VideoSeekBar.jsx +0 -186
- package/src/providers/index.js +0 -3
- package/src/stories/PixiImageSprite/PixiImageSprite.jsx +0 -318
- package/src/stories/PixiImageSprite/index.js +0 -1
- /package/src/{main.jsx → main.tsx} +0 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import ContextBridge from "./ContextBridge";
|
|
3
|
+
import { Stage as PixiStage } from "@pixi/react";
|
|
4
|
+
import { GsapPixieContext } from "../providers/GsapPixieContextProvider";
|
|
5
|
+
|
|
6
|
+
interface StageProps {
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
options: any;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const Stage: React.FC<StageProps> = ({ children, ...props }) => {
|
|
14
|
+
return (
|
|
15
|
+
<ContextBridge
|
|
16
|
+
Context={GsapPixieContext}
|
|
17
|
+
render={(children: React.ReactNode | React.ReactElement) => (
|
|
18
|
+
<PixiStage {...props}>
|
|
19
|
+
{/* @ts-ignore */}
|
|
20
|
+
{children}
|
|
21
|
+
</PixiStage>
|
|
22
|
+
)}
|
|
23
|
+
>
|
|
24
|
+
{children}
|
|
25
|
+
</ContextBridge>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default Stage;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import * as PIXI from "pixi.js";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
// @ts-ignore
|
|
4
|
+
import { Transformer } from "reacthub-react-bindings";
|
|
5
|
+
|
|
6
|
+
type PixiTransformerProps = {
|
|
7
|
+
pixiTransformerRef: React.MutableRefObject<any>;
|
|
8
|
+
imageRef: React.MutableRefObject<any>;
|
|
9
|
+
isMounted: boolean;
|
|
10
|
+
transformCommit?: (data: any) => void;
|
|
11
|
+
transformChange?: (data: any) => void;
|
|
12
|
+
mouseoverEvent?: (flag: boolean) => void;
|
|
13
|
+
uniqueId?: string;
|
|
14
|
+
};
|
|
15
|
+
const PixiTransformer = ({
|
|
16
|
+
pixiTransformerRef,
|
|
17
|
+
imageRef,
|
|
18
|
+
isMounted,
|
|
19
|
+
transformCommit,
|
|
20
|
+
transformChange,
|
|
21
|
+
mouseoverEvent,
|
|
22
|
+
uniqueId = "",
|
|
23
|
+
}: PixiTransformerProps) => {
|
|
24
|
+
const colorYellow = PIXI.utils.string2hex("#F3C409");
|
|
25
|
+
const anchorFill = PIXI.utils.string2hex("#F3C409");
|
|
26
|
+
|
|
27
|
+
const handleOnMouseOver = React.useCallback(() => {
|
|
28
|
+
if (mouseoverEvent) {
|
|
29
|
+
mouseoverEvent(true);
|
|
30
|
+
}
|
|
31
|
+
}, [mouseoverEvent]);
|
|
32
|
+
|
|
33
|
+
const handleOnMouseOut = React.useCallback(() => {
|
|
34
|
+
if (mouseoverEvent) {
|
|
35
|
+
mouseoverEvent(false);
|
|
36
|
+
}
|
|
37
|
+
}, [mouseoverEvent]);
|
|
38
|
+
|
|
39
|
+
const handleOnTransformCommit = React.useCallback(() => {
|
|
40
|
+
if (imageRef.current === null) return;
|
|
41
|
+
const x = imageRef.current.x;
|
|
42
|
+
const y = imageRef.current.y;
|
|
43
|
+
const width = imageRef.current.width;
|
|
44
|
+
const height = imageRef.current.height;
|
|
45
|
+
const rotate = imageRef.current.rotation;
|
|
46
|
+
const scale: [number, number] = [
|
|
47
|
+
imageRef.current.scale.x,
|
|
48
|
+
imageRef.current.scale.y,
|
|
49
|
+
];
|
|
50
|
+
if (transformCommit) {
|
|
51
|
+
transformCommit({
|
|
52
|
+
uniqueId,
|
|
53
|
+
transformation: {
|
|
54
|
+
x: Math.round(x),
|
|
55
|
+
y: Math.round(y),
|
|
56
|
+
width: Math.round(Math.max(5, width)),
|
|
57
|
+
height: Math.round(Math.max(5, height)),
|
|
58
|
+
rotate: Math.round(rotate),
|
|
59
|
+
scale: scale,
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}, [transformCommit]);
|
|
64
|
+
|
|
65
|
+
React.useEffect(() => {
|
|
66
|
+
if (pixiTransformerRef.current) {
|
|
67
|
+
pixiTransformerRef.current.on("mouseover", handleOnMouseOver);
|
|
68
|
+
pixiTransformerRef.current.on("mouseout", handleOnMouseOut);
|
|
69
|
+
}
|
|
70
|
+
return () => {
|
|
71
|
+
if (pixiTransformerRef.current) {
|
|
72
|
+
pixiTransformerRef.current.off("mouseover", handleOnMouseOver);
|
|
73
|
+
pixiTransformerRef.current.off("mouseout", handleOnMouseOut);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}, []);
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<Transformer
|
|
80
|
+
ref={pixiTransformerRef}
|
|
81
|
+
group={isMounted ? [imageRef.current] : []}
|
|
82
|
+
rotateEnabled={true}
|
|
83
|
+
boxRotationEnabled={true}
|
|
84
|
+
boxScalingEnabled={true}
|
|
85
|
+
wireframeStyle={{ thickness: 1, color: anchorFill }}
|
|
86
|
+
lockAspectRatio={true}
|
|
87
|
+
scaleEnabled={true}
|
|
88
|
+
handleStyle={{
|
|
89
|
+
// color: colorYellow,
|
|
90
|
+
outlineColor: colorYellow,
|
|
91
|
+
shape: "circle",
|
|
92
|
+
radius: 9,
|
|
93
|
+
}}
|
|
94
|
+
transformchange={transformChange}
|
|
95
|
+
transformcommit={handleOnTransformCommit}
|
|
96
|
+
/>
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export default PixiTransformer;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import {
|
|
3
|
+
Slider,
|
|
4
|
+
SliderTrack,
|
|
5
|
+
SliderFilledTrack,
|
|
6
|
+
SliderThumb,
|
|
7
|
+
SliderMark,
|
|
8
|
+
Flex,
|
|
9
|
+
Icon,
|
|
10
|
+
Text,
|
|
11
|
+
Box,
|
|
12
|
+
Button,
|
|
13
|
+
} from "@chakra-ui/react";
|
|
14
|
+
import {
|
|
15
|
+
GsapPixieContext,
|
|
16
|
+
Events,
|
|
17
|
+
} from "../providers/GsapPixieContextProvider";
|
|
18
|
+
import { useCustomEventListener, emitCustomEvent } from "react-custom-events";
|
|
19
|
+
|
|
20
|
+
import { gsap } from "gsap";
|
|
21
|
+
import { Draggable } from "gsap/Draggable";
|
|
22
|
+
|
|
23
|
+
gsap.registerPlugin(Draggable);
|
|
24
|
+
|
|
25
|
+
export const VideoSeekBar: React.FC = () => {
|
|
26
|
+
const rafRef = React.useRef<number>();
|
|
27
|
+
const sliderRef = React.useRef<HTMLDivElement>(null);
|
|
28
|
+
const sliderThumbRef = React.useRef<HTMLDivElement>(null);
|
|
29
|
+
const sliderProgressRef = React.useRef<HTMLDivElement>(null);
|
|
30
|
+
const {
|
|
31
|
+
tl: timeline,
|
|
32
|
+
playerTimeRef,
|
|
33
|
+
handlePlay,
|
|
34
|
+
handlePause,
|
|
35
|
+
handleReset,
|
|
36
|
+
handleSeek,
|
|
37
|
+
handleSeekTest,
|
|
38
|
+
handleRestart,
|
|
39
|
+
handleRepeat,
|
|
40
|
+
totalDuration: duration,
|
|
41
|
+
} = React.useContext(GsapPixieContext);
|
|
42
|
+
|
|
43
|
+
/// state
|
|
44
|
+
const [time, setTime] = React.useState<number>(0);
|
|
45
|
+
const totalVideoSeconds = 20;
|
|
46
|
+
|
|
47
|
+
const animate = (time: number) => {
|
|
48
|
+
/// console.log("totalDuration", timeline.current.totalDuration());
|
|
49
|
+
if (playerTimeRef && playerTimeRef.current) {
|
|
50
|
+
setTime(playerTimeRef.current);
|
|
51
|
+
}
|
|
52
|
+
rafRef.current = requestAnimationFrame(animate);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
React.useEffect(() => {
|
|
56
|
+
rafRef.current = requestAnimationFrame(animate);
|
|
57
|
+
return () => cancelAnimationFrame(rafRef.current!);
|
|
58
|
+
}, []); // Make sure the effect runs only once
|
|
59
|
+
|
|
60
|
+
const handleOnSliderChange = (value: number) => {
|
|
61
|
+
console.log("onslider change", value);
|
|
62
|
+
const nTotSeconds =
|
|
63
|
+
totalVideoSeconds && totalVideoSeconds > 0 ? totalVideoSeconds : 1;
|
|
64
|
+
const progress = value / nTotSeconds;
|
|
65
|
+
console.log("progressbar", value, progress);
|
|
66
|
+
|
|
67
|
+
playerTimeRef.current = value;
|
|
68
|
+
// handleSeek(value);
|
|
69
|
+
// // @ts-ignore
|
|
70
|
+
timeline && timeline.current.progress(progress);
|
|
71
|
+
// timeline && timeline.current.pause();
|
|
72
|
+
// gsap.set(sliderProgressRef.current!, {
|
|
73
|
+
// scaleX: progress,
|
|
74
|
+
// });
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/** on slider start
|
|
78
|
+
* 1. pause the timeline
|
|
79
|
+
* 2. trigger custom event isDraggingStart
|
|
80
|
+
*/
|
|
81
|
+
const handleOnSliderChangeStart = (value: number) => {
|
|
82
|
+
// trigger custom event and pause the timeline
|
|
83
|
+
emitCustomEvent(Events.DRAGGING_START);
|
|
84
|
+
timeline && timeline.current.pause();
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const handleOnSliderChangeEnd = (value: number) => {
|
|
88
|
+
// trigger custom event and pause the timeline
|
|
89
|
+
emitCustomEvent(Events.DRAGGING_END);
|
|
90
|
+
timeline && timeline.current.pause();
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/** Create Draggable for the Gsap Player */
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<Flex flexDir={"column"}>
|
|
97
|
+
<Box>
|
|
98
|
+
<Button onClick={handleRestart}>Play</Button>
|
|
99
|
+
<Button onClick={handlePause}>Pause</Button>
|
|
100
|
+
<Button onClick={handlePlay}>Resume</Button>
|
|
101
|
+
<Button onClick={handleSeekTest}>Seek (4:00)</Button>
|
|
102
|
+
</Box>
|
|
103
|
+
<Flex px={1} mt={2}>
|
|
104
|
+
<Slider
|
|
105
|
+
aria-label="slider-ex-1"
|
|
106
|
+
data-testid="sliderthumb"
|
|
107
|
+
value={playerTimeRef.current || 0}
|
|
108
|
+
min={0}
|
|
109
|
+
max={10}
|
|
110
|
+
step={0.1}
|
|
111
|
+
onChange={handleOnSliderChange}
|
|
112
|
+
onChangeStart={handleOnSliderChangeStart}
|
|
113
|
+
onChangeEnd={handleOnSliderChangeEnd}
|
|
114
|
+
>
|
|
115
|
+
<SliderTrack>
|
|
116
|
+
<SliderFilledTrack />
|
|
117
|
+
</SliderTrack>
|
|
118
|
+
<SliderThumb data-testid="sliderthumb" ref={sliderThumbRef} />
|
|
119
|
+
</Slider>
|
|
120
|
+
</Flex>
|
|
121
|
+
<Flex flexDir={"row"}>
|
|
122
|
+
<Text>
|
|
123
|
+
Gsap-CurrentTime: {Math.round((time + Number.EPSILON) * 100) / 100} /{" "}
|
|
124
|
+
{duration}
|
|
125
|
+
</Text>
|
|
126
|
+
</Flex>
|
|
127
|
+
</Flex>
|
|
128
|
+
);
|
|
129
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
|
|
3
|
+
export function useDebounce<T>(value: T, delay?: number): T {
|
|
4
|
+
const [debouncedValue, setDebouncedValue] = useState<T>(value);
|
|
5
|
+
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
const timer = setTimeout(() => setDebouncedValue(value), delay || 500);
|
|
8
|
+
|
|
9
|
+
return () => {
|
|
10
|
+
clearTimeout(timer);
|
|
11
|
+
};
|
|
12
|
+
}, [value, delay]);
|
|
13
|
+
|
|
14
|
+
return debouncedValue;
|
|
15
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
// @ts-ignore
|
|
3
|
+
import isEqual from "react-fast-compare";
|
|
4
|
+
|
|
5
|
+
type EffectFunc = () => void;
|
|
6
|
+
type Deps = ReadonlyArray<unknown>;
|
|
7
|
+
|
|
8
|
+
const useDeepEffect = (effectFunc: EffectFunc, deps: Deps) => {
|
|
9
|
+
const isFirst = useRef(true);
|
|
10
|
+
const prevDeps = useRef(deps);
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const isSame = prevDeps.current.every((obj, index) =>
|
|
13
|
+
isEqual(obj, deps[index]),
|
|
14
|
+
);
|
|
15
|
+
if (isFirst.current || !isSame) {
|
|
16
|
+
effectFunc();
|
|
17
|
+
}
|
|
18
|
+
isFirst.current = false;
|
|
19
|
+
prevDeps.current = deps;
|
|
20
|
+
}, deps);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default useDeepEffect;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
|
5
|
+
"plugins": [
|
|
6
|
+
{
|
|
7
|
+
"name": "typescript-plugin-css-modules",
|
|
8
|
+
"options": {
|
|
9
|
+
"postcssOptions": {
|
|
10
|
+
"useConfig": true
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"module": "ESNext",
|
|
16
|
+
"skipLibCheck": true,
|
|
17
|
+
/* Bundler mode */
|
|
18
|
+
"moduleResolution": "node",
|
|
19
|
+
"allowImportingTsExtensions": false,
|
|
20
|
+
"resolveJsonModule": true,
|
|
21
|
+
"isolatedModules": true,
|
|
22
|
+
"noEmit": true,
|
|
23
|
+
"jsx": "react-jsx",
|
|
24
|
+
"types": ["vitest/globals"],
|
|
25
|
+
/* Linting */
|
|
26
|
+
"strict": true,
|
|
27
|
+
"noUnusedLocals": true,
|
|
28
|
+
"noUnusedParameters": true,
|
|
29
|
+
"noFallthroughCasesInSwitch": true
|
|
30
|
+
},
|
|
31
|
+
"include": ["src"],
|
|
32
|
+
"references": [
|
|
33
|
+
{
|
|
34
|
+
"path": "./tsconfig.node.json"
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { defineConfig } from "vite";
|
|
2
|
-
import react from "@vitejs/plugin-react";
|
|
2
|
+
// import react from "@vitejs/plugin-react";
|
|
3
|
+
import react from "@vitejs/plugin-react-swc";
|
|
4
|
+
import EnvironmentPlugin from "vite-plugin-environment";
|
|
3
5
|
|
|
4
6
|
// https://vitejs.dev/config/
|
|
5
7
|
export default defineConfig({
|
|
6
|
-
plugins: [react()],
|
|
8
|
+
plugins: [react(), EnvironmentPlugin("all")],
|
|
7
9
|
// build: {
|
|
8
10
|
// rollupOptions: {
|
|
9
11
|
// external: [
|
package/vitest.config.ts
ADDED
package/src/App.jsx
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { useState } from 'react'
|
|
2
|
-
import reactLogo from './assets/react.svg'
|
|
3
|
-
import viteLogo from '/vite.svg'
|
|
4
|
-
import './App.css'
|
|
5
|
-
|
|
6
|
-
function App() {
|
|
7
|
-
const [count, setCount] = useState(0)
|
|
8
|
-
|
|
9
|
-
return (
|
|
10
|
-
<div className="App">
|
|
11
|
-
<div>
|
|
12
|
-
<a href="https://vitejs.dev" target="_blank">
|
|
13
|
-
<img src={viteLogo} className="logo" alt="Vite logo" />
|
|
14
|
-
</a>
|
|
15
|
-
<a href="https://reactjs.org" target="_blank">
|
|
16
|
-
<img src={reactLogo} className="logo react" alt="React logo" />
|
|
17
|
-
</a>
|
|
18
|
-
</div>
|
|
19
|
-
<h1>Vite + React</h1>
|
|
20
|
-
<div className="card">
|
|
21
|
-
<button onClick={() => setCount((count) => count + 1)}>
|
|
22
|
-
count is {count}
|
|
23
|
-
</button>
|
|
24
|
-
<p>
|
|
25
|
-
Edit <code>src/App.jsx</code> and save to test HMR
|
|
26
|
-
</p>
|
|
27
|
-
</div>
|
|
28
|
-
<p className="read-the-docs">
|
|
29
|
-
Click on the Vite and React logos to learn more
|
|
30
|
-
</p>
|
|
31
|
-
</div>
|
|
32
|
-
)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export default App
|
package/src/index.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
// your Stage:
|
|
2
|
-
import { Stage as PixiStage } from "@pixi/react";
|
|
3
|
-
import React from "react";
|
|
4
|
-
import { Flex, Icon, Text, Box, Button } from "@chakra-ui/react";
|
|
5
|
-
import * as PIXI from "pixi.js";
|
|
6
|
-
import { ChakraProvider } from "@chakra-ui/react";
|
|
7
|
-
import {
|
|
8
|
-
GsapPixieContext,
|
|
9
|
-
GsapPixieContextProvider,
|
|
10
|
-
} from "./GsapPixieContextProvider";
|
|
11
|
-
|
|
12
|
-
import { VideoSeekBar } from "./VideoSeekBar";
|
|
13
|
-
|
|
14
|
-
// the context bridge:
|
|
15
|
-
const ContextBridge = ({ children, Context, render }) => {
|
|
16
|
-
return (
|
|
17
|
-
<Context.Consumer>
|
|
18
|
-
{(value) => {
|
|
19
|
-
console.log("value", value);
|
|
20
|
-
return render(
|
|
21
|
-
<Context.Provider value={value}>{children}</Context.Provider>,
|
|
22
|
-
);
|
|
23
|
-
}}
|
|
24
|
-
</Context.Consumer>
|
|
25
|
-
);
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const Stage = ({ children, ...props }) => {
|
|
29
|
-
return (
|
|
30
|
-
<ContextBridge
|
|
31
|
-
Context={GsapPixieContext}
|
|
32
|
-
render={(children) => <PixiStage {...props}>{children}</PixiStage>}
|
|
33
|
-
>
|
|
34
|
-
{children}
|
|
35
|
-
</ContextBridge>
|
|
36
|
-
);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
export const App = ({ children, backgroundColor, ...props }) => {
|
|
40
|
-
const backgroundColorx = PIXI.utils.string2hex(backgroundColor || "#2D2E3C");
|
|
41
|
-
const width = 600;
|
|
42
|
-
const height = 500;
|
|
43
|
-
|
|
44
|
-
/// color: 0x1099bb,
|
|
45
|
-
return (
|
|
46
|
-
<div className="App">
|
|
47
|
-
<GsapPixieContextProvider>
|
|
48
|
-
<ChakraProvider>
|
|
49
|
-
<Flex flexDir={"column"}>
|
|
50
|
-
<Stage
|
|
51
|
-
width={width}
|
|
52
|
-
height={height}
|
|
53
|
-
// options={{ background: backgroundColor }}
|
|
54
|
-
options={{ backgroundColor: backgroundColorx, resolution: 2 }}
|
|
55
|
-
>
|
|
56
|
-
{children}
|
|
57
|
-
</Stage>
|
|
58
|
-
|
|
59
|
-
<Box mt={5} w={600}>
|
|
60
|
-
<VideoSeekBar />
|
|
61
|
-
</Box>
|
|
62
|
-
</Flex>
|
|
63
|
-
</ChakraProvider>
|
|
64
|
-
</GsapPixieContextProvider>
|
|
65
|
-
</div>
|
|
66
|
-
);
|
|
67
|
-
};
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
useState,
|
|
3
|
-
useEffect,
|
|
4
|
-
useCallback,
|
|
5
|
-
useRef,
|
|
6
|
-
useLayoutEffect,
|
|
7
|
-
} from "react";
|
|
8
|
-
import gsap from "gsap";
|
|
9
|
-
import { emitCustomEvent } from "react-custom-events";
|
|
10
|
-
// import { Power4 } from "gsap";
|
|
11
|
-
|
|
12
|
-
// Context has been created
|
|
13
|
-
const GsapPixieContext = React.createContext({});
|
|
14
|
-
|
|
15
|
-
const Events = {
|
|
16
|
-
STOP: "GSAP_STOP",
|
|
17
|
-
PAUSE: "GSAP_PAUSE",
|
|
18
|
-
PLAY: "GSAP_PLAY",
|
|
19
|
-
RESUME: "GSAP_RESUME",
|
|
20
|
-
};
|
|
21
|
-
// Provider
|
|
22
|
-
const GsapPixieContextProvider = ({ children }) => {
|
|
23
|
-
const [play, setPlay] = useState(false);
|
|
24
|
-
const [frameNumber, setFrameNumber] = useState(0);
|
|
25
|
-
|
|
26
|
-
const tl = useRef();
|
|
27
|
-
const gsapCtx = useRef();
|
|
28
|
-
const playerTimeRef = useRef(0.001);
|
|
29
|
-
|
|
30
|
-
// gsap.ticker.add((time, deltaTime, frame) => {
|
|
31
|
-
// console.log("timeframe", time, deltaTime, frame);
|
|
32
|
-
// setFrameNumber(frame);
|
|
33
|
-
// });
|
|
34
|
-
|
|
35
|
-
const parentElementRef = useRef();
|
|
36
|
-
// передаем предка анимируемых элементов
|
|
37
|
-
const q = gsap.utils.selector(parentElementRef);
|
|
38
|
-
|
|
39
|
-
useLayoutEffect(() => {
|
|
40
|
-
gsapCtx.current = gsap.context(() => {
|
|
41
|
-
// add a box and circle animation to our timeline and play on first render
|
|
42
|
-
// console.log("creating timeline");
|
|
43
|
-
tl.current && tl.current.progress(0).kill();
|
|
44
|
-
tl.current = gsap.timeline({
|
|
45
|
-
paused: true,
|
|
46
|
-
defaults: { duration: 0 },
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
return () => gsapCtx.current.revert();
|
|
50
|
-
}, []);
|
|
51
|
-
|
|
52
|
-
const onUpdate = useCallback(() => {
|
|
53
|
-
/// console.log("update event callback");
|
|
54
|
-
const timeline = tl.current;
|
|
55
|
-
var now = timeline.time();
|
|
56
|
-
var elapsedTime;
|
|
57
|
-
if (playerTimeRef.current) {
|
|
58
|
-
elapsedTime = now - playerTimeRef.current;
|
|
59
|
-
}
|
|
60
|
-
// console.log(`elapseTime :${elapsedTime} and frame: ${frameNumber}`);
|
|
61
|
-
//time = now;
|
|
62
|
-
playerTimeRef.current = now;
|
|
63
|
-
}, []);
|
|
64
|
-
|
|
65
|
-
// const timeline = React.useCallback(
|
|
66
|
-
// gsap.timeline({
|
|
67
|
-
// paused: true,
|
|
68
|
-
// defaults: { duration: 0, onUpdate: onUpdate },
|
|
69
|
-
// }),
|
|
70
|
-
// [],
|
|
71
|
-
// );
|
|
72
|
-
|
|
73
|
-
useEffect(() => {
|
|
74
|
-
const timeline = tl.current;
|
|
75
|
-
timeline
|
|
76
|
-
.eventCallback("onUpdate", function () {
|
|
77
|
-
/// console.log("onupdate", timeline.progress());
|
|
78
|
-
onUpdate();
|
|
79
|
-
})
|
|
80
|
-
.eventCallback("onComplete", function () {
|
|
81
|
-
timeline.seek(0.01);
|
|
82
|
-
timeline.pause();
|
|
83
|
-
onUpdate();
|
|
84
|
-
});
|
|
85
|
-
}, []);
|
|
86
|
-
|
|
87
|
-
useEffect(() => {
|
|
88
|
-
const timeline = tl.current;
|
|
89
|
-
if (!play) {
|
|
90
|
-
timeline.pause();
|
|
91
|
-
} else {
|
|
92
|
-
timeline.resume();
|
|
93
|
-
}
|
|
94
|
-
}, [play]);
|
|
95
|
-
|
|
96
|
-
const addTotalDuration = (duration) => {
|
|
97
|
-
const timeline = tl.current;
|
|
98
|
-
timeline.totalDuration(duration);
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
const handleReset = useCallback(() => {
|
|
102
|
-
const timeline = tl.current;
|
|
103
|
-
|
|
104
|
-
timeline.revert();
|
|
105
|
-
}, []);
|
|
106
|
-
|
|
107
|
-
const handleRestart = useCallback(() => {
|
|
108
|
-
const timeline = tl.current;
|
|
109
|
-
timeline.restart();
|
|
110
|
-
}, []);
|
|
111
|
-
|
|
112
|
-
const handleRepeat = useCallback(() => {
|
|
113
|
-
const timeline = tl.current;
|
|
114
|
-
|
|
115
|
-
timeline.repeat(1);
|
|
116
|
-
timeline.restart();
|
|
117
|
-
}, []);
|
|
118
|
-
|
|
119
|
-
const handleSeek = useCallback(() => {
|
|
120
|
-
const timeline = tl.current;
|
|
121
|
-
timeline.seek(4);
|
|
122
|
-
}, []);
|
|
123
|
-
|
|
124
|
-
const setDurationTimeline = useCallback(() => {
|
|
125
|
-
const timeline = tl.current;
|
|
126
|
-
|
|
127
|
-
timeline.totalDuration(3);
|
|
128
|
-
}, []);
|
|
129
|
-
|
|
130
|
-
const handlePause = () => {
|
|
131
|
-
const timeline = tl.current;
|
|
132
|
-
|
|
133
|
-
timeline.pause();
|
|
134
|
-
emitCustomEvent(Events.PAUSE);
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
const handlePlay = () => {
|
|
138
|
-
const timeline = tl.current;
|
|
139
|
-
timeline.resume();
|
|
140
|
-
emitCustomEvent(Events.RESUME);
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
return (
|
|
144
|
-
<GsapPixieContext.Provider
|
|
145
|
-
value={{
|
|
146
|
-
gsapCtx,
|
|
147
|
-
tl,
|
|
148
|
-
handlePlay,
|
|
149
|
-
handlePause,
|
|
150
|
-
handleReset,
|
|
151
|
-
handleSeek,
|
|
152
|
-
handleRestart,
|
|
153
|
-
handleRepeat,
|
|
154
|
-
playerTimeRef,
|
|
155
|
-
}}
|
|
156
|
-
>
|
|
157
|
-
{children}
|
|
158
|
-
</GsapPixieContext.Provider>
|
|
159
|
-
);
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
export { GsapPixieContext, GsapPixieContextProvider, Events };
|