react-native-video-trim 7.0.0 → 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.
@@ -50,12 +50,13 @@ class CropOverlayView @JvmOverloads constructor(
50
50
  private val borderWidth = dpToPx(1f)
51
51
  private val cornerLength = dpToPx(20f)
52
52
  private val cornerWidth = dpToPx(4f)
53
+ private val edgeHandleLength = dpToPx(20f)
53
54
  private val gridLineWidth = 1f / resources.displayMetrics.density
54
55
  private val edgeHitZone = dpToPx(30f)
55
56
 
56
57
  private val borderPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
57
58
  style = Paint.Style.STROKE
58
- color = Color.argb(153, 255, 255, 255)
59
+ color = Color.WHITE
59
60
  strokeWidth = borderWidth
60
61
  }
61
62
  private val gridPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
@@ -68,6 +69,7 @@ class CropOverlayView @JvmOverloads constructor(
68
69
  color = Color.WHITE
69
70
  strokeWidth = cornerWidth
70
71
  strokeCap = Paint.Cap.ROUND
72
+ strokeJoin = Paint.Join.ROUND
71
73
  }
72
74
  private val dimmingPaint = Paint().apply {
73
75
  color = Color.argb(140, 0, 0, 0)
@@ -140,14 +142,24 @@ class CropOverlayView @JvmOverloads constructor(
140
142
  }
141
143
 
142
144
  val cl = cornerLength
145
+ val hw = cornerWidth / 2f
143
146
  val path = Path()
144
147
  fun addCorner(sx: Float, sy: Float, cx: Float, cy: Float, ex: Float, ey: Float) {
145
148
  path.moveTo(sx, sy); path.lineTo(cx, cy); path.lineTo(ex, ey)
146
149
  }
147
- addCorner(cr.left, cr.top + cl, cr.left, cr.top, cr.left + cl, cr.top)
148
- addCorner(cr.right - cl, cr.top, cr.right, cr.top, cr.right, cr.top + cl)
149
- addCorner(cr.left, cr.bottom - cl, cr.left, cr.bottom, cr.left + cl, cr.bottom)
150
- addCorner(cr.right - cl, cr.bottom, cr.right, cr.bottom, cr.right, cr.bottom - cl)
150
+ addCorner(cr.left - hw, cr.top + cl, cr.left - hw, cr.top - hw, cr.left + cl, cr.top - hw)
151
+ addCorner(cr.right - cl, cr.top - hw, cr.right + hw, cr.top - hw, cr.right + hw, cr.top + cl)
152
+ addCorner(cr.left - hw, cr.bottom - cl, cr.left - hw, cr.bottom + hw, cr.left + cl, cr.bottom + hw)
153
+ addCorner(cr.right - cl, cr.bottom + hw, cr.right + hw, cr.bottom + hw, cr.right + hw, cr.bottom - cl)
154
+
155
+ val ehl = edgeHandleLength / 2f
156
+ val cx = cr.centerX()
157
+ val cy = cr.centerY()
158
+ path.moveTo(cx - ehl, cr.top - hw); path.lineTo(cx + ehl, cr.top - hw)
159
+ path.moveTo(cx - ehl, cr.bottom + hw); path.lineTo(cx + ehl, cr.bottom + hw)
160
+ path.moveTo(cr.left - hw, cy - ehl); path.lineTo(cr.left - hw, cy + ehl)
161
+ path.moveTo(cr.right + hw, cy - ehl); path.lineTo(cr.right + hw, cy + ehl)
162
+
151
163
  canvas.drawPath(path, cornerPaint)
152
164
  }
153
165
 
