react-native-video-trim 6.2.3 → 7.0.1

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.
@@ -0,0 +1,19 @@
1
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
2
+ android:width="23.232dp"
3
+ android:height="24.688dp"
4
+ android:viewportWidth="23.232"
5
+ android:viewportHeight="24.688">
6
+ <path
7
+ android:fillColor="#FF000000"
8
+ android:pathData="M0,0h23.232v24.688h-23.232z"
9
+ android:strokeAlpha="0"
10
+ android:fillAlpha="0"/>
11
+ <path
12
+ android:pathData="M1.475,21.236L9.189,21.236C10.342,21.236 10.869,20.718 10.869,19.556L10.869,7.886C10.869,7.027 10.234,6.568 9.541,6.568C9.072,6.568 8.613,6.782 8.301,7.232L0.303,18.911C0.088,19.224 0,19.585 0,19.917C0,20.63 0.469,21.236 1.475,21.236ZM2.09,19.644C1.963,19.644 1.943,19.536 2.002,19.448L9.102,8.706C9.16,8.618 9.268,8.648 9.268,8.755L9.268,19.361C9.268,19.585 9.209,19.644 8.975,19.644ZM21.396,21.236C22.412,21.236 22.871,20.63 22.871,19.917C22.871,19.585 22.783,19.224 22.568,18.911L14.58,7.232C14.258,6.782 13.809,6.568 13.34,6.568C12.646,6.568 12.012,7.027 12.012,7.886L12.012,19.556C12.012,20.718 12.529,21.236 13.691,21.236ZM20.781,19.644L13.896,19.644C13.672,19.644 13.613,19.585 13.613,19.361L13.613,8.755C13.613,8.648 13.721,8.618 13.769,8.706L20.869,19.448C20.938,19.536 20.918,19.644 20.781,19.644Z"
13
+ android:fillColor="#ffffff"
14
+ android:fillAlpha="0.85"/>
15
+ <path
16
+ android:pathData="M17.373,0.708L17.373,4.614C17.373,5.298 18.027,5.542 18.565,5.132L21.182,3.179C21.533,2.905 21.523,2.398 21.182,2.144L18.565,0.2C18.027,-0.21 17.373,0.034 17.373,0.708ZM5.508,4.614L5.508,0.708C5.508,0.034 4.854,-0.21 4.316,0.2L1.699,2.144C1.338,2.407 1.338,2.905 1.699,3.179L4.316,5.132C4.834,5.523 5.508,5.318 5.508,4.614ZM18.691,3.237C18.994,3.237 19.258,2.984 19.258,2.661C19.258,2.359 18.994,2.105 18.691,2.105L4.18,2.105C3.867,2.105 3.613,2.359 3.613,2.661C3.613,2.984 3.867,3.237 4.18,3.237Z"
17
+ android:fillColor="#ffffff"
18
+ android:fillAlpha="0.85"/>
19
+ </vector>
@@ -0,0 +1,15 @@
1
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
2
+ android:width="18.818dp"
3
+ android:height="18.232dp"
4
+ android:viewportWidth="18.818"
5
+ android:viewportHeight="18.232">
6
+ <path
7
+ android:fillColor="#FF000000"
8
+ android:pathData="M0,0h18.818v18.232h-18.818z"
9
+ android:strokeAlpha="0"
10
+ android:fillAlpha="0"/>
11
+ <path
12
+ android:pathData="M0,6.572C0,6.807 0.098,7.031 0.293,7.217L6.035,12.852C6.201,13.027 6.455,13.125 6.66,13.125C7.188,13.125 7.529,12.773 7.529,12.266C7.529,12.012 7.441,11.826 7.295,11.67L4.473,8.926L1.719,6.572L4.473,4.209L7.295,1.465C7.441,1.309 7.529,1.123 7.529,0.869C7.529,0.361 7.188,0.01 6.66,0.01C6.455,0.01 6.201,0.107 6.035,0.283L0.293,5.918C0.098,6.104 0,6.328 0,6.572ZM8.486,17.363C8.486,17.842 8.838,18.232 9.375,18.232L11.631,18.232C15.996,18.232 18.457,15.742 18.457,12.002C18.457,8.271 15.928,5.693 11.475,5.693L4.912,5.693L1.68,5.84C1.279,5.859 0.957,6.162 0.957,6.572C0.957,6.973 1.279,7.275 1.68,7.295L4.912,7.441L11.621,7.441C14.951,7.441 16.709,9.277 16.709,11.904C16.709,14.541 14.951,16.494 11.621,16.494L9.375,16.494C8.838,16.494 8.486,16.885 8.486,17.363Z"
13
+ android:fillColor="#ffffff"
14
+ android:fillAlpha="0.85"/>
15
+ </vector>
@@ -0,0 +1,15 @@
1
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
2
+ android:width="18.818dp"
3
+ android:height="18.232dp"
4
+ android:viewportWidth="18.818"
5
+ android:viewportHeight="18.232">
6
+ <path
7
+ android:fillColor="#FF000000"
8
+ android:pathData="M0,0h18.818v18.232h-18.818z"
9
+ android:strokeAlpha="0"
10
+ android:fillAlpha="0"/>
11
+ <path
12
+ android:pathData="M18.457,6.572C18.457,6.328 18.359,6.104 18.164,5.918L12.422,0.283C12.256,0.107 12.002,0.01 11.787,0.01C11.269,0.01 10.928,0.361 10.928,0.869C10.928,1.123 11.006,1.309 11.162,1.465L13.975,4.209L16.738,6.572L13.975,8.926L11.162,11.67C11.006,11.826 10.928,12.012 10.928,12.266C10.928,12.773 11.269,13.125 11.787,13.125C12.002,13.125 12.256,13.027 12.422,12.852L18.164,7.217C18.359,7.031 18.457,6.807 18.457,6.572ZM9.971,17.363C9.971,16.885 9.619,16.494 9.082,16.494L6.836,16.494C3.506,16.494 1.738,14.541 1.738,11.904C1.738,9.277 3.506,7.441 6.836,7.441L13.535,7.441L16.768,7.295C17.178,7.275 17.5,6.973 17.5,6.572C17.5,6.162 17.178,5.859 16.768,5.84L13.535,5.693L6.982,5.693C2.52,5.693 0,8.271 0,12.002C0,15.742 2.451,18.232 6.826,18.232L9.082,18.232C9.619,18.232 9.971,17.842 9.971,17.363Z"
13
+ android:fillColor="#ffffff"
14
+ android:fillAlpha="0.85"/>
15
+ </vector>
@@ -0,0 +1,15 @@
1
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
2
+ android:width="21.777dp"
3
+ android:height="21.982dp"
4
+ android:viewportWidth="21.777"
5
+ android:viewportHeight="21.982">
6
+ <path
7
+ android:fillColor="#FF000000"
8
+ android:pathData="M0,0h21.777v21.982h-21.777z"
9
+ android:strokeAlpha="0"
10
+ android:fillAlpha="0"/>
11
+ <path
12
+ android:pathData="M21.416,16.553C21.416,16.094 21.113,15.811 20.635,15.811L6.553,15.811C6.338,15.811 6.27,15.742 6.27,15.527L6.27,1.445C6.27,0.957 5.957,0.625 5.479,0.625C5.01,0.625 4.697,0.957 4.697,1.445L4.697,16.514C4.697,16.982 5,17.285 5.469,17.285L20.635,17.285C21.113,17.285 21.416,17.002 21.416,16.553ZM0,6.055C0,6.523 0.303,6.797 0.771,6.797L14.863,6.797C15.078,6.797 15.146,6.875 15.146,7.08L15.146,21.162C15.146,21.65 15.459,21.982 15.928,21.982C16.406,21.982 16.719,21.65 16.719,21.162L16.719,6.104C16.719,5.635 16.416,5.322 15.947,5.322L0.771,5.322C0.303,5.322 0,5.605 0,6.055Z"
13
+ android:fillColor="#ffffff"
14
+ android:fillAlpha="0.85"/>
15
+ </vector>
@@ -0,0 +1,19 @@
1
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
2
+ android:width="24.082dp"
3
+ android:height="24.727dp"
4
+ android:viewportWidth="24.082"
5
+ android:viewportHeight="24.727">
6
+ <path
7
+ android:fillColor="#FF000000"
8
+ android:pathData="M0,0h24.082v24.727h-24.082z"
9
+ android:strokeAlpha="0"
10
+ android:fillAlpha="0"/>
11
+ <path
12
+ android:pathData="M7.021,21.353L16.699,21.353C18.369,21.353 19.209,20.562 19.209,18.853L19.209,9.194C19.209,7.485 18.369,6.685 16.699,6.685L7.021,6.685C5.352,6.685 4.512,7.485 4.512,9.194L4.512,18.853C4.512,20.562 5.352,21.353 7.021,21.353ZM7.051,19.78C6.387,19.78 6.084,19.507 6.084,18.823L6.084,9.214C6.084,8.53 6.387,8.257 7.051,8.257L16.67,8.257C17.344,8.257 17.637,8.53 17.637,9.214L17.637,18.823C17.637,19.507 17.344,19.78 16.67,19.78Z"
13
+ android:fillColor="#ffffff"
14
+ android:fillAlpha="0.85"/>
15
+ <path
16
+ android:pathData="M19.209,4.312L19.209,0.698C19.209,-0.024 18.662,-0.19 18.115,0.22L15.654,2.027C15.244,2.329 15.234,2.681 15.654,2.984L18.115,4.78C18.662,5.181 19.209,5.015 19.209,4.312ZM18.184,1.929C17.871,1.929 17.617,2.183 17.617,2.486C17.617,2.798 17.871,3.062 18.184,3.062L19.404,3.062C21.523,3.062 22.861,4.527 22.861,6.607L22.861,8.071C22.861,8.403 23.135,8.677 23.467,8.677C23.799,8.677 24.082,8.403 24.082,8.071L24.082,6.607C24.082,3.814 22.217,1.929 19.414,1.929Z"
17
+ android:fillColor="#ffffff"
18
+ android:fillAlpha="0.85"/>
19
+ </vector>
@@ -3,39 +3,132 @@
3
3
  android:layout_width="match_parent"
