improve player gesture

Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
dom
2026-05-09 12:25:59 +08:00
parent 0292517947
commit bbdcc1d3ce
2 changed files with 168 additions and 244 deletions

View File

@@ -16,29 +16,30 @@ import 'package:vector_math/vector_math_64.dart' show Quad, Vector3;
class MouseInteractiveViewer extends StatefulWidget { class MouseInteractiveViewer extends StatefulWidget {
const MouseInteractiveViewer({ const MouseInteractiveViewer({
super.key, super.key,
this.clipBehavior = Clip.hardEdge, this.clipBehavior = .hardEdge,
this.panAxis = PanAxis.free, this.panAxis = .free,
this.boundaryMargin = EdgeInsets.zero, this.boundaryMargin = .zero,
this.constrained = true, this.constrained = true,
this.maxScale = 2.5, this.maxScale = 2.5,
this.minScale = 0.8, this.minScale = 0.8,
this.interactionEndFrictionCoefficient = _kDrag, this.interactionEndFrictionCoefficient = _kDrag,
this.pointerSignalFallback, required this.pointerSignalFallback,
this.onPointerPanZoomUpdate, this.onPointerPanZoomUpdate,
this.onPointerPanZoomEnd, this.onPointerPanZoomEnd,
this.onPointerDown, required this.onPointerDown,
this.onInteractionEnd, required this.onPanEnd,
this.onInteractionStart, required this.onPanStart,
this.onInteractionUpdate, required this.onPanUpdate,
required this.onScaleUpdate,
this.panEnabled = true, this.panEnabled = true,
this.scaleEnabled = true, this.scaleEnabled = true,
this.scaleFactor = kDefaultMouseScrollToScaleFactor, this.scaleFactor = kDefaultMouseScrollToScaleFactor,
this.transformationController, required this.transformationController,
this.alignment, this.alignment,
this.trackpadScrollCausesScale = false, this.trackpadScrollCausesScale = false,
required this.childKey, required this.childKey,
required this.child, required this.child,
required this.onTranslate, required this.scaleGestureRecognizer,
}) : assert(minScale > 0), }) : assert(minScale > 0),
assert(interactionEndFrictionCoefficient > 0), assert(interactionEndFrictionCoefficient > 0),
assert(maxScale > 0), assert(maxScale > 0),
@@ -57,16 +58,17 @@ class MouseInteractiveViewer extends StatefulWidget {
final double maxScale; final double maxScale;
final double minScale; final double minScale;
final double interactionEndFrictionCoefficient; final double interactionEndFrictionCoefficient;
final PointerSignalEventListener? pointerSignalFallback; final PointerSignalEventListener pointerSignalFallback;
final PointerPanZoomUpdateEventListener? onPointerPanZoomUpdate; final PointerPanZoomUpdateEventListener? onPointerPanZoomUpdate;
final PointerPanZoomEndEventListener? onPointerPanZoomEnd; final PointerPanZoomEndEventListener? onPointerPanZoomEnd;
final PointerDownEventListener? onPointerDown; final PointerDownEventListener onPointerDown;
final GestureScaleEndCallback? onInteractionEnd; final GestureScaleEndCallback onPanEnd;
final GestureScaleStartCallback? onInteractionStart; final GestureScaleStartCallback onPanStart;
final GestureScaleUpdateCallback? onInteractionUpdate; final GestureScaleUpdateCallback onPanUpdate;
final TransformationController? transformationController; final ValueChanged<double> onScaleUpdate;
final TransformationController transformationController;
final GlobalKey childKey; final GlobalKey childKey;
final VoidCallback onTranslate; final ScaleGestureRecognizer scaleGestureRecognizer;
static const double _kDrag = 0.0000135; static const double _kDrag = 0.0000135;
@@ -76,8 +78,7 @@ class MouseInteractiveViewer extends StatefulWidget {
class _MouseInteractiveViewerState extends State<MouseInteractiveViewer> class _MouseInteractiveViewerState extends State<MouseInteractiveViewer>
with TickerProviderStateMixin { with TickerProviderStateMixin {
late TransformationController _transformer = late TransformationController _transformer;
widget.transformationController ?? TransformationController();
final GlobalKey _parentKey = GlobalKey(); final GlobalKey _parentKey = GlobalKey();
Animation<Offset>? _animation; Animation<Offset>? _animation;
@@ -234,6 +235,9 @@ class _MouseInteractiveViewerState extends State<MouseInteractiveViewer>
widget.minScale, widget.minScale,
widget.maxScale, widget.maxScale,
); );
widget.onScaleUpdate(clampedTotalScale);
final double clampedScale = clampedTotalScale / currentScale; final double clampedScale = clampedTotalScale / currentScale;
return matrix.clone() return matrix.clone()
..scaleByDouble(clampedScale, clampedScale, clampedScale, 1); ..scaleByDouble(clampedScale, clampedScale, clampedScale, 1);
@@ -270,10 +274,15 @@ class _MouseInteractiveViewerState extends State<MouseInteractiveViewer>
} }
} }
bool _isSinglePointer = false;
// Handle the start of a gesture. All of pan, scale, and rotate are handled // Handle the start of a gesture. All of pan, scale, and rotate are handled
// with GestureDetector's scale gesture. // with GestureDetector's scale gesture.
void _onScaleStart(ScaleStartDetails details) { void _onScaleStart(ScaleStartDetails details) {
widget.onInteractionStart?.call(details); if (_isSinglePointer = details.pointerCount == 1) {
widget.onPanStart(details);
return;
}
if (_controller.isAnimating) { if (_controller.isAnimating) {
_controller _controller
@@ -300,6 +309,11 @@ class _MouseInteractiveViewerState extends State<MouseInteractiveViewer>
// Handle an update to an ongoing gesture. All of pan, scale, and rotate are // Handle an update to an ongoing gesture. All of pan, scale, and rotate are
// handled with GestureDetector's scale gesture. // handled with GestureDetector's scale gesture.
void _onScaleUpdate(ScaleUpdateDetails details) { void _onScaleUpdate(ScaleUpdateDetails details) {
if (_isSinglePointer) {
widget.onPanUpdate(details);
return;
}
final double scale = _transformer.value.getMaxScaleOnAxis(); final double scale = _transformer.value.getMaxScaleOnAxis();
_scaleAnimationFocalPoint = details.localFocalPoint; _scaleAnimationFocalPoint = details.localFocalPoint;
final Offset focalPointScene = _transformer.toScene( final Offset focalPointScene = _transformer.toScene(
@@ -316,7 +330,6 @@ class _MouseInteractiveViewerState extends State<MouseInteractiveViewer>
_gestureType ??= _getGestureType(details); _gestureType ??= _getGestureType(details);
} }
if (!_gestureIsSupported(_gestureType)) { if (!_gestureIsSupported(_gestureType)) {
widget.onInteractionUpdate?.call(details);
return; return;
} }
@@ -356,7 +369,6 @@ class _MouseInteractiveViewerState extends State<MouseInteractiveViewer>
case _GestureType.rotate: case _GestureType.rotate:
if (details.rotation == 0.0) { if (details.rotation == 0.0) {
widget.onInteractionUpdate?.call(details);
return; return;
} }
final double desiredRotation = _rotationStart! + details.rotation; final double desiredRotation = _rotationStart! + details.rotation;
@@ -373,7 +385,6 @@ class _MouseInteractiveViewerState extends State<MouseInteractiveViewer>
// In an effort to keep the behavior similar whether or not scaleEnabled // In an effort to keep the behavior similar whether or not scaleEnabled
// is true, these gestures are thrown away. // is true, these gestures are thrown away.
if (details.scale != 1.0) { if (details.scale != 1.0) {
widget.onInteractionUpdate?.call(details);
return; return;
} }
_currentAxis ??= _getPanAxis(_referenceFocalPoint!, focalPointScene); _currentAxis ??= _getPanAxis(_referenceFocalPoint!, focalPointScene);
@@ -387,13 +398,16 @@ class _MouseInteractiveViewerState extends State<MouseInteractiveViewer>
); );
_referenceFocalPoint = _transformer.toScene(details.localFocalPoint); _referenceFocalPoint = _transformer.toScene(details.localFocalPoint);
} }
widget.onInteractionUpdate?.call(details);
} }
// Handle the end of a gesture of _GestureType. All of pan, scale, and rotate // Handle the end of a gesture of _GestureType. All of pan, scale, and rotate
// are handled with GestureDetector's scale gesture. // are handled with GestureDetector's scale gesture.
void _onScaleEnd(ScaleEndDetails details) { void _onScaleEnd(ScaleEndDetails details) {
widget.onInteractionEnd?.call(details); if (_isSinglePointer) {
widget.onPanEnd(details);
return;
}
_scaleStart = null; _scaleStart = null;
_rotationStart = null; _rotationStart = null;
_referenceFocalPoint = null; _referenceFocalPoint = null;
@@ -481,10 +495,6 @@ class _MouseInteractiveViewerState extends State<MouseInteractiveViewer>
final double scaleChange; final double scaleChange;
if (event is PointerScrollEvent) { if (event is PointerScrollEvent) {
if (event.kind == PointerDeviceKind.trackpad) { if (event.kind == PointerDeviceKind.trackpad) {
widget.onInteractionStart?.call(
ScaleStartDetails(focalPoint: global, localFocalPoint: local),
);
final Offset localDelta = PointerEvent.transformDeltaViaPositions( final Offset localDelta = PointerEvent.transformDeltaViaPositions(
untransformedEndPosition: global + event.scrollDelta, untransformedEndPosition: global + event.scrollDelta,
untransformedDelta: event.scrollDelta, untransformedDelta: event.scrollDelta,
@@ -501,14 +511,6 @@ class _MouseInteractiveViewerState extends State<MouseInteractiveViewer>
newFocalPointScene - focalPointScene, newFocalPointScene - focalPointScene,
); );
widget.onInteractionUpdate?.call(
ScaleUpdateDetails(
focalPoint: global - event.scrollDelta,
localFocalPoint: local - localDelta,
focalPointDelta: -localDelta,
),
);
widget.onInteractionEnd?.call(ScaleEndDetails());
return; return;
} }
_handlePointerScrollEvent(event); _handlePointerScrollEvent(event);
@@ -518,19 +520,8 @@ class _MouseInteractiveViewerState extends State<MouseInteractiveViewer>
} else { } else {
return; return;
} }
widget.onInteractionStart?.call(
ScaleStartDetails(focalPoint: global, localFocalPoint: local),
);
if (!_gestureIsSupported(_GestureType.scale)) { if (!_gestureIsSupported(_GestureType.scale)) {
widget.onInteractionUpdate?.call(
ScaleUpdateDetails(
focalPoint: global,
localFocalPoint: local,
scale: scaleChange,
),
);
widget.onInteractionEnd?.call(ScaleEndDetails());
return; return;
} }
@@ -544,22 +535,12 @@ class _MouseInteractiveViewerState extends State<MouseInteractiveViewer>
_transformer.value, _transformer.value,
focalPointSceneScaled - focalPointScene, focalPointSceneScaled - focalPointScene,
); );
widget.onInteractionUpdate?.call(
ScaleUpdateDetails(
focalPoint: global,
localFocalPoint: local,
scale: scaleChange,
),
);
widget.onInteractionEnd?.call(ScaleEndDetails());
} }
void _handlePointerScrollEvent(PointerScrollEvent event) { void _handlePointerScrollEvent(PointerScrollEvent event) {
final Offset local = event.localPosition;
final Offset global = event.position;
if (_gestureIsSupported(_GestureType.scale)) { if (_gestureIsSupported(_GestureType.scale)) {
final Offset local = event.localPosition;
final Offset global = event.position;
if (HardwareKeyboard.instance.isControlPressed) { if (HardwareKeyboard.instance.isControlPressed) {
_handleMouseWheelScale(event, local, global); _handleMouseWheelScale(event, local, global);
return; return;
@@ -569,16 +550,8 @@ class _MouseInteractiveViewerState extends State<MouseInteractiveViewer>
_handleMouseWheelPanAsScale(event, local, global, shift); _handleMouseWheelPanAsScale(event, local, global, shift);
return; return;
} }
widget.pointerSignalFallback?.call(event); widget.pointerSignalFallback(event);
} }
widget.onInteractionUpdate?.call(
ScaleUpdateDetails(
focalPoint: global,
localFocalPoint: local,
scale: math.exp(-event.scrollDelta.dy / widget.scaleFactor),
),
);
widget.onInteractionEnd?.call(ScaleEndDetails());
} }
void _handleMouseWheelScale( void _handleMouseWheelScale(
@@ -597,15 +570,6 @@ class _MouseInteractiveViewerState extends State<MouseInteractiveViewer>
_transformer.value, _transformer.value,
focalPointSceneScaled - focalPointScene, focalPointSceneScaled - focalPointScene,
); );
widget.onInteractionUpdate?.call(
ScaleUpdateDetails(
focalPoint: global,
localFocalPoint: local,
scale: scaleChange,
),
);
widget.onInteractionEnd?.call(ScaleEndDetails());
} }
void _handleMouseWheelPanAsScale( void _handleMouseWheelPanAsScale(
@@ -625,8 +589,6 @@ class _MouseInteractiveViewerState extends State<MouseInteractiveViewer>
_transformer.value, _transformer.value,
newFocalPointScene - focalPointScene, newFocalPointScene - focalPointScene,
); );
widget.onTranslate();
} }
void _handleInertiaAnimation() { void _handleInertiaAnimation() {
@@ -675,33 +637,18 @@ class _MouseInteractiveViewerState extends State<MouseInteractiveViewer>
setState(() {}); setState(() {});
} }
void _onPointerDown(PointerDownEvent event) {
final localPosition = event.localPosition;
if (localPosition.dx < 40 || localPosition.dy < 40) {
return;
}
widget.onPointerDown?.call(event);
_scaleGestureRecognizer.addPointer(event);
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_scaleGestureRecognizer = _scaleGestureRecognizer = widget.scaleGestureRecognizer
ScaleGestureRecognizer( ..gestureSettings = gestureSettings
debugOwner: this, ..onStart = _onScaleStart
dragStartBehavior: .start, ..onUpdate = _onScaleUpdate
allowedButtonsFilter: (buttons) => buttons == kPrimaryButton, ..onEnd = _onScaleEnd;
trackpadScrollToScaleFactor: Offset(0, -1 / widget.scaleFactor),
trackpadScrollCausesScale: widget.trackpadScrollCausesScale,
)
..gestureSettings = gestureSettings
..onStart = _onScaleStart
..onUpdate = _onScaleUpdate
..onEnd = _onScaleEnd;
_controller = AnimationController(vsync: this); _controller = AnimationController(vsync: this);
_scaleController = AnimationController(vsync: this); _scaleController = AnimationController(vsync: this);
_transformer = widget.transformationController;
_transformer.addListener(_handleTransformation); _transformer.addListener(_handleTransformation);
} }
@@ -709,28 +656,20 @@ class _MouseInteractiveViewerState extends State<MouseInteractiveViewer>
void didUpdateWidget(MouseInteractiveViewer oldWidget) { void didUpdateWidget(MouseInteractiveViewer oldWidget) {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
final TransformationController? newController = final newController = widget.transformationController;
widget.transformationController;
if (newController == oldWidget.transformationController) { if (newController == oldWidget.transformationController) {
return; return;
} }
_transformer.removeListener(_handleTransformation); _transformer.removeListener(_handleTransformation);
if (oldWidget.transformationController == null) { _transformer = newController;
_transformer.dispose();
}
_transformer = newController ?? TransformationController();
_transformer.addListener(_handleTransformation); _transformer.addListener(_handleTransformation);
} }
@override @override
void dispose() { void dispose() {
_scaleGestureRecognizer.dispose();
_controller.dispose(); _controller.dispose();
_scaleController.dispose(); _scaleController.dispose();
_transformer.removeListener(_handleTransformation); _transformer.removeListener(_handleTransformation);
if (widget.transformationController == null) {
_transformer.dispose();
}
super.dispose(); super.dispose();
} }
@@ -742,7 +681,7 @@ class _MouseInteractiveViewerState extends State<MouseInteractiveViewer>
key: _parentKey, key: _parentKey,
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
onPointerSignal: _receivedPointerSignal, onPointerSignal: _receivedPointerSignal,
onPointerDown: _onPointerDown, onPointerDown: widget.onPointerDown,
onPointerPanZoomStart: _scaleGestureRecognizer.addPointerPanZoom, onPointerPanZoomStart: _scaleGestureRecognizer.addPointerPanZoom,
onPointerPanZoomUpdate: widget.onPointerPanZoomUpdate, onPointerPanZoomUpdate: widget.onPointerPanZoomUpdate,
onPointerPanZoomEnd: widget.onPointerPanZoomEnd, onPointerPanZoomEnd: widget.onPointerPanZoomEnd,

