diff --git a/lib/pages/live_room/widgets/bottom_control.dart b/lib/pages/live_room/widgets/bottom_control.dart index 60bdf0736..9158d1200 100644 --- a/lib/pages/live_room/widgets/bottom_control.dart +++ b/lib/pages/live_room/widgets/bottom_control.dart @@ -1,5 +1,6 @@ import 'package:PiliPlus/pages/live_room/controller.dart'; import 'package:PiliPlus/plugin/pl_player/controller.dart'; +import 'package:PiliPlus/plugin/pl_player/models/video_fit_type.dart'; import 'package:PiliPlus/plugin/pl_player/widgets/common_btn.dart'; import 'package:PiliPlus/plugin/pl_player/widgets/play_pause_btn.dart'; import 'package:PiliPlus/utils/storage.dart'; @@ -97,13 +98,13 @@ class BottomControl extends StatelessWidget { }, ), Obx( - () => PopupMenuButton( + () => PopupMenuButton( initialValue: plPlayerController.videoFit.value, color: Colors.black.withValues(alpha: 0.8), itemBuilder: (context) { - return BoxFit.values + return VideoFitType.values .map( - (BoxFit boxFit) => PopupMenuItem( + (boxFit) => PopupMenuItem( height: 35, padding: const EdgeInsets.only(left: 30), value: boxFit, diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index ae16b2774..ce5f8b4b8 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -25,6 +25,7 @@ import 'package:PiliPlus/plugin/pl_player/models/fullscreen_mode.dart'; import 'package:PiliPlus/plugin/pl_player/models/heart_beat_type.dart'; import 'package:PiliPlus/plugin/pl_player/models/play_repeat.dart'; import 'package:PiliPlus/plugin/pl_player/models/play_status.dart'; +import 'package:PiliPlus/plugin/pl_player/models/video_fit_type.dart'; import 'package:PiliPlus/plugin/pl_player/utils/fullscreen.dart'; import 'package:PiliPlus/services/service_locator.dart'; import 'package:PiliPlus/utils/accounts.dart'; @@ -104,7 +105,7 @@ class PlPlayerController { bool _isVertical = false; - final Rx _videoFit = Rx(BoxFit.contain); + final Rx _videoFit = Rx(VideoFitType.contain); late StreamSubscription _dataListenerForVideoFit; late StreamSubscription _dataListenerForEnterFullscreen; @@ -220,7 +221,7 @@ class PlPlayerController { bool get autoplay => _autoPlay; /// 视频比例 - Rx get videoFit => _videoFit; + Rx get videoFit => _videoFit; /// 后台播放 RxBool get continuePlayInBackground => _continuePlayInBackground; @@ -370,24 +371,21 @@ class PlPlayerController { : Colors.black.withValues(alpha: subtitleBgOpaticy), ); - SubtitleViewConfiguration get subtitleViewConfiguration => - SubtitleViewConfiguration( - style: subTitleStyle, - padding: EdgeInsets.only( - left: subtitlePaddingH.toDouble(), - right: subtitlePaddingH.toDouble(), - bottom: subtitlePaddingB.toDouble(), - ), - textScaleFactor: 1, - strokeWidth: subtitleBgOpaticy == 0 ? subtitleStrokeWidth : null, - ); + late final Rx subtitleConfig = _getSubConfig.obs; - GlobalKey Function()? getPlayerKey; + SubtitleViewConfiguration get _getSubConfig => SubtitleViewConfiguration( + style: subTitleStyle, + padding: EdgeInsets.only( + left: subtitlePaddingH.toDouble(), + right: subtitlePaddingH.toDouble(), + bottom: subtitlePaddingB.toDouble(), + ), + textScaleFactor: 1, + strokeWidth: subtitleBgOpaticy == 0 ? subtitleStrokeWidth : null, + ); void updateSubtitleStyle() { - getPlayerKey?.call().currentState?.update( - subtitleViewConfiguration: subtitleViewConfiguration, - ); + subtitleConfig.value = _getSubConfig; } void onUpdatePadding(EdgeInsets padding) { @@ -1271,42 +1269,32 @@ class PlPlayerController { } /// Toggle Change the videofit accordingly - void toggleVideoFit(BoxFit value) { + void toggleVideoFit(VideoFitType value) { _videoFit.value = value; - setVideoFit(); - getPlayerKey?.call().currentState?.update(fit: value); - } - - /// 缓存fit - Future setVideoFit() async { - SmartDialog.showToast( - _videoFit.value.toast, - displayTime: const Duration(seconds: 1), - ); video.put(VideoBoxKey.cacheVideoFit, _videoFit.value.index); } /// 读取fit int fitValue = Pref.cacheVideoFit; Future getVideoFit() async { - var attr = BoxFit.values[fitValue]; + var attr = VideoFitType.values[fitValue]; // 由于none与scaleDown涉及视频原始尺寸,需要等待视频加载后再设置,否则尺寸会变为0,出现错误; - if (attr == BoxFit.none || attr == BoxFit.scaleDown) { + if (attr == VideoFitType.none || attr == VideoFitType.scaleDown) { if (buffered.value == Duration.zero) { - attr = BoxFit.contain; + attr = VideoFitType.contain; _dataListenerForVideoFit = dataStatus.status.listen((status) { if (status == DataStatus.loaded) { _dataListenerForVideoFit.cancel(); - var attr = BoxFit.values[fitValue]; - if (attr == BoxFit.none || attr == BoxFit.scaleDown) { + var attr = VideoFitType.values[fitValue]; + if (attr == VideoFitType.none || attr == VideoFitType.scaleDown) { _videoFit.value = attr; } } }); } // fill不应该在竖屏视频生效 - } else if (attr == BoxFit.fill && isVertical) { - attr = BoxFit.contain; + } else if (attr == VideoFitType.fill && isVertical) { + attr = VideoFitType.contain; } _videoFit.value = attr; } @@ -1676,17 +1664,3 @@ class PlPlayerController { late final RxList dmTrend = [].obs; late final RxBool showDmTreandChart = true.obs; } - -extension BoxFitExt on BoxFit { - String get desc => const ['拉伸', '自动', '裁剪', '等宽', '等高', '原始', '限制'][index]; - - String get toast => const [ - '拉伸至播放器尺寸,将产生变形(竖屏改为自动)', - '缩放至播放器尺寸,保留黑边', - '缩放至填满播放器,裁剪超出部分', - '缩放至撑满播放器宽度', - '缩放至撑满播放器高度', - '不缩放,以视频原始尺寸显示', - '仅超出时缩小至播放器尺寸', - ][index]; -} diff --git a/lib/plugin/pl_player/models/video_fit_type.dart b/lib/plugin/pl_player/models/video_fit_type.dart new file mode 100644 index 000000000..768731944 --- /dev/null +++ b/lib/plugin/pl_player/models/video_fit_type.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart' show BoxFit; + +enum VideoFitType { + fill('拉伸', boxFit: BoxFit.fill), + contain('自动', boxFit: BoxFit.contain), + cover('裁剪', boxFit: BoxFit.cover), + fitWidth('等宽', boxFit: BoxFit.fitWidth), + fitHeight('等高', boxFit: BoxFit.fitHeight), + none('原始', boxFit: BoxFit.none), + scaleDown('限制', boxFit: BoxFit.scaleDown), + ratio_4x3('4:3', aspectRatio: 4 / 3), + ratio_16x9('16:9', aspectRatio: 16 / 9); + + final String desc; + final BoxFit boxFit; + final double? aspectRatio; + const VideoFitType( + this.desc, { + this.boxFit = BoxFit.contain, + this.aspectRatio, + }); +} diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 860dbe5f2..707a262fd 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -21,6 +21,7 @@ import 'package:PiliPlus/plugin/pl_player/models/double_tap_type.dart'; import 'package:PiliPlus/plugin/pl_player/models/duration.dart'; import 'package:PiliPlus/plugin/pl_player/models/fullscreen_mode.dart'; import 'package:PiliPlus/plugin/pl_player/models/gesture_type.dart'; +import 'package:PiliPlus/plugin/pl_player/models/video_fit_type.dart'; import 'package:PiliPlus/plugin/pl_player/widgets/app_bar_ani.dart'; import 'package:PiliPlus/plugin/pl_player/widgets/backward_seek.dart'; import 'package:PiliPlus/plugin/pl_player/widgets/bottom_control.dart'; @@ -167,7 +168,6 @@ class _PLVideoPlayerState extends State @override void initState() { super.initState(); - plPlayerController.getPlayerKey = () => key; _controlsListener = plPlayerController.showControls.listen((bool val) { final visible = val && !plPlayerController.controlsLock.value; visible ? animationController.forward() : animationController.reverse(); @@ -379,6 +379,7 @@ class _PLVideoPlayerState extends State /// 超分辨率 BottomControlType.superResolution => Obx( () => PopupMenuButton( + requestFocus: false, initialValue: plPlayerController.superResolutionType.value, color: Colors.black.withValues(alpha: 0.8), itemBuilder: (context) { @@ -485,13 +486,14 @@ class _PLVideoPlayerState extends State /// 画面比例 BottomControlType.fit => Obx( - () => PopupMenuButton( + () => PopupMenuButton( + requestFocus: false, initialValue: plPlayerController.videoFit.value, color: Colors.black.withValues(alpha: 0.8), itemBuilder: (context) { - return BoxFit.values + return VideoFitType.values .map( - (BoxFit boxFit) => PopupMenuItem( + (boxFit) => PopupMenuItem( height: 35, padding: const EdgeInsets.only(left: 30), value: boxFit, @@ -519,6 +521,7 @@ class _PLVideoPlayerState extends State () => widget.videoDetailController?.subtitles.isEmpty == true ? const SizedBox.shrink() : PopupMenuButton( + requestFocus: false, initialValue: widget .videoDetailController! .vttSubtitlesIndex @@ -570,6 +573,7 @@ class _PLVideoPlayerState extends State /// 播放速度 BottomControlType.speed => Obx( () => PopupMenuButton( + requestFocus: false, initialValue: plPlayerController.playbackSpeed, color: Colors.black.withValues(alpha: 0.8), itemBuilder: (context) { @@ -670,383 +674,387 @@ class _PLVideoPlayerState extends State late final transformationController = TransformationController(); + late ThemeData theme; + late double maxWidth; + late double maxHeight; + + void _onInteractionStart(ScaleStartDetails details) { + if (plPlayerController.controlsLock.value) return; + // 如果起点太靠上则屏蔽 + if (details.localFocalPoint.dy < 40) return; + if (details.localFocalPoint.dx < 40) return; + if (details.localFocalPoint.dx > maxWidth - 40) return; + if (details.localFocalPoint.dy > maxHeight - 40) return; + if (details.pointerCount == 2) { + interacting = true; + } + _initialFocalPoint = details.localFocalPoint; + // if (kDebugMode) { + // debugPrint("_initialFocalPoint$_initialFocalPoint"); + // } + _gestureType = null; + } + + void _onInteractionUpdate(ScaleUpdateDetails details) { + showRestoreScaleBtn.value = transformationController.value.row0.x != 1.0; + if (interacting || _initialFocalPoint == Offset.zero) return; + Offset cumulativeDelta = details.localFocalPoint - _initialFocalPoint; + if (details.pointerCount == 2 && cumulativeDelta.distance < 1.5) { + interacting = true; + _gestureType = null; + return; + } + + /// 锁定时禁用 + if (plPlayerController.controlsLock.value) return; + + if (_gestureType == null) { + if (cumulativeDelta.distance < 1) return; + if (cumulativeDelta.dx.abs() > 3 * cumulativeDelta.dy.abs()) { + _gestureType = GestureType.horizontal; + } else if (cumulativeDelta.dy.abs() > 3 * cumulativeDelta.dx.abs()) { + if (!plPlayerController.enableSlideVolumeBrightness && + !plPlayerController.enableSlideFS) { + return; + } + + // _gestureType = 'vertical'; + + final double tapPosition = details.localFocalPoint.dx; + final double sectionWidth = maxWidth / 3; + if (tapPosition < sectionWidth) { + if (!plPlayerController.enableSlideVolumeBrightness) { + return; + } + // 左边区域 + _gestureType = GestureType.left; + } else if (tapPosition < sectionWidth * 2) { + if (!plPlayerController.enableSlideFS) { + return; + } + // 全屏 + _gestureType = GestureType.center; + } else { + if (!plPlayerController.enableSlideVolumeBrightness) { + return; + } + // 右边区域 + _gestureType = GestureType.right; + } + } else { + return; + } + } + + Offset delta = details.focalPointDelta; + + if (_gestureType == GestureType.horizontal) { + // live模式下禁用 + if (plPlayerController.isLive) return; + + final int curSliderPosition = + plPlayerController.sliderPosition.value.inMilliseconds; + final int newPos = + (curSliderPosition + + (plPlayerController.sliderScale * delta.dx / maxWidth) + .round()) + .clamp( + 0, + plPlayerController.duration.value.inMilliseconds, + ); + final Duration result = Duration(milliseconds: newPos); + final height = maxHeight * 0.125; + if (details.localFocalPoint.dy <= height && + (details.localFocalPoint.dx >= maxWidth * 0.875 || + details.localFocalPoint.dx <= maxWidth * 0.125)) { + plPlayerController.cancelSeek = true; + plPlayerController.showPreview.value = false; + if (plPlayerController.hasToast != true) { + plPlayerController.hasToast = true; + SmartDialog.showAttach( + targetContext: context, + alignment: Alignment.center, + animationTime: const Duration(milliseconds: 200), + animationType: SmartAnimationType.fade, + displayTime: const Duration(milliseconds: 1500), + maskColor: Colors.transparent, + builder: (context) => Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 4, + ), + decoration: BoxDecoration( + borderRadius: const BorderRadius.all( + Radius.circular(6), + ), + color: theme.colorScheme.secondaryContainer, + ), + child: Text( + '松开手指,取消进退', + style: TextStyle( + color: theme.colorScheme.onSecondaryContainer, + ), + ), + ), + ); + } + } else { + if (plPlayerController.cancelSeek == true) { + plPlayerController + ..cancelSeek = null + ..hasToast = null; + } + } + plPlayerController + ..onUpdatedSliderProgress(result) + ..onChangedSliderStart(); + if (plPlayerController.showSeekPreview && + plPlayerController.cancelSeek != true) { + plPlayerController.updatePreviewIndex(newPos ~/ 1000); + } + } else if (_gestureType == GestureType.left) { + // 左边区域 👈 + final double level = maxHeight * 3; + final double brightness = _brightnessValue.value - delta.dy / level; + final double result = brightness.clamp(0.0, 1.0); + setBrightness(result); + } else if (_gestureType == GestureType.center) { + // 全屏 + const double threshold = 2.5; // 滑动阈值 + double cumulativeDy = details.localFocalPoint.dy - _initialFocalPoint.dy; + + void fullScreenTrigger(bool status) { + plPlayerController.triggerFullScreen(status: status); + } + + if (cumulativeDy > threshold) { + _gestureType = GestureType.center_down; + if (isFullScreen ^ plPlayerController.fullScreenGestureReverse) { + fullScreenTrigger( + plPlayerController.fullScreenGestureReverse, + ); + } + // if (kDebugMode) debugPrint('center_down:$cumulativeDy'); + } else if (cumulativeDy < -threshold) { + _gestureType = GestureType.center_up; + if (!isFullScreen ^ plPlayerController.fullScreenGestureReverse) { + fullScreenTrigger( + !plPlayerController.fullScreenGestureReverse, + ); + } + // if (kDebugMode) debugPrint('center_up:$cumulativeDy'); + } + } else if (_gestureType == GestureType.right) { + // 右边区域 + final double level = maxHeight * 0.5; + EasyThrottle.throttle( + 'setVolume', + const Duration(milliseconds: 20), + () { + final double volume = _volumeValue.value - delta.dy / level; + final double result = volume.clamp(0.0, 1.0); + setVolume(result); + }, + ); + } + } + + void _onInteractionEnd(ScaleEndDetails details) { + if (plPlayerController.showSeekPreview) { + plPlayerController.showPreview.value = false; + } + if (plPlayerController.isSliderMoving.value) { + if (plPlayerController.cancelSeek == true) { + plPlayerController.onUpdatedSliderProgress( + plPlayerController.position.value, + ); + } else { + plPlayerController.seekTo( + plPlayerController.sliderPosition.value, + isSeek: false, + ); + } + plPlayerController.onChangedSliderEnd(); + } + interacting = false; + _initialFocalPoint = Offset.zero; + _gestureType = null; + } + + void onVerticalDragStart(DragStartDetails details) { + if (plPlayerController.controlsLock.value) return; + if (details.localPosition.dy < 40) return; + if (details.localPosition.dx < 40) return; + if (details.localPosition.dx > maxWidth - 40) return; + if (details.localPosition.dy > maxHeight - 40) return; + _initialFocalPoint = details.localPosition; + _gestureType = null; + } + + void onVerticalDragUpdate(DragUpdateDetails details) { + if (plPlayerController.controlsLock.value) return; + if (!plPlayerController.enableSlideVolumeBrightness && + !plPlayerController.enableSlideFS) { + return; + } + final double tapPosition = details.localPosition.dx; + final double sectionWidth = maxWidth / 3; + late GestureType gestureType; + if (tapPosition < sectionWidth) { + if (!plPlayerController.enableSlideVolumeBrightness) { + return; + } + // 左边区域 + gestureType = GestureType.left; + } else if (tapPosition < sectionWidth * 2) { + if (!plPlayerController.enableSlideFS) { + return; + } + // 全屏 + gestureType = GestureType.center; + } else { + if (!plPlayerController.enableSlideVolumeBrightness) { + return; + } + // 右边区域 + gestureType = GestureType.right; + } + + if (_gestureType != null && _gestureType != gestureType) { + return; + } + _gestureType = gestureType; + + if (_gestureType == GestureType.left) { + // 左边区域 👈 + final double level = maxHeight * 3; + final double brightness = + _brightnessValue.value - details.delta.dy / level; + final double result = brightness.clamp(0.0, 1.0); + setBrightness(result); + } else if (_gestureType == GestureType.center) { + // 全屏 + const double threshold = 2.5; // 滑动阈值 + double cumulativeDy = details.localPosition.dy - _initialFocalPoint.dy; + + void fullScreenTrigger(bool status) { + plPlayerController.triggerFullScreen(status: status); + } + + if (cumulativeDy > threshold) { + _gestureType = GestureType.center_down; + if (isFullScreen ^ plPlayerController.fullScreenGestureReverse) { + fullScreenTrigger( + plPlayerController.fullScreenGestureReverse, + ); + } + // if (kDebugMode) debugPrint('center_down:$cumulativeDy'); + } else if (cumulativeDy < -threshold) { + _gestureType = GestureType.center_up; + if (!isFullScreen ^ plPlayerController.fullScreenGestureReverse) { + fullScreenTrigger( + !plPlayerController.fullScreenGestureReverse, + ); + } + // if (kDebugMode) debugPrint('center_up:$cumulativeDy'); + } + } else if (_gestureType == GestureType.right) { + // 右边区域 + final double level = maxHeight * 0.5; + EasyThrottle.throttle( + 'setVolume', + const Duration(milliseconds: 20), + () { + final double volume = _volumeValue.value - details.delta.dy / level; + final double result = volume.clamp(0.0, 1.0); + setVolume(result); + }, + ); + } + } + + void onVerticalDragEnd(DragEndDetails details) { + interacting = false; + _initialFocalPoint = Offset.zero; + _gestureType = null; + } + + void onDoubleTapDown(TapDownDetails details) { + if (plPlayerController.controlsLock.value) { + return; + } + if (plPlayerController.isLive) { + doubleTapFuc(DoubleTapType.center); + return; + } + final double tapPosition = details.localPosition.dx; + final double sectionWidth = maxWidth / 4; + DoubleTapType type; + if (tapPosition < sectionWidth) { + type = DoubleTapType.left; + } else if (tapPosition < sectionWidth * 3) { + type = DoubleTapType.center; + } else { + type = DoubleTapType.right; + } + doubleTapFuc(type); + } + @override Widget build(BuildContext context) { - final ThemeData theme = Theme.of(context); + theme = Theme.of(context); + maxWidth = widget.maxWidth; + maxHeight = widget.maxHeight; final Color primary = theme.colorScheme.primary; const TextStyle textStyle = TextStyle( color: Colors.white, fontSize: 12, ); - final maxWidth = widget.maxWidth; - final maxHeight = widget.maxHeight; - return Stack( fit: StackFit.passthrough, key: _playerKey, children: [ Obx( - () => Video( - fill: widget.fill ?? Colors.black, - key: key, - alignment: widget.alignment ?? Alignment.center, - controller: videoController, - controls: NoVideoControls, - pauseUponEnteringBackgroundMode: - !plPlayerController.continuePlayInBackground.value, - resumeUponEnteringForegroundMode: true, - // 字幕尺寸调节 - subtitleViewConfiguration: - plPlayerController.subtitleViewConfiguration, - fit: plPlayerController.videoFit.value, - dmWidget: widget.danmuWidget, - transformationController: transformationController, - scaleEnabled: !plPlayerController.controlsLock.value, - enableShrinkVideoSize: plPlayerController.enableShrinkVideoSize, - onInteractionStart: (ScaleStartDetails details) { - if (plPlayerController.controlsLock.value) return; - // 如果起点太靠上则屏蔽 - if (details.localFocalPoint.dy < 40) return; - if (details.localFocalPoint.dx < 40) return; - if (details.localFocalPoint.dx > maxWidth - 40) return; - if (details.localFocalPoint.dy > maxHeight - 40) return; - if (details.pointerCount == 2) { - interacting = true; - } - _initialFocalPoint = details.localFocalPoint; - // if (kDebugMode) { - // debugPrint("_initialFocalPoint$_initialFocalPoint"); - // } - _gestureType = null; - }, - onInteractionUpdate: (ScaleUpdateDetails details) { - showRestoreScaleBtn.value = - transformationController.value.row0.x != 1.0; - if (interacting || _initialFocalPoint == Offset.zero) return; - Offset cumulativeDelta = - details.localFocalPoint - _initialFocalPoint; - if (details.pointerCount == 2 && cumulativeDelta.distance < 1.5) { - interacting = true; - _gestureType = null; - return; - } - - /// 锁定时禁用 - if (plPlayerController.controlsLock.value) return; - - if (_gestureType == null) { - if (cumulativeDelta.distance < 1) return; - if (cumulativeDelta.dx.abs() > 3 * cumulativeDelta.dy.abs()) { - _gestureType = GestureType.horizontal; - } else if (cumulativeDelta.dy.abs() > - 3 * cumulativeDelta.dx.abs()) { - if (!plPlayerController.enableSlideVolumeBrightness && - !plPlayerController.enableSlideFS) { - return; - } - - // _gestureType = 'vertical'; - - final double tapPosition = details.localFocalPoint.dx; - final double sectionWidth = maxWidth / 3; - if (tapPosition < sectionWidth) { - if (!plPlayerController.enableSlideVolumeBrightness) { - return; - } - // 左边区域 - _gestureType = GestureType.left; - } else if (tapPosition < sectionWidth * 2) { - if (!plPlayerController.enableSlideFS) { - return; - } - // 全屏 - _gestureType = GestureType.center; - } else { - if (!plPlayerController.enableSlideVolumeBrightness) { - return; - } - // 右边区域 - _gestureType = GestureType.right; - } - } else { - return; - } - } - - Offset delta = details.focalPointDelta; - - if (_gestureType == GestureType.horizontal) { - // live模式下禁用 - if (plPlayerController.isLive) return; - - final int curSliderPosition = - plPlayerController.sliderPosition.value.inMilliseconds; - final int newPos = - (curSliderPosition + - (plPlayerController.sliderScale * - delta.dx / - maxWidth) - .round()) - .clamp( - 0, - plPlayerController.duration.value.inMilliseconds, - ); - final Duration result = Duration(milliseconds: newPos); - final height = maxHeight * 0.125; - if (details.localFocalPoint.dy <= height && - (details.localFocalPoint.dx >= maxWidth * 0.875 || - details.localFocalPoint.dx <= maxWidth * 0.125)) { - plPlayerController.cancelSeek = true; - plPlayerController.showPreview.value = false; - if (plPlayerController.hasToast != true) { - plPlayerController.hasToast = true; - SmartDialog.showAttach( - targetContext: context, - alignment: Alignment.center, - animationTime: const Duration(milliseconds: 200), - animationType: SmartAnimationType.fade, - displayTime: const Duration(milliseconds: 1500), - maskColor: Colors.transparent, - builder: (context) => Container( - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 4, - ), - decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(6), - ), - color: theme.colorScheme.secondaryContainer, - ), - child: Text( - '松开手指,取消进退', - style: TextStyle( - color: theme.colorScheme.onSecondaryContainer, - ), - ), - ), - ); - } - } else { - if (plPlayerController.cancelSeek == true) { - plPlayerController - ..cancelSeek = null - ..hasToast = null; - } - } - plPlayerController - ..onUpdatedSliderProgress(result) - ..onChangedSliderStart(); - if (plPlayerController.showSeekPreview && - plPlayerController.cancelSeek != true) { - plPlayerController.updatePreviewIndex(newPos ~/ 1000); - } - } else if (_gestureType == GestureType.left) { - // 左边区域 👈 - final double level = maxHeight * 3; - final double brightness = - _brightnessValue.value - delta.dy / level; - final double result = brightness.clamp(0.0, 1.0); - setBrightness(result); - } else if (_gestureType == GestureType.center) { - // 全屏 - const double threshold = 2.5; // 滑动阈值 - double cumulativeDy = - details.localFocalPoint.dy - _initialFocalPoint.dy; - - void fullScreenTrigger(bool status) { - plPlayerController.triggerFullScreen(status: status); - } - - if (cumulativeDy > threshold) { - _gestureType = GestureType.center_down; - if (isFullScreen ^ - plPlayerController.fullScreenGestureReverse) { - fullScreenTrigger( - plPlayerController.fullScreenGestureReverse, - ); - } - // if (kDebugMode) debugPrint('center_down:$cumulativeDy'); - } else if (cumulativeDy < -threshold) { - _gestureType = GestureType.center_up; - if (!isFullScreen ^ - plPlayerController.fullScreenGestureReverse) { - fullScreenTrigger( - !plPlayerController.fullScreenGestureReverse, - ); - } - // if (kDebugMode) debugPrint('center_up:$cumulativeDy'); - } - } else if (_gestureType == GestureType.right) { - // 右边区域 - final double level = maxHeight * 0.5; - EasyThrottle.throttle( - 'setVolume', - const Duration(milliseconds: 20), - () { - final double volume = _volumeValue.value - delta.dy / level; - final double result = volume.clamp(0.0, 1.0); - setVolume(result); - }, - ); - } - }, - onInteractionEnd: (ScaleEndDetails details) { - if (plPlayerController.showSeekPreview) { - plPlayerController.showPreview.value = false; - } - if (plPlayerController.isSliderMoving.value) { - if (plPlayerController.cancelSeek == true) { - plPlayerController.onUpdatedSliderProgress( - plPlayerController.position.value, - ); - } else { - plPlayerController.seekTo( - plPlayerController.sliderPosition.value, - isSeek: false, - ); - } - plPlayerController.onChangedSliderEnd(); - } - interacting = false; - _initialFocalPoint = Offset.zero; - _gestureType = null; - }, - flipX: plPlayerController.flipX.value, - flipY: plPlayerController.flipY.value, - onVerticalDragStart: (details) { - if (plPlayerController.controlsLock.value) return; - if (details.localPosition.dy < 40) return; - if (details.localPosition.dx < 40) return; - if (details.localPosition.dx > maxWidth - 40) return; - if (details.localPosition.dy > maxHeight - 40) return; - _initialFocalPoint = details.localPosition; - _gestureType = null; - }, - onVerticalDragUpdate: (details) { - if (plPlayerController.controlsLock.value) return; - if (!plPlayerController.enableSlideVolumeBrightness && - !plPlayerController.enableSlideFS) { - return; - } - final double tapPosition = details.localPosition.dx; - final double sectionWidth = maxWidth / 3; - late GestureType gestureType; - if (tapPosition < sectionWidth) { - if (!plPlayerController.enableSlideVolumeBrightness) { - return; - } - // 左边区域 - gestureType = GestureType.left; - } else if (tapPosition < sectionWidth * 2) { - if (!plPlayerController.enableSlideFS) { - return; - } - // 全屏 - gestureType = GestureType.center; - } else { - if (!plPlayerController.enableSlideVolumeBrightness) { - return; - } - // 右边区域 - gestureType = GestureType.right; - } - - if (_gestureType != null && _gestureType != gestureType) { - return; - } - _gestureType = gestureType; - - if (_gestureType == GestureType.left) { - // 左边区域 👈 - final double level = maxHeight * 3; - final double brightness = - _brightnessValue.value - details.delta.dy / level; - final double result = brightness.clamp(0.0, 1.0); - setBrightness(result); - } else if (_gestureType == GestureType.center) { - // 全屏 - const double threshold = 2.5; // 滑动阈值 - double cumulativeDy = - details.localPosition.dy - _initialFocalPoint.dy; - - void fullScreenTrigger(bool status) { - plPlayerController.triggerFullScreen(status: status); - } - - if (cumulativeDy > threshold) { - _gestureType = GestureType.center_down; - if (isFullScreen ^ - plPlayerController.fullScreenGestureReverse) { - fullScreenTrigger( - plPlayerController.fullScreenGestureReverse, - ); - } - // if (kDebugMode) debugPrint('center_down:$cumulativeDy'); - } else if (cumulativeDy < -threshold) { - _gestureType = GestureType.center_up; - if (!isFullScreen ^ - plPlayerController.fullScreenGestureReverse) { - fullScreenTrigger( - !plPlayerController.fullScreenGestureReverse, - ); - } - // if (kDebugMode) debugPrint('center_up:$cumulativeDy'); - } - } else if (_gestureType == GestureType.right) { - // 右边区域 - final double level = maxHeight * 0.5; - EasyThrottle.throttle( - 'setVolume', - const Duration(milliseconds: 20), - () { - final double volume = - _volumeValue.value - details.delta.dy / level; - final double result = volume.clamp(0.0, 1.0); - setVolume(result); - }, - ); - } - }, - onVerticalDragEnd: (details) { - interacting = false; - _initialFocalPoint = Offset.zero; - _gestureType = null; - }, - onTap: () { - plPlayerController.controls = - !plPlayerController.showControls.value; - }, - onDoubleTapDown: (TapDownDetails details) { - if (plPlayerController.controlsLock.value) { - return; - } - if (plPlayerController.isLive) { - doubleTapFuc(DoubleTapType.center); - return; - } - final double tapPosition = details.localPosition.dx; - final double sectionWidth = maxWidth / 4; - DoubleTapType type; - if (tapPosition < sectionWidth) { - type = DoubleTapType.left; - } else if (tapPosition < sectionWidth * 3) { - type = DoubleTapType.center; - } else { - type = DoubleTapType.right; - } - doubleTapFuc(type); - }, - onLongPressStart: (LongPressStartDetails detail) { - plPlayerController.setLongPressStatus(true); - }, - onLongPressEnd: (LongPressEndDetails details) { - plPlayerController.setLongPressStatus(false); - }, - enableDragSubtitle: plPlayerController.enableDragSubtitle, - onUpdatePadding: plPlayerController.onUpdatePadding, - ), + () { + final videoFit = plPlayerController.videoFit.value; + return Video( + fill: widget.fill ?? Colors.black, + key: key, + alignment: widget.alignment ?? Alignment.center, + controller: videoController, + controls: NoVideoControls, + pauseUponEnteringBackgroundMode: + !plPlayerController.continuePlayInBackground.value, + resumeUponEnteringForegroundMode: true, + // 字幕尺寸调节 + subtitleViewConfiguration: + plPlayerController.subtitleConfig.value, + fit: videoFit.boxFit, + aspectRatio: videoFit.aspectRatio, + dmWidget: widget.danmuWidget, + transformationController: transformationController, + scaleEnabled: !plPlayerController.controlsLock.value, + enableShrinkVideoSize: plPlayerController.enableShrinkVideoSize, + onInteractionStart: _onInteractionStart, + onInteractionUpdate: _onInteractionUpdate, + onInteractionEnd: _onInteractionEnd, + flipX: plPlayerController.flipX.value, + flipY: plPlayerController.flipY.value, + onVerticalDragStart: onVerticalDragStart, + onVerticalDragUpdate: onVerticalDragUpdate, + onVerticalDragEnd: onVerticalDragEnd, + onTap: () => plPlayerController.controls = + !plPlayerController.showControls.value, + onDoubleTapDown: onDoubleTapDown, + onLongPressStart: (_) => + plPlayerController.setLongPressStatus(true), + onLongPressEnd: (_) => + plPlayerController.setLongPressStatus(false), + enableDragSubtitle: plPlayerController.enableDragSubtitle, + onUpdatePadding: plPlayerController.onUpdatePadding, + ); + }, ), // /// 弹幕面板 diff --git a/pubspec.lock b/pubspec.lock index e0cfe7382..28d5979d9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1225,7 +1225,7 @@ packages: description: path: media_kit_video ref: "version_1.2.5" - resolved-ref: a74d1af2bcc6b5c88b2216c594e7e3eba0c7cee5 + resolved-ref: "69fbf92def88d4304fc177fa4dd14664c0c2d8d5" url: "https://github.com/bggRGjQaUbCoE/media-kit.git" source: git version: "1.2.5"