4
4
  android:layout_height="match_parent"
5
5
  android:orientation="vertical">
6
+
6
7
  <FrameLayout
7
8
  android:id="@+id/headerView"
8
9
  android:layout_width="match_parent"
9
10
  android:layout_height="wrap_content"
10
- android:minHeight="50dp"
11
11
  android:background="@android:color/black"
12
- android:visibility="gone"
13
- >
14
- <TextView
15
- android:id="@+id/headerText"
16
- android:layout_width="wrap_content"
17
- android:layout_height="match_parent"
12
+ android:visibility="gone">
13
+
14
+ <HorizontalScrollView
15
+ android:layout_width="match_parent"
16
+ android:layout_height="wrap_content"
18
17
  android:layout_gravity="center"
19
- android:clickable="true"
20
- android:focusable="true"
21
- android:text="Header text"
22
- android:textColor="@android:color/white"
23
- android:textSize="16dp" />
18
+ android:scrollbars="none"
19
+ android:fillViewport="true"
20
+ android:paddingTop="6dp"
21
+ android:paddingBottom="2dp">
22
+
23
+ <TextView
24
+ android:id="@+id/headerText"
25
+ android:layout_width="wrap_content"
26
+ android:layout_height="wrap_content"
27
+ android:gravity="center"
28
+ android:singleLine="true"
29
+ android:text=""
30
+ android:textColor="@android:color/white"
31
+ android:textSize="16dp"
32
+ android:paddingHorizontal="16dp" />
33
+ </HorizontalScrollView>
24
34
  </FrameLayout>