View File

@@ -110,7 +110,7 @@ class PLVideoPlayer extends StatefulWidget {
class _PLVideoPlayerState extends State<PLVideoPlayer> class _PLVideoPlayerState extends State<PLVideoPlayer>
with WidgetsBindingObserver, TickerProviderStateMixin { with WidgetsBindingObserver, TickerProviderStateMixin {
late AnimationController animationController; late AnimationController _animationController;
late VideoController videoController; late VideoController videoController;
late final CommonIntroController introController = widget.introController!; late final CommonIntroController introController = widget.introController!;
late final VideoDetailController videoDetailController = late final VideoDetailController videoDetailController =
@@ -126,15 +126,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
late final RxBool showRestoreScaleBtn = false.obs; late final RxBool showRestoreScaleBtn = false.obs;
GestureType? _gestureType; GestureType? _gestureType;
Offset? _initialFocalPoint;
Offset initialFocalPoint = Offset.zero;
//播放器放缩
bool interacting = false;
// 阅读器限制
// Timer? _accessibilityDebounce;
// double _lastAnnouncedValue = -1;
bool _pauseDueToPauseUponEnteringBackgroundMode = false; bool _pauseDueToPauseUponEnteringBackgroundMode = false;
@@ -161,9 +153,9 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
} }
if (visible) { if (visible) {
animationController.forward(); _animationController.forward();
} else { } else {
animationController.reverse(); _animationController.reverse();
} }
if (widget.videoDetailController case final controller?) { if (widget.videoDetailController case final controller?) {
@@ -197,9 +189,9 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
_onControlChanged, _onControlChanged,
); );
transformationController = TransformationController(); _transformationController = TransformationController();
animationController = AnimationController( _animationController = AnimationController(
vsync: this, vsync: this,
duration: const Duration(milliseconds: 100), duration: const Duration(milliseconds: 100),
); );
@@ -250,6 +242,17 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
_tapGestureRecognizer = TapGestureRecognizer()..onTapUp = _onTapUp; _tapGestureRecognizer = TapGestureRecognizer()..onTapUp = _onTapUp;
_doubleTapGestureRecognizer = DoubleTapGestureRecognizer() _doubleTapGestureRecognizer = DoubleTapGestureRecognizer()
..onDoubleTapDown = _onDoubleTapDown; ..onDoubleTapDown = _onDoubleTapDown;
_scaleGestureRecognizer = ScaleGestureRecognizer(
debugOwner: this,
dragStartBehavior: .start,
allowedButtonsFilter: (buttons) => buttons == kPrimaryButton,
trackpadScrollToScaleFactor: const Offset(
0,
-1 / kDefaultMouseScrollToScaleFactor,
),
trackpadScrollCausesScale: false,
);
} }
@override @override
@@ -289,17 +292,17 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
@override @override
void dispose() { void dispose() {
removeObserverMobile(this); removeObserverMobile(this);
_danmakuListener?.cancel();
_tapGestureRecognizer.dispose(); _tapGestureRecognizer.dispose();
_longPressRecognizer?.dispose(); _longPressRecognizer?.dispose();
_doubleTapGestureRecognizer.dispose(); _doubleTapGestureRecognizer.dispose();
_scaleGestureRecognizer.dispose();
_brightnessListener?.cancel(); _brightnessListener?.cancel();
_controlsListener?.cancel(); _controlsListener?.cancel();
animationController.dispose(); _animationController.dispose();
_transformationController.dispose();
if (PlatformUtils.isMobile) { if (PlatformUtils.isMobile) {
FlutterVolumeController.removeListener(); FlutterVolumeController.removeListener();
} }
transformationController.dispose();
super.dispose(); super.dispose();
} }
@@ -800,7 +803,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
bool get isFullScreen => plPlayerController.isFullScreen.value; bool get isFullScreen => plPlayerController.isFullScreen.value;
late final TransformationController transformationController; late final TransformationController _transformationController;
late ColorScheme colorScheme; late ColorScheme colorScheme;
late double maxWidth; late double maxWidth;
@@ -812,74 +815,55 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
colorScheme = ColorScheme.of(context); colorScheme = ColorScheme.of(context);
} }
void _onInteractionStart(ScaleStartDetails details) { void _onPanStart(ScaleStartDetails details) {
if (plPlayerController.controlsLock.value) return;
final localFocalPoint = details.localFocalPoint;
if (localFocalPoint.dx > maxWidth - 40 ||
localFocalPoint.dy > maxHeight - 40) {
return;
}
if (details.pointerCount > 1) {
interacting = true;
}
initialFocalPoint = localFocalPoint;
_gestureType = null; _gestureType = null;
_initialFocalPoint = details.localFocalPoint;
} }
void _onInteractionUpdate(ScaleUpdateDetails details) { void _onScaleUpdate(double scale) {
showRestoreScaleBtn.value = showRestoreScaleBtn.value = scale != 1.0;
transformationController.value.storage[0] != 1.0; }
if (interacting || initialFocalPoint == Offset.zero) {
return;
}
Offset cumulativeDelta = details.localFocalPoint - initialFocalPoint;
if (details.pointerCount > 1 && cumulativeDelta.distanceSquared < 2.25) {
interacting = true;
_gestureType = null;
return;
}
/// 锁定时禁用
if (plPlayerController.controlsLock.value) return;
void _onPanUpdate(ScaleUpdateDetails details) {
if (_gestureType == null) { if (_gestureType == null) {
final cumulativeDelta = details.localFocalPoint - _initialFocalPoint!;
if (cumulativeDelta.distanceSquared < 1) return; if (cumulativeDelta.distanceSquared < 1) return;
final dx = cumulativeDelta.dx.abs(); final dx = cumulativeDelta.dx.abs();
final dy = cumulativeDelta.dy.abs(); final dy = cumulativeDelta.dy.abs();
if (dx > 3 * dy) {
_gestureType = GestureType.horizontal;
} else if (dy > 3 * dx) {
// _gestureType = 'vertical';
if (dx > 3 * dy) {
_gestureType = .horizontal;
} else if (dy > 3 * dx) {
final double tapPosition = details.localFocalPoint.dx; final double tapPosition = details.localFocalPoint.dx;
final double sectionWidth = maxWidth / 3; final double sectionWidth = maxWidth / 3;
if (tapPosition < sectionWidth) { if (tapPosition < sectionWidth) {
if (PlatformUtils.isDesktop) {
return;
}
// 左边区域 // 左边区域
_gestureType = GestureType.left; if (PlatformUtils.isDesktop) {
_gestureType = .right;
} else {
_gestureType = .left;
}
} else if (tapPosition < sectionWidth * 2) { } else if (tapPosition < sectionWidth * 2) {
// 全屏 // 全屏
_gestureType = GestureType.center; _gestureType = .center;
} else { } else {
// 右边区域 // 右边区域
_gestureType = GestureType.right; _gestureType = .right;
} }
} else {
return;
} }
return;
} }
Offset delta = details.focalPointDelta; final delta = details.focalPointDelta;
if (_gestureType == GestureType.horizontal) { if (_gestureType == .horizontal) {
// live模式下禁用 // live模式下禁用
if (plPlayerController.isLive) return; if (plPlayerController.isLive) return;
final int curSliderPosition = final curSliderPosition =
plPlayerController.sliderPosition.inMilliseconds; plPlayerController.sliderPosition.inMilliseconds;
final int newPos = final newPos =
(curSliderPosition + (curSliderPosition +
(plPlayerController.sliderScale * delta.dx / maxWidth) (plPlayerController.sliderScale * delta.dx / maxWidth)
.round()) .round())
@@ -934,31 +918,25 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
plPlayerController.cancelSeek != true) { plPlayerController.cancelSeek != true) {
plPlayerController.updatePreviewIndex(newPos ~/ 1000); plPlayerController.updatePreviewIndex(newPos ~/ 1000);
} }
} else if (_gestureType == GestureType.left) { } else if (_gestureType == .left) {
// 左边区域 👈 // 左边区域 👈
final double level = maxHeight * 3; final double level = maxHeight * 3;
final double brightness = (_brightnessValue.value - delta.dy / level) final double brightness = (_brightnessValue.value - delta.dy / level)
.clamp(0.0, 1.0); .clamp(0.0, 1.0);
setBrightness(brightness); setBrightness(brightness);
} else if (_gestureType == GestureType.center) { } else if (_gestureType == .center) {
// 全屏 // 全屏
const double threshold = 2.5; // 滑动阈值 const double threshold = 2.5; // 滑动阈值
double cumulativeDy = details.localFocalPoint.dy - initialFocalPoint.dy; double cumulativeDy = details.localFocalPoint.dy - _initialFocalPoint!.dy;
if (cumulativeDy > threshold) { if (cumulativeDy > threshold) {
_gestureType = GestureType.center_down; _gestureType = .center_down;
if (isFullScreen) { plPlayerController.triggerFullScreen(status: false);
plPlayerController.triggerFullScreen(status: false);
}
// if (kDebugMode) debugPrint('center_down:$cumulativeDy');
} else if (cumulativeDy < -threshold) { } else if (cumulativeDy < -threshold) {
_gestureType = GestureType.center_up; _gestureType = .center_up;
if (!isFullScreen) { plPlayerController.triggerFullScreen(status: true);
plPlayerController.triggerFullScreen(status: true);
}
// if (kDebugMode) debugPrint('center_up:$cumulativeDy');
} }
} else if (_gestureType == GestureType.right) { } else if (_gestureType == .right) {
// 右边区域 // 右边区域
final double level = maxHeight * 0.5; final double level = maxHeight * 0.5;
EasyThrottle.throttle( EasyThrottle.throttle(
@@ -976,7 +954,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
} }
} }
void _onInteractionEnd(ScaleEndDetails details) { void _onPanEnd(ScaleEndDetails details) {
if (Platform.isAndroid && _gestureType == .left) { if (Platform.isAndroid && _gestureType == .left) {
ScreenBrightnessPlatform.instance.restoreBrightnessMode(); ScreenBrightnessPlatform.instance.restoreBrightnessMode();
} }
@@ -994,15 +972,11 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
} }
plPlayerController.onChangedSliderEnd(); plPlayerController.onChangedSliderEnd();
} }
interacting = false; _initialFocalPoint = null;
initialFocalPoint = Offset.zero;
_gestureType = null; _gestureType = null;
} }
void onDoubleTapDownMobile(TapDownDetails details) { void onDoubleTapDownMobile(TapDownDetails details) {
if (plPlayerController.isLive || plPlayerController.controlsLock.value) {
return;
}
final double tapPosition = details.localPosition.dx; final double tapPosition = details.localPosition.dx;
final double sectionWidth = maxWidth / 4; final double sectionWidth = maxWidth / 4;
DoubleTapType type; DoubleTapType type;
@@ -1016,39 +990,21 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
plPlayerController.doubleTapFuc(type); plPlayerController.doubleTapFuc(type);
} }
void onTapDesktop() {
if (plPlayerController.isLive || plPlayerController.controlsLock.value) {
return;
}
plPlayerController.onDoubleTapCenter();
}
void onDoubleTapDesktop() {
if (plPlayerController.controlsLock.value) {
return;
}
plPlayerController.triggerFullScreen(status: !isFullScreen);
}
void _onTapUp(TapUpDetails details) { void _onTapUp(TapUpDetails details) {
switch (details.kind) { switch (details.kind) {
case ui.PointerDeviceKind.mouse when PlatformUtils.isDesktop: case ui.PointerDeviceKind.mouse when PlatformUtils.isDesktop:
onTapDesktop(); plPlayerController.onDoubleTapCenter();
break;
default: default:
plPlayerController.controls = !plPlayerController.showControls.value; plPlayerController.controls = !plPlayerController.showControls.value;
break;
} }
} }
void _onDoubleTapDown(TapDownDetails details) { void _onDoubleTapDown(TapDownDetails details) {
switch (details.kind) { switch (details.kind) {
case ui.PointerDeviceKind.mouse when PlatformUtils.isDesktop: case ui.PointerDeviceKind.mouse when PlatformUtils.isDesktop:
onDoubleTapDesktop(); plPlayerController.triggerFullScreen(status: !isFullScreen);
break;
default: default:
onDoubleTapDownMobile(details); onDoubleTapDownMobile(details);
break;
} }
} }
@@ -1062,7 +1018,18 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
plPlayerController.setLongPressStatus(false)); plPlayerController.setLongPressStatus(false));
late final TapGestureRecognizer _tapGestureRecognizer; late final TapGestureRecognizer _tapGestureRecognizer;
late final DoubleTapGestureRecognizer _doubleTapGestureRecognizer; late final DoubleTapGestureRecognizer _doubleTapGestureRecognizer;
StreamSubscription<bool>? _danmakuListener; late final ScaleGestureRecognizer _scaleGestureRecognizer;
static const _kOffsetThreshold = 40.0;
bool _isPositionAllowed(Offset offset) {
if (offset.dx < _kOffsetThreshold ||
offset.dy < _kOffsetThreshold ||
offset.dx > maxWidth - _kOffsetThreshold ||
offset.dy > maxHeight - _kOffsetThreshold) {
return false;
}
return true;
}
void _onPointerDown(PointerDownEvent event) { void _onPointerDown(PointerDownEvent event) {
if (PlatformUtils.isDesktop) { if (PlatformUtils.isDesktop) {
@@ -1075,20 +1042,37 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
..controlsLock.value = false ..controlsLock.value = false
..showControls.value = false; ..showControls.value = false;
} }
plPlayerController plPlayerController.triggerFullScreen(
.triggerFullScreen( status: !isFullScreen,
status: !isFullScreen, inAppFullScreen: isSecondaryBtn,
inAppFullScreen: isSecondaryBtn, );
)
.whenComplete(() => initialFocalPoint = Offset.zero);
return; return;
} }
} }
_tapGestureRecognizer.addPointer(event); if (_isPositionAllowed(event.localPosition)) {
_doubleTapGestureRecognizer.addPointer(event); final controlsUnlock = !plPlayerController.controlsLock.value;
if (!plPlayerController.isLive) { if (PlatformUtils.isMobile) {
longPressRecognizer.addPointer(event); _tapGestureRecognizer.addPointer(event);
if (controlsUnlock) {
if (!plPlayerController.isLive) {
_doubleTapGestureRecognizer.addPointer(event);
longPressRecognizer.addPointer(event);
}
_scaleGestureRecognizer.addPointer(event);
}
} else {
if (controlsUnlock) {
if (plPlayerController.isLive) {
_doubleTapGestureRecognizer.addPointer(event);
} else {
_tapGestureRecognizer.addPointer(event);
_doubleTapGestureRecognizer.addPointer(event);
longPressRecognizer.addPointer(event);
}
_scaleGestureRecognizer.addPointer(event);
}
}
} }
} }
@@ -1100,14 +1084,14 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
final dx = pan.dx.abs(); final dx = pan.dx.abs();
final dy = pan.dy.abs(); final dy = pan.dy.abs();
if (dx > 3 * dy) { if (dx > 3 * dy) {
_gestureType = GestureType.horizontal; _gestureType = .horizontal;
} else if (dy > 3 * dx) { } else if (dy > 3 * dx) {
_gestureType = GestureType.right; _gestureType = .right;
} }
return; return;
} }
if (_gestureType == GestureType.horizontal) { if (_gestureType == .horizontal) {
if (plPlayerController.isLive) return; if (plPlayerController.isLive) return;
Offset delta = event.localPanDelta; Offset delta = event.localPanDelta;
@@ -1131,7 +1115,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
plPlayerController.cancelSeek != true) { plPlayerController.cancelSeek != true) {
plPlayerController.updatePreviewIndex(newPos ~/ 1000); plPlayerController.updatePreviewIndex(newPos ~/ 1000);
} }
} else if (_gestureType == GestureType.right) { } else if (_gestureType == .right) {
final double level = maxHeight * 0.5; final double level = maxHeight * 0.5;
EasyThrottle.throttle( EasyThrottle.throttle(
'setVolume', 'setVolume',
@@ -1149,6 +1133,12 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
} }
void _onPointerPanZoomEnd(PointerPanZoomEndEvent event) { void _onPointerPanZoomEnd(PointerPanZoomEndEvent event) {
plPlayerController.showPreview.value = false;
if (plPlayerController.isSliderMoving.value) {
plPlayerController
..seekTo(plPlayerController.sliderPosition, isSeek: false)
..onChangedSliderEnd();
}
_gestureType = null; _gestureType = null;
} }
@@ -1413,7 +1403,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
children: [ children: [
AppBarAni( AppBarAni(
isTop: true, isTop: true,
controller: animationController, controller: _animationController,
isFullScreen: isFullScreen, isFullScreen: isFullScreen,
child: plPlayerController.isDesktopPip child: plPlayerController.isDesktopPip
? GestureDetector( ? GestureDetector(
@@ -1425,7 +1415,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
), ),
AppBarAni( AppBarAni(
isTop: false, isTop: false,
controller: animationController, controller: _animationController,
isFullScreen: isFullScreen, isFullScreen: isFullScreen,
child: child:
widget.bottomControl ?? widget.bottomControl ??
@@ -1489,12 +1479,12 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
); );
final anim = animController.drive( final anim = animController.drive(
Matrix4Tween( Matrix4Tween(
begin: transformationController.value, begin: _transformationController.value,
end: Matrix4.identity(), end: Matrix4.identity(),
).chain(CurveTween(curve: Curves.easeOut)), ).chain(CurveTween(curve: Curves.easeOut)),
); );
void listener() { void listener() {
transformationController.value = anim.value; _transformationController.value = anim.value;
} }
animController.addListener(listener); animController.addListener(listener);
@@ -1792,7 +1782,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
Widget get _videoWidget { Widget get _videoWidget {
return Container( return Container(
clipBehavior: Clip.none, clipBehavior: .none,
width: maxWidth, width: maxWidth,
height: maxHeight, height: maxHeight,
color: widget.fill, color: widget.fill,
@@ -1803,21 +1793,16 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
onPointerPanZoomUpdate: _onPointerPanZoomUpdate, onPointerPanZoomUpdate: _onPointerPanZoomUpdate,
onPointerPanZoomEnd: _onPointerPanZoomEnd, onPointerPanZoomEnd: _onPointerPanZoomEnd,
onPointerDown: _onPointerDown, onPointerDown: _onPointerDown,
onInteractionStart: _onInteractionStart, onPanStart: _onPanStart,
onInteractionUpdate: _onInteractionUpdate, onPanUpdate: _onPanUpdate,
onInteractionEnd: _onInteractionEnd, onPanEnd: _onPanEnd,
onScaleUpdate: _onScaleUpdate,
scaleGestureRecognizer: _scaleGestureRecognizer,
panEnabled: false, panEnabled: false,
minScale: 1.0, minScale: 1.0,
maxScale: 2.0, maxScale: 2.0,
panAxis: PanAxis.aligned, panAxis: .aligned,
transformationController: transformationController, transformationController: _transformationController,
onTranslate: () {
final storage = transformationController.value.storage;
showRestoreScaleBtn.value =
storage[12].abs() > 2.0 ||
storage[13].abs() > 2.0 ||
storage[0] != 1.0;
},
childKey: _videoKey, childKey: _videoKey,
child: RepaintBoundary( child: RepaintBoundary(
key: _videoKey, key: _videoKey,