@@ -320,20 +320,25 @@ class VideoTrimmerView(
320
320
  if (vw <= 0 || vh <= 0) return
321
321
 
322
322
  videoContainer.post {
323
- val containerW = videoContainer.width
324
- val containerH = videoContainer.height
325
- if (containerW <= 0 || containerH <= 0) return@post
323
+ val cw = containerContentWidth().toInt()
324
+ val ch = containerContentHeight().toInt()
325
+ if (cw <= 0 || ch <= 0) return@post
326
+
327
+ val margin = bracketOverflow()
328
+ val availW = cw - 2 * margin
329
+ val availH = ch - 2 * margin
330
+ if (availW <= 0 || availH <= 0) return@post
326
331
 
327
332
  val videoAR = vw.toFloat() / vh
328
- val containerAR = containerW.toFloat() / containerH
333
+ val containerAR = availW.toFloat() / availH
329
334
  val newW: Int
330
335
  val newH: Int
331
336
  if (videoAR > containerAR) {
332
- newW = containerW
333
- newH = (containerW / videoAR).toInt()
337
+ newW = availW
338
+ newH = (availW / videoAR).toInt()
334
339
  } else {
335
- newH = containerH
336
- newW = (containerH * videoAR).toInt()
340
+ newH = availH
341
+ newW = (availH * videoAR).toInt()
337
342
  }
338
343
  mVideoView.layoutParams = FrameLayout.LayoutParams(newW, newH, Gravity.CENTER)
339
344
  }
@@ -519,6 +524,10 @@ class VideoTrimmerView(
519
524
  cropBtn.setOnClickListener { onCropTapped() }
520
525
  undoBtn.setOnClickListener { onUndoTapped() }
521
526
  redoBtn.setOnClickListener { onRedoTapped() }
527
+
528
+ cropBtn.setColorFilter(Color.argb(128, 255, 255, 255), android.graphics.PorterDuff.Mode.SRC_IN)
529
+ undoBtn.setColorFilter(Color.argb(128, 255, 255, 255), android.graphics.PorterDuff.Mode.SRC_IN)
530
+ redoBtn.setColorFilter(Color.argb(128, 255, 255, 255), android.graphics.PorterDuff.Mode.SRC_IN)
522
531
  }
523
532
 
524
533
  fun onSaveClicked() {
@@ -1304,13 +1313,22 @@ class VideoTrimmerView(
1304
1313
 
1305
1314
  // region Transform
1306
1315
 
1316
+ private fun containerContentWidth(): Float =
1317
+ (videoContainer.width - videoContainer.paddingLeft - videoContainer.paddingRight).toFloat()
1318
+
1319
+ private fun containerContentHeight(): Float =
1320
+ (videoContainer.height - videoContainer.paddingTop - videoContainer.paddingBottom).toFloat()
1321
+
1322
+ private fun bracketOverflow(): Int =
1323
+ TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4f, resources.displayMetrics).toInt()
1324
+
1307
1325
  private fun onFlipTapped() {
1308
1326
  pushUndo()
1309
1327
  isFlipped = !isFlipped
1310
1328
  val newCumDeg = -cumulativeRotationDeg
1311
1329
  val fitScale = if (rotationCount % 2 != 0) {
1312
- val cw = videoContainer.width.toFloat()
1313
- val ch = videoContainer.height.toFloat()
1330
+ val cw = containerContentWidth()
1331
+ val ch = containerContentHeight()
1314
1332
  if (cw > 0 && ch > 0) minOf(cw / ch, ch / cw) else 1f
1315
1333
  } else {
1316
1334
  1f
@@ -1379,12 +1397,12 @@ class VideoTrimmerView(
1379
1397
  }
1380
1398
 
1381
1399
  private fun updateVideoTransform(resetCrop: Boolean = false) {
1382
- val containerW = videoContainer.width.toFloat()
1383
- val containerH = videoContainer.height.toFloat()
1384
- if (containerW <= 0 || containerH <= 0) return
1400
+ val cw = containerContentWidth()
1401
+ val ch = containerContentHeight()
1402
+ if (cw <= 0 || ch <= 0) return
1385
1403
 
1386
1404
  val fitScale = if (rotationCount % 2 != 0) {
1387
- minOf(containerW / containerH, containerH / containerW)
1405
+ minOf(cw / ch, ch / cw)
1388
1406
  } else {
1389
1407
  1f
1390
1408
  }
@@ -1487,22 +1505,22 @@ class VideoTrimmerView(
1487
1505
  }
1488
1506
 
1489
1507
  private fun getVideoDisplayRectInContainer(): RectF {
1490
- val containerW = videoContainer.width.toFloat()
1491
- val containerH = videoContainer.height.toFloat()
1492
- if (containerW <= 0 || containerH <= 0) return RectF()
1508
+ val cw = containerContentWidth()
1509
+ val ch = containerContentHeight()
1510
+ if (cw <= 0 || ch <= 0) return RectF()
1493
1511
 
1494
1512
  val tvW = mVideoView.width.toFloat()
1495
1513
  val tvH = mVideoView.height.toFloat()
1496
1514
  if (tvW <= 0 || tvH <= 0) return RectF()
1497
1515
 
1498
- val tvX = (containerW - tvW) / 2f
1499
- val tvY = (containerH - tvH) / 2f
1516
+ val tvX = (cw - tvW) / 2f
1517
+ val tvY = (ch - tvH) / 2f
1500
1518
  val videoRect = RectF(tvX, tvY, tvX + tvW, tvY + tvH)
1501
1519
 
1502
- val pivotX = containerW / 2f
1503
- val pivotY = containerH / 2f
1520
+ val pivotX = cw / 2f
1521
+ val pivotY = ch / 2f
1504
1522
  val fitScale = if (rotationCount % 2 != 0) {
1505
- minOf(containerW / containerH, containerH / containerW)
1523
+ minOf(cw / ch, ch / cw)
1506
1524
  } else {
1507
1525
  1f
1508
1526
  }
@@ -1603,10 +1621,10 @@ class VideoTrimmerView(
1603
1621
  isFlipped = snap.isFlipped
1604
1622
  cumulativeRotationDeg = snap.cumulativeRotationDeg
1605
1623
 
1606
- val containerW = videoContainer.width.toFloat()
1607
- val containerH = videoContainer.height.toFloat()
1608
- val fitScale = if (rotationCount % 2 != 0 && containerW > 0 && containerH > 0) {
1609
- minOf(containerW / containerH, containerH / containerW)
1624
+ val cw = containerContentWidth()
1625
+ val ch = containerContentHeight()
1626
+ val fitScale = if (rotationCount % 2 != 0 && cw > 0 && ch > 0) {
1627
+ minOf(cw / ch, ch / cw)
1610
1628
  } else {
1611
1629
  1f
1612
1630
  }
@@ -94,8 +94,8 @@
94
94
 
95
95
  <ImageView
96
96
  android:id="@+id/undoBtn"
97
- android:layout_width="22dp"
98
- android:layout_height="22dp"
97
+ android:layout_width="18dp"
98
+ android:layout_height="18dp"
99
99
  android:src="@drawable/arrow_uturn_backward"
100
100
  android:tint="#80FFFFFF"
101
101
  android:scaleType="fitCenter"
@@ -104,8 +104,8 @@
104
104
 
105
105
  <ImageView
106
106
  android:id="@+id/redoBtn"
107
- android:layout_width="22dp"
108
- android:layout_height="22dp"
107
+ android:layout_width="18dp"
108
+ android:layout_height="18dp"
109
109
  android:src="@drawable/arrow_uturn_forward"
110
110
  android:tint="#80FFFFFF"
111
111
  android:scaleType="fitCenter"
@@ -120,7 +120,9 @@
120
120
  android:layout_above="@+id/layout"
121
121
  android:layout_below="@+id/transformRow"
122
122
  android:background="@android:color/black"
123
- android:clipChildren="true">
123
+ android:clipChildren="true"
124
+ android:clipToPadding="false"
125
+ android:paddingVertical="4dp">
124
126
 
125
127
  <TextureView
126
128
  android:id="@+id/video_loader"
@@ -6,7 +6,6 @@ class CropOverlayView: UIView {
6
6
  var cropRect: CGRect = .zero {
7
7
  didSet {
8
8
  setNeedsDisplay()
9
- updateDimmingMask()
10
9
  }
11
10
  }
12
11
 
@@ -31,6 +30,7 @@ class CropOverlayView: UIView {
31
30
  private let borderWidth: CGFloat = 1.0
32
31
  private let cornerLength: CGFloat = 20
33
32
  private let cornerWidth: CGFloat = 4.0
33
+ private let edgeHandleLength: CGFloat = 20
34
34
  private let gridLineWidth: CGFloat = CGFloat(1.0 / UIScreen.main.scale)
35
35
  private let edgeHitZone: CGFloat = 30
36
36
 
@@ -38,8 +38,6 @@ class CropOverlayView: UIView {
38
38
  private var dragStart: CGPoint = .zero
39
39
  private var dragStartRect: CGRect = .zero
40
40
 
41
- private let dimmingLayer = CAShapeLayer()
42
-
43
41
  private enum DragEdge {
44
42
  case top, bottom, left, right
45
43
  case topLeft, topRight, bottomLeft, bottomRight
@@ -59,13 +57,9 @@ class CropOverlayView: UIView {
59
57
  private func commonInit() {
60
58
  backgroundColor = .clear
61
59
  isUserInteractionEnabled = true
62
- clipsToBounds = true
60
+ clipsToBounds = false
63
61
  isOpaque = false
64
62
 
65
- dimmingLayer.fillRule = .evenOdd
66
- dimmingLayer.fillColor = UIColor.black.withAlphaComponent(0.55).cgColor
67
- layer.addSublayer(dimmingLayer)
68
-
69
63
  let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
70
64
  addGestureRecognizer(pan)
71
65
 
@@ -73,32 +67,26 @@ class CropOverlayView: UIView {
73
67
  addGestureRecognizer(pinch)
74
68
  }
75
69
 
76
- override func layoutSubviews() {
77
- super.layoutSubviews()
78
- updateDimmingMask()
79
- }
80
-
81
- private func updateDimmingMask() {
82
- let full = UIBezierPath(rect: bounds)
83
- if !cropRect.isEmpty {
84
- full.append(UIBezierPath(rect: cropRect))
85
- }
86
- dimmingLayer.path = full.cgPath
87
- dimmingLayer.frame = bounds
88
- }
89
-
90
70
  // MARK: - Drawing
91
71
 
92
72
  override func draw(_ rect: CGRect) {
93
73
  guard !cropRect.isEmpty, let ctx = UIGraphicsGetCurrentContext() else { return }
94
74
  let cr = cropRect
95
75
 
96
- ctx.setStrokeColor(UIColor.white.withAlphaComponent(0.6).cgColor)
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)
97
86
  ctx.setLineWidth(borderWidth)
98
87
  ctx.stroke(cr)
99
88
 
100
89
  ctx.setLineWidth(gridLineWidth)
101
- ctx.setStrokeColor(UIColor.white.cgColor)
102
90
  for i in 1...2 {
103
91
  let x = cr.minX + cr.width * CGFloat(i) / 3
104
92
  ctx.move(to: CGPoint(x: x, y: cr.minY))
@@ -114,27 +102,41 @@ class CropOverlayView: UIView {
114
102
  ctx.setStrokeColor(UIColor.white.cgColor)
115
103
  ctx.setLineWidth(cornerWidth)
116
104
  ctx.setLineCap(.round)
105
+ ctx.setLineJoin(.round)
117
106
 
118
107
  let cl = cornerLength
108
+ let hw = cornerWidth / 2
119
109
  let corners: [(CGPoint, CGPoint, CGPoint)] = [
120
- (CGPoint(x: cr.minX, y: cr.minY + cl),
121
- CGPoint(x: cr.minX, y: cr.minY),
122
- CGPoint(x: cr.minX + cl, y: cr.minY)),
123
- (CGPoint(x: cr.maxX - cl, y: cr.minY),
124
- CGPoint(x: cr.maxX, y: cr.minY),
125
- CGPoint(x: cr.maxX, y: cr.minY + cl)),
126
- (CGPoint(x: cr.minX, y: cr.maxY - cl),
127
- CGPoint(x: cr.minX, y: cr.maxY),
128
- CGPoint(x: cr.minX + cl, y: cr.maxY)),
129
- (CGPoint(x: cr.maxX - cl, y: cr.maxY),
130
- CGPoint(x: cr.maxX, y: cr.maxY),
131
- CGPoint(x: cr.maxX, y: cr.maxY - cl)),
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)),
132
122
  ]
133
123
  for (start, corner, end) in corners {
134
124
  ctx.move(to: start)
135
125
  ctx.addLine(to: corner)
136
126
  ctx.addLine(to: end)
137
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
+
138
140
  ctx.strokePath()
139
141
  }
140
142
 
@@ -469,11 +469,12 @@ class VideoTrimmerViewController: UIViewController {
469
469
  addChild(playerController)
470
470
  playerContainerView.addSubview(playerController.view)
471
471
  playerController.view.translatesAutoresizingMaskIntoConstraints = false
472
+ let bracketInset: CGFloat = 4
472
473
  NSLayoutConstraint.activate([
473
- playerController.view.leadingAnchor.constraint(equalTo: playerContainerView.leadingAnchor),
474
- playerController.view.trailingAnchor.constraint(equalTo: playerContainerView.trailingAnchor),
475
- playerController.view.topAnchor.constraint(equalTo: playerContainerView.topAnchor),
476
- playerController.view.bottomAnchor.constraint(equalTo: playerContainerView.bottomAnchor)
474
+ playerController.view.leadingAnchor.constraint(equalTo: playerContainerView.leadingAnchor, constant: bracketInset),
475
+ playerController.view.trailingAnchor.constraint(equalTo: playerContainerView.trailingAnchor, constant: -bracketInset),
476
+ playerController.view.topAnchor.constraint(equalTo: playerContainerView.topAnchor, constant: bracketInset),
477
+ playerController.view.bottomAnchor.constraint(equalTo: playerContainerView.bottomAnchor, constant: -bracketInset)
477
478
  ])
478
479
 
479
480
  // Add observer for the end of playback
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-video-trim",
3
- "version": "7.0.0",
3
+ "version": "7.0.1",
4
4
  "description": "Video trimmer for your React Native app",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",