25
35
 
26
- <RelativeLayout
36
+ <LinearLayout
37
+ android:id="@+id/transformRow"
38
+ android:layout_width="match_parent"
39
+ android:layout_height="wrap_content"
40
+ android:layout_below="@+id/headerView"
41
+ android:orientation="horizontal"
42
+ android:paddingHorizontal="16dp"
43
+ android:paddingVertical="4dp"
44
+ android:background="@android:color/black"
45
+ android:gravity="center_vertical"
46
+ android:visibility="gone">
47
+
48
+ <LinearLayout
49
+ android:layout_width="wrap_content"
50
+ android:layout_height="wrap_content"
51
+ android:orientation="horizontal"
52
+ android:gravity="center_vertical">
53
+
54
+ <ImageView
55
+ android:id="@+id/flipBtn"
56
+ android:layout_width="22dp"
57
+ android:layout_height="22dp"
58
+ android:src="@drawable/arrow_trianglehead_left_and_right_righttriangle_left_righttriangle_right"
59
+ android:tint="@android:color/white"
60
+ android:scaleType="fitCenter"
61
+ android:layout_marginEnd="12dp"
62
+ android:contentDescription="Flip" />
63
+
64
+ <ImageView
65
+ android:id="@+id/rotateBtn"
66
+ android:layout_width="22dp"
67
+ android:layout_height="22dp"
68
+ android:src="@drawable/rotate_left"
69
+ android:tint="@android:color/white"
70
+ android:scaleType="fitCenter"
71
+ android:layout_marginEnd="12dp"
72
+ android:contentDescription="Rotate" />
73
+
74
+ <ImageView
75
+ android:id="@+id/cropBtn"
76
+ android:layout_width="22dp"
77
+ android:layout_height="22dp"
78
+ android:src="@drawable/crop"
79
+ android:tint="#80FFFFFF"
80
+ android:scaleType="fitCenter"
81
+ android:contentDescription="Crop" />
82
+ </LinearLayout>
83
+
84
+ <View
85
+ android:layout_width="0dp"
86
+ android:layout_height="0dp"
87
+ android:layout_weight="1" />
88
+
89
+ <LinearLayout
90
+ android:layout_width="wrap_content"
91
+ android:layout_height="wrap_content"
92
+ android:orientation="horizontal"
93
+ android:gravity="center_vertical">
94
+
95
+ <ImageView
96
+ android:id="@+id/undoBtn"
97
+ android:layout_width="18dp"
98
+ android:layout_height="18dp"
99
+ android:src="@drawable/arrow_uturn_backward"
100
+ android:tint="#80FFFFFF"
101
+ android:scaleType="fitCenter"
102
+ android:layout_marginEnd="12dp"
103
+ android:contentDescription="Undo" />
104
+
105
+ <ImageView
106
+ android:id="@+id/redoBtn"
107
+ android:layout_width="18dp"
108
+ android:layout_height="18dp"
109
+ android:src="@drawable/arrow_uturn_forward"
110
+ android:tint="#80FFFFFF"
111
+ android:scaleType="fitCenter"
112
+ android:contentDescription="Redo" />
113
+ </LinearLayout>
114
+ </LinearLayout>
115
+
116
+ <FrameLayout
117
+ android:id="@+id/videoContainer"
27
118
  android:layout_width="match_parent"
