@umituz/react-native-video-editor 1.0.35 → 1.1.0
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-video-editor",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Professional video editor with layer-based timeline, text/image/shape/audio/animation layers, and export functionality",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -21,8 +21,7 @@
|
|
|
21
21
|
"video-editor",
|
|
22
22
|
"timeline",
|
|
23
23
|
"layers",
|
|
24
|
-
"video-export"
|
|
25
|
-
"animation"
|
|
24
|
+
"video-export"
|
|
26
25
|
],
|
|
27
26
|
"author": "umituz",
|
|
28
27
|
"license": "MIT",
|
|
@@ -59,7 +58,6 @@
|
|
|
59
58
|
"react": "19.1.0",
|
|
60
59
|
"react-native": "0.81.5",
|
|
61
60
|
"react-native-gesture-handler": "^2.30.0",
|
|
62
|
-
"react-native-reanimated": "^4.2.1",
|
|
63
61
|
"typescript": "~5.9.2",
|
|
64
62
|
"@types/react-native": "*",
|
|
65
63
|
"@typescript-eslint/parser": "*",
|
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import React from "react";
|
|
7
|
-
import { StyleSheet } from "react-native";
|
|
7
|
+
import { View, StyleSheet } from "react-native";
|
|
8
8
|
import { GestureDetector } from "react-native-gesture-handler";
|
|
9
|
-
import Animated, { useAnimatedStyle } from "react-native-reanimated";
|
|
10
9
|
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
11
10
|
import type { Layer } from "../../domain/entities";
|
|
12
11
|
import { useDraggableLayerGestures } from "../hooks/useDraggableLayerGestures";
|
|
@@ -40,10 +39,7 @@ export const DraggableLayer: React.FC<DraggableLayerProps> = ({
|
|
|
40
39
|
const initialHeight = (layer.size.height / 100) * canvasHeight;
|
|
41
40
|
|
|
42
41
|
const {
|
|
43
|
-
|
|
44
|
-
translateY,
|
|
45
|
-
width,
|
|
46
|
-
height,
|
|
42
|
+
state,
|
|
47
43
|
composedGesture,
|
|
48
44
|
topLeftResizeHandler,
|
|
49
45
|
topRightResizeHandler,
|
|
@@ -61,30 +57,27 @@ export const DraggableLayer: React.FC<DraggableLayerProps> = ({
|
|
|
61
57
|
onSizeChange,
|
|
62
58
|
});
|
|
63
59
|
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
height: height.value,
|
|
75
|
-
};
|
|
76
|
-
});
|
|
60
|
+
const layerStyle = {
|
|
61
|
+
transform: [
|
|
62
|
+
{ translateX: state.x },
|
|
63
|
+
{ translateY: state.y },
|
|
64
|
+
{ rotate: `${layer.rotation}deg` },
|
|
65
|
+
],
|
|
66
|
+
opacity: layer.opacity,
|
|
67
|
+
width: state.width,
|
|
68
|
+
height: state.height,
|
|
69
|
+
};
|
|
77
70
|
|
|
78
71
|
return (
|
|
79
72
|
<GestureDetector gesture={composedGesture}>
|
|
80
|
-
<
|
|
73
|
+
<View
|
|
81
74
|
style={[
|
|
82
75
|
styles.layer,
|
|
83
76
|
{
|
|
84
77
|
borderColor: isSelected ? tokens.colors.primary : "transparent",
|
|
85
78
|
borderWidth: isSelected ? 2 : 0,
|
|
86
79
|
},
|
|
87
|
-
|
|
80
|
+
layerStyle,
|
|
88
81
|
]}
|
|
89
82
|
>
|
|
90
83
|
<LayerContent layer={layer} />
|
|
@@ -97,7 +90,7 @@ export const DraggableLayer: React.FC<DraggableLayerProps> = ({
|
|
|
97
90
|
bottomRightGesture={bottomRightResizeHandler}
|
|
98
91
|
/>
|
|
99
92
|
)}
|
|
100
|
-
</
|
|
93
|
+
</View>
|
|
101
94
|
</GestureDetector>
|
|
102
95
|
);
|
|
103
96
|
};
|
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import React from "react";
|
|
7
|
-
import { StyleSheet } from "react-native";
|
|
7
|
+
import { View, StyleSheet } from "react-native";
|
|
8
8
|
import { Gesture, GestureDetector } from "react-native-gesture-handler";
|
|
9
|
-
import Animated from "react-native-reanimated";
|
|
10
9
|
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
11
10
|
|
|
12
11
|
interface ResizeHandlesProps {
|
|
@@ -27,7 +26,7 @@ export const ResizeHandles: React.FC<ResizeHandlesProps> = ({
|
|
|
27
26
|
return (
|
|
28
27
|
<>
|
|
29
28
|
<GestureDetector gesture={topLeftGesture}>
|
|
30
|
-
<
|
|
29
|
+
<View
|
|
31
30
|
style={[
|
|
32
31
|
styles.handle,
|
|
33
32
|
styles.handleTopLeft,
|
|
@@ -40,7 +39,7 @@ export const ResizeHandles: React.FC<ResizeHandlesProps> = ({
|
|
|
40
39
|
</GestureDetector>
|
|
41
40
|
|
|
42
41
|
<GestureDetector gesture={topRightGesture}>
|
|
43
|
-
<
|
|
42
|
+
<View
|
|
44
43
|
style={[
|
|
45
44
|
styles.handle,
|
|
46
45
|
styles.handleTopRight,
|
|
@@ -53,7 +52,7 @@ export const ResizeHandles: React.FC<ResizeHandlesProps> = ({
|
|
|
53
52
|
</GestureDetector>
|
|
54
53
|
|
|
55
54
|
<GestureDetector gesture={bottomLeftGesture}>
|
|
56
|
-
<
|
|
55
|
+
<View
|
|
57
56
|
style={[
|
|
58
57
|
styles.handle,
|
|
59
58
|
styles.handleBottomLeft,
|
|
@@ -66,7 +65,7 @@ export const ResizeHandles: React.FC<ResizeHandlesProps> = ({
|
|
|
66
65
|
</GestureDetector>
|
|
67
66
|
|
|
68
67
|
<GestureDetector gesture={bottomRightGesture}>
|
|
69
|
-
<
|
|
68
|
+
<View
|
|
70
69
|
style={[
|
|
71
70
|
styles.handle,
|
|
72
71
|
styles.handleBottomRight,
|
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
* Manages gesture handling for draggable layers
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { useState, useRef, useCallback } from "react";
|
|
7
7
|
import { Gesture } from "react-native-gesture-handler";
|
|
8
|
-
import { withSpring, runOnJS } from "react-native-reanimated";
|
|
9
8
|
|
|
10
9
|
interface UseDraggableLayerGesturesParams {
|
|
11
10
|
initialX: number;
|
|
@@ -19,11 +18,15 @@ interface UseDraggableLayerGesturesParams {
|
|
|
19
18
|
onSizeChange: (width: number, height: number) => void;
|
|
20
19
|
}
|
|
21
20
|
|
|
21
|
+
interface LayerState {
|
|
22
|
+
x: number;
|
|
23
|
+
y: number;
|
|
24
|
+
width: number;
|
|
25
|
+
height: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
22
28
|
interface UseDraggableLayerGesturesReturn {
|
|
23
|
-
|
|
24
|
-
translateY: ReturnType<typeof useSharedValue<number>>;
|
|
25
|
-
width: ReturnType<typeof useSharedValue<number>>;
|
|
26
|
-
height: ReturnType<typeof useSharedValue<number>>;
|
|
29
|
+
state: LayerState;
|
|
27
30
|
composedGesture: ReturnType<typeof Gesture.Race>;
|
|
28
31
|
topLeftResizeHandler: ReturnType<typeof Gesture.Pan>;
|
|
29
32
|
topRightResizeHandler: ReturnType<typeof Gesture.Pan>;
|
|
@@ -33,9 +36,6 @@ interface UseDraggableLayerGesturesReturn {
|
|
|
33
36
|
|
|
34
37
|
const MIN_SIZE = 50;
|
|
35
38
|
|
|
36
|
-
/**
|
|
37
|
-
* Hook for managing draggable layer gestures
|
|
38
|
-
*/
|
|
39
39
|
export function useDraggableLayerGestures({
|
|
40
40
|
initialX,
|
|
41
41
|
initialY,
|
|
@@ -47,102 +47,84 @@ export function useDraggableLayerGestures({
|
|
|
47
47
|
onPositionChange,
|
|
48
48
|
onSizeChange,
|
|
49
49
|
}: UseDraggableLayerGesturesParams): UseDraggableLayerGesturesReturn {
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
const [state, setState] = useState<LayerState>({
|
|
51
|
+
x: initialX,
|
|
52
|
+
y: initialY,
|
|
53
|
+
width: initialWidth,
|
|
54
|
+
height: initialHeight,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const startRef = useRef({ x: initialX, y: initialY, width: initialWidth, height: initialHeight });
|
|
54
58
|
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const startHeight = useSharedValue(initialHeight);
|
|
59
|
+
const clamp = useCallback((value: number, min: number, max: number) => {
|
|
60
|
+
return Math.max(min, Math.min(max, value));
|
|
61
|
+
}, []);
|
|
59
62
|
|
|
60
63
|
const gestureHandler = Gesture.Pan()
|
|
61
64
|
.onStart(() => {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
runOnJS(onSelect)();
|
|
65
|
+
startRef.current = { ...state, x: state.x, y: state.y };
|
|
66
|
+
onSelect();
|
|
65
67
|
})
|
|
66
68
|
.onUpdate((event) => {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
+
const newX = startRef.current.x + event.translationX;
|
|
70
|
+
const newY = startRef.current.y + event.translationY;
|
|
71
|
+
setState((prev) => ({ ...prev, x: newX, y: newY }));
|
|
69
72
|
})
|
|
70
73
|
.onEnd(() => {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const newY = (translateY.value / canvasHeight) * 100;
|
|
80
|
-
runOnJS(onPositionChange)(newX, newY);
|
|
74
|
+
setState((prev) => {
|
|
75
|
+
const clampedX = clamp(prev.x, 0, canvasWidth - prev.width);
|
|
76
|
+
const clampedY = clamp(prev.y, 0, canvasHeight - prev.height);
|
|
77
|
+
const newX = (clampedX / canvasWidth) * 100;
|
|
78
|
+
const newY = (clampedY / canvasHeight) * 100;
|
|
79
|
+
onPositionChange(newX, newY);
|
|
80
|
+
return { ...prev, x: clampedX, y: clampedY };
|
|
81
|
+
});
|
|
81
82
|
});
|
|
82
83
|
|
|
83
84
|
const createResizeHandler = (
|
|
84
|
-
deltaX: (
|
|
85
|
-
deltaY: (
|
|
85
|
+
deltaX: (tx: number) => number,
|
|
86
|
+
deltaY: (ty: number) => number,
|
|
86
87
|
) => {
|
|
87
88
|
return Gesture.Pan()
|
|
88
89
|
.onStart(() => {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
startX.value = translateX.value;
|
|
92
|
-
startY.value = translateY.value;
|
|
93
|
-
runOnJS(onSelect)();
|
|
90
|
+
startRef.current = { ...state };
|
|
91
|
+
onSelect();
|
|
94
92
|
})
|
|
95
93
|
.onUpdate((event) => {
|
|
96
|
-
const newWidth = Math.max(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
);
|
|
104
|
-
width.value = Math.min(newWidth, canvasWidth - startX.value);
|
|
105
|
-
height.value = Math.min(newHeight, canvasHeight - startY.value);
|
|
94
|
+
const newWidth = Math.max(MIN_SIZE, startRef.current.width + deltaX(event.translationX));
|
|
95
|
+
const newHeight = Math.max(MIN_SIZE, startRef.current.height + deltaY(event.translationY));
|
|
96
|
+
const clampedWidth = Math.min(newWidth, canvasWidth - startRef.current.x);
|
|
97
|
+
const clampedHeight = Math.min(newHeight, canvasHeight - startRef.current.y);
|
|
98
|
+
|
|
99
|
+
let newX = startRef.current.x;
|
|
100
|
+
let newY = startRef.current.y;
|
|
106
101
|
|
|
107
102
|
if (deltaX(event.translationX) < 0) {
|
|
108
|
-
|
|
109
|
-
0,
|
|
110
|
-
startX.value + (startWidth.value - width.value),
|
|
111
|
-
);
|
|
103
|
+
newX = Math.max(0, startRef.current.x + (startRef.current.width - clampedWidth));
|
|
112
104
|
}
|
|
113
105
|
if (deltaY(event.translationY) < 0) {
|
|
114
|
-
|
|
115
|
-
0,
|
|
116
|
-
startY.value + (startHeight.value - height.value),
|
|
117
|
-
);
|
|
106
|
+
newY = Math.max(0, startRef.current.y + (startRef.current.height - clampedHeight));
|
|
118
107
|
}
|
|
108
|
+
|
|
109
|
+
setState({ x: newX, y: newY, width: clampedWidth, height: clampedHeight });
|
|
119
110
|
})
|
|
120
111
|
.onEnd(() => {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
112
|
+
setState((prev) => {
|
|
113
|
+
const newWidth = (prev.width / canvasWidth) * 100;
|
|
114
|
+
const newHeight = (prev.height / canvasHeight) * 100;
|
|
115
|
+
const newX = (prev.x / canvasWidth) * 100;
|
|
116
|
+
const newY = (prev.y / canvasHeight) * 100;
|
|
117
|
+
onSizeChange(newWidth, newHeight);
|
|
118
|
+
onPositionChange(newX, newY);
|
|
119
|
+
return prev;
|
|
120
|
+
});
|
|
127
121
|
});
|
|
128
122
|
};
|
|
129
123
|
|
|
130
|
-
const topLeftResizeHandler = createResizeHandler(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
);
|
|
134
|
-
const topRightResizeHandler = createResizeHandler(
|
|
135
|
-
(tx) => tx,
|
|
136
|
-
(ty) => -ty,
|
|
137
|
-
);
|
|
138
|
-
const bottomLeftResizeHandler = createResizeHandler(
|
|
139
|
-
(tx) => -tx,
|
|
140
|
-
(ty) => ty,
|
|
141
|
-
);
|
|
142
|
-
const bottomRightResizeHandler = createResizeHandler(
|
|
143
|
-
(tx) => tx,
|
|
144
|
-
(ty) => ty,
|
|
145
|
-
);
|
|
124
|
+
const topLeftResizeHandler = createResizeHandler((tx) => -tx, (ty) => -ty);
|
|
125
|
+
const topRightResizeHandler = createResizeHandler((tx) => tx, (ty) => -ty);
|
|
126
|
+
const bottomLeftResizeHandler = createResizeHandler((tx) => -tx, (ty) => ty);
|
|
127
|
+
const bottomRightResizeHandler = createResizeHandler((tx) => tx, (ty) => ty);
|
|
146
128
|
|
|
147
129
|
const composedGesture = Gesture.Race(
|
|
148
130
|
gestureHandler,
|
|
@@ -153,10 +135,7 @@ export function useDraggableLayerGestures({
|
|
|
153
135
|
);
|
|
154
136
|
|
|
155
137
|
return {
|
|
156
|
-
|
|
157
|
-
translateY,
|
|
158
|
-
width,
|
|
159
|
-
height,
|
|
138
|
+
state,
|
|
160
139
|
composedGesture,
|
|
161
140
|
topLeftResizeHandler,
|
|
162
141
|
topRightResizeHandler,
|