28
119
  android:layout_height="match_parent"
29
120
  android:layout_above="@+id/layout"
30
- android:layout_below="@+id/headerView"
121
+ android:layout_below="@+id/transformRow"
31
122
  android:background="@android:color/black"
32
- android:gravity="center">
123
+ android:clipChildren="true"
124
+ android:clipToPadding="false"
125
+ android:paddingVertical="4dp">
33
126
 
34
- <VideoView
127
+ <TextureView
35
128
  android:id="@+id/video_loader"
36
129
  android:layout_width="match_parent"
37
130
  android:layout_height="match_parent"
38
- />
131
+ android:layout_gravity="center" />
39
132
 
40
133
  <FrameLayout
41
134
  android:id="@+id/audioBannerView"
@@ -53,11 +146,9 @@
53
146
  android:layout_height="match_parent"
54
147
  android:layout_gravity="center"
55
148
  android:contentDescription="Airpods Max"
56
- android:src="@drawable/airpodsmax"
57
- />
149
+ android:src="@drawable/airpodsmax" />
58
150
  </FrameLayout>
59
-
60
- </RelativeLayout>
151
+ </FrameLayout>
61
152
 
62
153
  <RelativeLayout
63
154
  android:id="@+id/layout"
@@ -66,7 +157,6 @@
66
157
  android:layout_alignParentBottom="true"
67
158
  android:background="@android:color/black">
68
159
 
69
-
70
160
  <RelativeLayout
71
161
  android:id="@+id/trimmerView"
72
162
  android:layout_width="match_parent"
@@ -79,8 +169,7 @@
79
169
  android:layout_height="match_parent"
80
170
  android:layout_marginHorizontal="20dp"
81
171
  android:orientation="horizontal"
82
- android:padding="4dp"
83
- />
172
+ android:padding="4dp" />
84
173
 
85
174
  <RelativeLayout
86
175
  android:id="@+id/trimmerContainerWrapper"
@@ -161,9 +250,7 @@
161
250
  android:contentDescription="TrailingHandle"
162
251
  android:src="@drawable/chevron_compact_right" />
163
252
  </FrameLayout>
164
-
165
253
  </RelativeLayout>
166
-
167
254
  </RelativeLayout>
168
255
 
169
256
  <FrameLayout
@@ -213,7 +300,6 @@
213
300
  android:text="00:00.00"
214
301
  android:textColor="@android:color/white"
215
302
  android:textSize="16sp" />
216
-
217
303
  </FrameLayout>
218
304
 
219
305
  <FrameLayout
@@ -244,8 +330,7 @@
244
330
  android:padding="12dp"
245
331
  android:src="@drawable/play_fill"
246
332
  android:tint="@color/white"
247
- android:visibility="gone"
248
- />
333
+ android:visibility="gone" />
249
334
 
250
335
  <ImageView
251
336
  android:id="@+id/failToLoadBtn"
@@ -255,8 +340,7 @@
255
340
  android:padding="12dp"
256
341
  android:src="@drawable/exclamationmark_triangle_fill"
257
342
  android:tint="@color/trim_color"
258
- android:visibility="gone"
259
- />
343
+ android:visibility="gone" />
260
344
 
261
345
  <ProgressBar
262
346
  android:id="@+id/loadingIndicator"
@@ -265,8 +349,7 @@
265
349
  android:layout_height="wrap_content"
266
350
  android:layout_gravity="center"
267
351
  android:padding="10dp"
268
- android:indeterminateTint="@color/white"
269
- />
352
+ android:indeterminateTint="@color/white" />
270
353
 
271
354
  <TextView
272
355
  android:id="@+id/saveBtn"
@@ -278,10 +361,7 @@
278
361
  android:text="@string/save"
279
362
  android:textColor="#2196F3"
280
363
  android:textSize="16dp"
281
- android:visibility="gone"
282
- />
283
-
364
+ android:visibility="gone" />
284
365
  </FrameLayout>
285
-
286
366
  </RelativeLayout>
287
367
  </RelativeLayout>
@@ -0,0 +1,287 @@
1
+ import UIKit
2
+
3
+ @available(iOS 13.0, *)
4
+ class CropOverlayView: UIView {
5
+
6
+ var cropRect: CGRect = .zero {
7
+ didSet {
8
+ setNeedsDisplay()
9
+ }
10
+ }
11
+
12
+ var allowedRect: CGRect = .zero {
13
+ didSet {
14
+ if cropRect.isEmpty {
15
+ cropRect = allowedRect
16
+ } else {
17
+ let clamped = cropRect.intersection(allowedRect)
18
+ if clamped.isEmpty || clamped.width < minCropSize || clamped.height < minCropSize {
19
+ cropRect = allowedRect
20
+ }
21
+ }
22
+ }
23
+ }
24
+
25
+ var onCropChanged: (() -> Void)?
26
+ var onCropBegan: (() -> Void)?
27
+ var onCropEnded: (() -> Void)?
28
+
29
+ private let minCropSize: CGFloat = 60
30
+ private let borderWidth: CGFloat = 1.0
31
+ private let cornerLength: CGFloat = 20
32
+ private let cornerWidth: CGFloat = 4.0
33
+ private let edgeHandleLength: CGFloat = 20
34
+ private let gridLineWidth: CGFloat = CGFloat(1.0 / UIScreen.main.scale)
35
+ private let edgeHitZone: CGFloat = 30
36
+
37
+ private var activeEdge: DragEdge?
38
+ private var dragStart: CGPoint = .zero
39
+ private var dragStartRect: CGRect = .zero
40
+
41
+ private enum DragEdge {
42
+ case top, bottom, left, right
43
+ case topLeft, topRight, bottomLeft, bottomRight
44
+ case move
45
+ }
46
+
47
+ override init(frame: CGRect) {
48
+ super.init(frame: frame)
49
+ commonInit()
50
+ }
51
+
52
+ required init?(coder: NSCoder) {
53
+ super.init(coder: coder)
54
+ commonInit()
55
+ }
56
+
57
+ private func commonInit() {
58
+ backgroundColor = .clear
59
+ isUserInteractionEnabled = true
60
+ clipsToBounds = false
61
+ isOpaque = false
62
+
63
+ let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
64
+ addGestureRecognizer(pan)
65
+
66
+ let pinch = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(_:)))
67
+ addGestureRecognizer(pinch)
68
+ }
69
+
70
+ // MARK: - Drawing
71
+
72
+ override func draw(_ rect: CGRect) {
73
+ guard !cropRect.isEmpty, let ctx = UIGraphicsGetCurrentContext() else { return }
74
+ let cr = cropRect
75
+
76
+ ctx.saveGState()
77
+ let fullPath = UIBezierPath(rect: bounds)
78
+ fullPath.append(UIBezierPath(rect: cr))
79
+ fullPath.usesEvenOddFillRule = true
80
+ ctx.addPath(fullPath.cgPath)
81
+ ctx.setFillColor(UIColor.black.withAlphaComponent(0.55).cgColor)
82
+ ctx.fillPath(using: .evenOdd)
83
+ ctx.restoreGState()
84
+
85
+ ctx.setStrokeColor(UIColor.white.cgColor)
86
+ ctx.setLineWidth(borderWidth)
87
+ ctx.stroke(cr)
88
+
89
+ ctx.setLineWidth(gridLineWidth)
90
+ for i in 1...2 {
91
+ let x = cr.minX + cr.width * CGFloat(i) / 3
92
+ ctx.move(to: CGPoint(x: x, y: cr.minY))
93
+ ctx.addLine(to: CGPoint(x: x, y: cr.maxY))
94
+ }
95
+ for i in 1...2 {
96
+ let y = cr.minY + cr.height * CGFloat(i) / 3
97
+ ctx.move(to: CGPoint(x: cr.minX, y: y))
98
+ ctx.addLine(to: CGPoint(x: cr.maxX, y: y))
99
+ }
100
+ ctx.strokePath()
101
+
102
+ ctx.setStrokeColor(UIColor.white.cgColor)
103
+ ctx.setLineWidth(cornerWidth)
104
+ ctx.setLineCap(.round)
105
+ ctx.setLineJoin(.round)
106
+
107
+ let cl = cornerLength
108
+ let hw = cornerWidth / 2
109
+ let corners: [(CGPoint, CGPoint, CGPoint)] = [
110
+ (CGPoint(x: cr.minX - hw, y: cr.minY + cl),
111
+ CGPoint(x: cr.minX - hw, y: cr.minY - hw),
112
+ CGPoint(x: cr.minX + cl, y: cr.minY - hw)),
113
+ (CGPoint(x: cr.maxX - cl, y: cr.minY - hw),
114
+ CGPoint(x: cr.maxX + hw, y: cr.minY - hw),
115
+ CGPoint(x: cr.maxX + hw, y: cr.minY + cl)),
116
+ (CGPoint(x: cr.minX - hw, y: cr.maxY - cl),
117
+ CGPoint(x: cr.minX - hw, y: cr.maxY + hw),
118
+ CGPoint(x: cr.minX + cl, y: cr.maxY + hw)),
119
+ (CGPoint(x: cr.maxX - cl, y: cr.maxY + hw),
120
+ CGPoint(x: cr.maxX + hw, y: cr.maxY + hw),
121
+ CGPoint(x: cr.maxX + hw, y: cr.maxY - cl)),
122
+ ]
123
+ for (start, corner, end) in corners {
124
+ ctx.move(to: start)
125
+ ctx.addLine(to: corner)
126
+ ctx.addLine(to: end)
127
+ }
128
+
129
+ let ehl = edgeHandleLength / 2
130
+ let cx = cr.midX, cy = cr.midY
131
+ ctx.move(to: CGPoint(x: cx - ehl, y: cr.minY - hw))
132
+ ctx.addLine(to: CGPoint(x: cx + ehl, y: cr.minY - hw))
133
+ ctx.move(to: CGPoint(x: cx - ehl, y: cr.maxY + hw))
134
+ ctx.addLine(to: CGPoint(x: cx + ehl, y: cr.maxY + hw))
135
+ ctx.move(to: CGPoint(x: cr.minX - hw, y: cy - ehl))
136
+ ctx.addLine(to: CGPoint(x: cr.minX - hw, y: cy + ehl))
137
+ ctx.move(to: CGPoint(x: cr.maxX + hw, y: cy - ehl))
138
+ ctx.addLine(to: CGPoint(x: cr.maxX + hw, y: cy + ehl))
139
+
140
+ ctx.strokePath()
141
+ }
142
+
143
+ // MARK: - Hit Testing
144
+
145
+ override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
146
+ if detectEdge(at: point) != nil { return self }
147
+ return nil
148
+ }
149
+
150
+ private func detectEdge(at pt: CGPoint) -> DragEdge? {
151
+ let r = cropRect
152
+ let z = edgeHitZone
153
+
154
+ let nearT = abs(pt.y - r.minY) < z
155
+ let nearB = abs(pt.y - r.maxY) < z
156
+ let nearL = abs(pt.x - r.minX) < z
157
+ let nearR = abs(pt.x - r.maxX) < z
158
+ let inH = pt.x > r.minX - z && pt.x < r.maxX + z
159
+ let inV = pt.y > r.minY - z && pt.y < r.maxY + z
160
+
161
+ if nearT && nearL { return .topLeft }
162
+ if nearT && nearR { return .topRight }
163
+ if nearB && nearL { return .bottomLeft }
164
+ if nearB && nearR { return .bottomRight }
165
+ if nearT && inH { return .top }
166
+ if nearB && inH { return .bottom }
167
+ if nearL && inV { return .left }
168
+ if nearR && inV { return .right }
169
+ if r.contains(pt) { return .move }
170
+ return nil
171
+ }
172
+
173
+ // MARK: - Gestures
174
+
175
+ @objc private func handlePan(_ g: UIPanGestureRecognizer) {
176
+ let pt = g.location(in: self)
177
+ switch g.state {
178
+ case .began:
179
+ activeEdge = detectEdge(at: pt)
180
+ dragStart = pt
181
+ dragStartRect = cropRect
182
+ onCropBegan?()
183
+ case .changed:
184
+ guard let edge = activeEdge else { return }
185
+ let dx = pt.x - dragStart.x
186
+ let dy = pt.y - dragStart.y
187
+ cropRect = computeNewRect(edge: edge, dx: dx, dy: dy)
188
+ onCropChanged?()
189
+ case .ended, .cancelled:
190
+ activeEdge = nil
191
+ onCropEnded?()
192
+ default:
193
+ break
194
+ }
195
+ }
196
+
197
+ @objc private func handlePinch(_ g: UIPinchGestureRecognizer) {
198
+ switch g.state {
199
+ case .began:
200
+ dragStartRect = cropRect
201
+ onCropBegan?()
202
+ case .changed:
203
+ let scale = g.scale
204
+ let cx = dragStartRect.midX
205
+ let cy = dragStartRect.midY
206
+ var newW = max(dragStartRect.width * scale, minCropSize)
207
+ var newH = max(dragStartRect.height * scale, minCropSize)
208
+ newW = min(newW, allowedRect.width)
209
+ newH = min(newH, allowedRect.height)
210
+ var r = CGRect(x: cx - newW / 2, y: cy - newH / 2, width: newW, height: newH)
211
+ r = clamp(r, isMove: true)
212
+ cropRect = r
213
+ onCropChanged?()
214
+ case .ended, .cancelled:
215
+ onCropEnded?()
216
+ default:
217
+ break
218
+ }
219
+ }
220
+
221
+ // MARK: - Rect Computation
222
+
223
+ private func computeNewRect(edge: DragEdge, dx: CGFloat, dy: CGFloat) -> CGRect {
224
+ var r = dragStartRect
225
+
226
+ switch edge {
227
+ case .topLeft:
228
+ r.origin.x += dx; r.size.width -= dx
229
+ r.origin.y += dy; r.size.height -= dy
230
+ case .topRight:
231
+ r.size.width += dx
232
+ r.origin.y += dy; r.size.height -= dy
233
+ case .bottomLeft:
234
+ r.origin.x += dx; r.size.width -= dx
235
+ r.size.height += dy
236
+ case .bottomRight:
237
+ r.size.width += dx; r.size.height += dy
238
+ case .top:
239
+ r.origin.y += dy; r.size.height -= dy
240
+ case .bottom:
241
+ r.size.height += dy
242
+ case .left:
243
+ r.origin.x += dx; r.size.width -= dx
244
+ case .right:
245
+ r.size.width += dx
246
+ case .move:
247
+ r.origin.x += dx; r.origin.y += dy
248
+ return clamp(r, isMove: true)
249
+ }
250
+
251
+ if r.width < minCropSize {
252
+ let anchorsRight = (edge == .left || edge == .topLeft || edge == .bottomLeft)
253
+ if anchorsRight { r.origin.x = dragStartRect.maxX - minCropSize }
254
+ r.size.width = minCropSize
255
+ }
256
+ if r.height < minCropSize {
257
+ let anchorsBottom = (edge == .top || edge == .topLeft || edge == .topRight)
258
+ if anchorsBottom { r.origin.y = dragStartRect.maxY - minCropSize }
259
+ r.size.height = minCropSize
260
+ }
261
+
262
+ return clamp(r, isMove: false)
263
+ }
264
+
265
+ private func clamp(_ rect: CGRect, isMove: Bool) -> CGRect {
266
+ var r = rect
267
+ let a = allowedRect
268
+ guard !a.isEmpty else { return r }
269
+
270
+ if isMove {
271
+ r.size.width = min(r.width, a.width)
272
+ r.size.height = min(r.height, a.height)
273
+ r.origin.x = max(a.minX, min(r.origin.x, a.maxX - r.width))
274
+ r.origin.y = max(a.minY, min(r.origin.y, a.maxY - r.height))
275
+ } else {
276
+ r.origin.x = max(r.origin.x, a.minX)
277
+ r.origin.y = max(r.origin.y, a.minY)
278
+ if r.maxX > a.maxX { r.size.width = a.maxX - r.origin.x }
279
+ if r.maxY > a.maxY { r.size.height = a.maxY - r.origin.y }
280
+ }
281
+ return r
282
+ }
283
+
284
+ func resetCrop() {
285
+ cropRect = allowedRect
286
+ }
287
+ }
package/ios/VideoTrim.mm CHANGED
@@ -70,10 +70,9 @@ RCT_EXPORT_MODULE()
70
70
  dict[@"outputExt"] = options.outputExt();
71
71
  dict[@"removeAfterSavedToPhoto"] = @(options.removeAfterSavedToPhoto());
72
72
  dict[@"removeAfterFailedToSavePhoto"] = @(options.removeAfterFailedToSavePhoto());
73
- dict[@"enableRotation"] = @(options.enableRotation());
74
- dict[@"rotationAngle"] = @(options.rotationAngle());
75
73
  dict[@"startTime"] = @(options.startTime());
76
74
  dict[@"endTime"] = @(options.endTime());
75
+ dict[@"enablePreciseTrimming"] = @(options.enablePreciseTrimming());
77
76
 
78
77
  [self->videoTrim trim:url url:dict config:^(NSDictionary<NSString *,id> * _Nonnull result) {
79
78
  BOOL success = [result[@"success"] boolValue];
@@ -109,8 +108,6 @@ RCT_EXPORT_MODULE()
109
108
  dict[@"removeAfterFailedToSaveDocuments"] = @(config.removeAfterFailedToSaveDocuments());
110
109
  dict[@"removeAfterShared"] = @(config.removeAfterShared());
111
110
  dict[@"removeAfterFailedToShare"] = @(config.removeAfterFailedToShare());
112
- dict[@"enableRotation"] = @(config.enableRotation());
113
- dict[@"rotationAngle"] = @(config.rotationAngle());
114
111
  dict[@"enableHapticFeedback"] = @(config.enableHapticFeedback());
115
112
  dict[@"maxDuration"] = @(config.maxDuration());
116
113
  dict[@"minDuration"] = @(config.minDuration());
@@ -145,6 +142,7 @@ RCT_EXPORT_MODULE()
145
142
  dict[@"alertOnFailTitle"] = config.alertOnFailTitle();
146
143
  dict[@"alertOnFailMessage"] = config.alertOnFailMessage();
147
144
  dict[@"alertOnFailCloseText"] = config.alertOnFailCloseText();
145
+ dict[@"enablePreciseTrimming"] = @(config.enablePreciseTrimming());
148
146
 
149
147
  // Handle optional color values
150
148
  auto trimmerColorOpt = config.trimmerColor();