mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
feat: 视频新增双指缩放手势,底部控制条新增上下集快捷切换按钮,视频尺寸选项仅在全屏下显示
This commit is contained in:
@@ -315,6 +315,31 @@ class BangumiIntroController extends GetxController {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool prevPlay() {
|
||||||
|
late List episodes;
|
||||||
|
if (bangumiDetail.value.episodes != null) {
|
||||||
|
episodes = bangumiDetail.value.episodes!;
|
||||||
|
}
|
||||||
|
VideoDetailController videoDetailCtr =
|
||||||
|
Get.find<VideoDetailController>(tag: Get.arguments['heroTag']);
|
||||||
|
int currentIndex =
|
||||||
|
episodes.indexWhere((e) => e.cid == videoDetailCtr.cid.value);
|
||||||
|
int prevIndex = currentIndex - 1;
|
||||||
|
PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat;
|
||||||
|
if (prevIndex < 0) {
|
||||||
|
if (platRepeat == PlayRepeat.listCycle) {
|
||||||
|
prevIndex = episodes.length - 1;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int cid = episodes[prevIndex].cid!;
|
||||||
|
String bvid = episodes[prevIndex].bvid!;
|
||||||
|
int aid = episodes[prevIndex].aid!;
|
||||||
|
changeSeasonOrbangu(bvid, cid, aid);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// 列表循环或者顺序播放时,自动播放下一个
|
/// 列表循环或者顺序播放时,自动播放下一个
|
||||||
bool nextPlay() {
|
bool nextPlay() {
|
||||||
late List episodes;
|
late List episodes;
|
||||||
@@ -328,16 +353,13 @@ class BangumiIntroController extends GetxController {
|
|||||||
int nextIndex = currentIndex + 1;
|
int nextIndex = currentIndex + 1;
|
||||||
PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat;
|
PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat;
|
||||||
// 列表循环
|
// 列表循环
|
||||||
if (platRepeat == PlayRepeat.listCycle) {
|
if (nextIndex == episodes.length - 1) {
|
||||||
if (nextIndex == episodes.length - 1) {
|
if (platRepeat == PlayRepeat.listCycle) {
|
||||||
nextIndex = 0;
|
nextIndex = 0;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (nextIndex == episodes.length - 1 &&
|
|
||||||
platRepeat == PlayRepeat.listOrder) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int cid = episodes[nextIndex].cid!;
|
int cid = episodes[nextIndex].cid!;
|
||||||
String bvid = episodes[nextIndex].bvid!;
|
String bvid = episodes[nextIndex].bvid!;
|
||||||
int aid = episodes[nextIndex].aid!;
|
int aid = episodes[nextIndex].aid!;
|
||||||
|
|||||||
@@ -486,6 +486,45 @@ class VideoIntroController extends GetxController {
|
|||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 播放上一个
|
||||||
|
bool prevPlay() {
|
||||||
|
final List episodes = [];
|
||||||
|
bool isPages = false;
|
||||||
|
if (videoDetail.value.ugcSeason != null) {
|
||||||
|
final UgcSeason ugcSeason = videoDetail.value.ugcSeason!;
|
||||||
|
final List<SectionItem> sections = ugcSeason.sections!;
|
||||||
|
for (int i = 0; i < sections.length; i++) {
|
||||||
|
final List<EpisodeItem> episodesList = sections[i].episodes!;
|
||||||
|
episodes.addAll(episodesList);
|
||||||
|
}
|
||||||
|
} else if (videoDetail.value.pages != null) {
|
||||||
|
isPages = true;
|
||||||
|
final List<Part> pages = videoDetail.value.pages!;
|
||||||
|
episodes.addAll(pages);
|
||||||
|
}
|
||||||
|
|
||||||
|
final int currentIndex =
|
||||||
|
episodes.indexWhere((e) => e.cid == lastPlayCid.value);
|
||||||
|
int prevIndex = currentIndex - 1;
|
||||||
|
final VideoDetailController videoDetailCtr =
|
||||||
|
Get.find<VideoDetailController>(tag: heroTag);
|
||||||
|
final PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat;
|
||||||
|
|
||||||
|
// 列表循环
|
||||||
|
if (prevIndex < 0) {
|
||||||
|
if (platRepeat == PlayRepeat.listCycle) {
|
||||||
|
prevIndex = episodes.length - 1;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final int cid = episodes[prevIndex].cid!;
|
||||||
|
final String rBvid = isPages ? bvid : episodes[prevIndex].bvid;
|
||||||
|
final int rAid = isPages ? IdUtils.bv2av(bvid) : episodes[prevIndex].aid!;
|
||||||
|
changeSeasonOrbangu(rBvid, cid, rAid);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// 列表循环或者顺序播放时,自动播放下一个
|
/// 列表循环或者顺序播放时,自动播放下一个
|
||||||
bool nextPlay() {
|
bool nextPlay() {
|
||||||
final List episodes = [];
|
final List episodes = [];
|
||||||
@@ -514,8 +553,7 @@ class VideoIntroController extends GetxController {
|
|||||||
if (nextIndex >= episodes.length) {
|
if (nextIndex >= episodes.length) {
|
||||||
if (platRepeat == PlayRepeat.listCycle) {
|
if (platRepeat == PlayRepeat.listCycle) {
|
||||||
nextIndex = 0;
|
nextIndex = 0;
|
||||||
}
|
} else {
|
||||||
if (platRepeat == PlayRepeat.listOrder) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,9 +89,9 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
late int defaultBtmProgressBehavior;
|
late int defaultBtmProgressBehavior;
|
||||||
late bool enableQuickDouble;
|
late bool enableQuickDouble;
|
||||||
late bool fullScreenGestureReverse;
|
late bool fullScreenGestureReverse;
|
||||||
|
//播放器放缩
|
||||||
|
bool interacting = false;
|
||||||
|
|
||||||
// 用于记录上一次全屏切换手势触发时间,避免误触
|
|
||||||
DateTime? lastFullScreenToggleTime;
|
|
||||||
// 是否在调整固定进度条
|
// 是否在调整固定进度条
|
||||||
RxBool draggingFixedProgressBar = false.obs;
|
RxBool draggingFixedProgressBar = false.obs;
|
||||||
// 阅读器限制
|
// 阅读器限制
|
||||||
@@ -214,16 +214,37 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
// 动态构建底部控制条
|
// 动态构建底部控制条
|
||||||
List<Widget> buildBottomControl() {
|
List<Widget> buildBottomControl() {
|
||||||
final PlPlayerController _ = widget.controller;
|
final PlPlayerController _ = widget.controller;
|
||||||
|
bool isSeason = videoIntroController?.videoDetail.value.ugcSeason != null;
|
||||||
|
bool isPage = videoIntroController?.videoDetail.value.pages != null &&
|
||||||
|
videoIntroController!.videoDetail.value.pages!.length > 1;
|
||||||
|
bool isBangumi = bangumiIntroController?.bangumiDetail.value != null;
|
||||||
|
bool anySeason = isSeason || isPage || isBangumi;
|
||||||
Map<BottomControlType, Widget> videoProgressWidgets = {
|
Map<BottomControlType, Widget> videoProgressWidgets = {
|
||||||
/// 上一集
|
/// 上一集
|
||||||
BottomControlType.pre: ComBtn(
|
BottomControlType.pre: Container(
|
||||||
icon: const Icon(
|
width: 42,
|
||||||
Icons.skip_previous,
|
height: 30,
|
||||||
size: 15,
|
alignment: Alignment.center,
|
||||||
color: Colors.white,
|
child: ComBtn(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.skip_previous,
|
||||||
|
size: 22,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
fuc: () {
|
||||||
|
bool? res;
|
||||||
|
if (videoIntroController != null) {
|
||||||
|
res = videoIntroController!.prevPlay();
|
||||||
|
}
|
||||||
|
if (bangumiIntroController != null) {
|
||||||
|
res = bangumiIntroController!.prevPlay();
|
||||||
|
}
|
||||||
|
if (res == false) {
|
||||||
|
SmartDialog.showToast('已经是第一集了');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: '上一集',
|
||||||
),
|
),
|
||||||
fuc: () {},
|
|
||||||
tooltip: '上一集',
|
|
||||||
),
|
),
|
||||||
|
|
||||||
/// 播放暂停
|
/// 播放暂停
|
||||||
@@ -232,14 +253,30 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
),
|
),
|
||||||
|
|
||||||
/// 下一集
|
/// 下一集
|
||||||
BottomControlType.next: ComBtn(
|
BottomControlType.next: Container(
|
||||||
icon: const Icon(
|
width: 42,
|
||||||
Icons.skip_next,
|
height: 30,
|
||||||
size: 15,
|
alignment: Alignment.center,
|
||||||
color: Colors.white,
|
child: ComBtn(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.skip_next,
|
||||||
|
size: 22,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
fuc: () {
|
||||||
|
bool? res;
|
||||||
|
if (videoIntroController != null) {
|
||||||
|
res = videoIntroController!.nextPlay();
|
||||||
|
}
|
||||||
|
if (bangumiIntroController != null) {
|
||||||
|
res = bangumiIntroController!.nextPlay();
|
||||||
|
}
|
||||||
|
if (res == false) {
|
||||||
|
SmartDialog.showToast('已经是最后一集了');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: '下一集',
|
||||||
),
|
),
|
||||||
fuc: () {},
|
|
||||||
tooltip: '下一集',
|
|
||||||
),
|
),
|
||||||
|
|
||||||
/// 时间进度
|
/// 时间进度
|
||||||
@@ -260,14 +297,6 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
'已播放${Utils.durationReadFormat(Utils.timeFormat(_.positionSeconds.value))}',
|
'已播放${Utils.durationReadFormat(Utils.timeFormat(_.positionSeconds.value))}',
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
// const SizedBox(width: 2),
|
|
||||||
// const ExcludeSemantics(
|
|
||||||
// child: Text(
|
|
||||||
// '/',
|
|
||||||
// style: textStyle,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// const SizedBox(width: 2),
|
|
||||||
Obx(
|
Obx(
|
||||||
() => Text(
|
() => Text(
|
||||||
Utils.timeFormat(_.durationSeconds.value),
|
Utils.timeFormat(_.durationSeconds.value),
|
||||||
@@ -288,65 +317,51 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
BottomControlType.space: const Spacer(),
|
BottomControlType.space: const Spacer(),
|
||||||
|
|
||||||
/// 选集
|
/// 选集
|
||||||
BottomControlType.episode: Obx(() {
|
BottomControlType.episode: Container(
|
||||||
bool isSeason =
|
width: 42,
|
||||||
videoIntroController?.videoDetail.value.ugcSeason != null;
|
height: 30,
|
||||||
bool isPage = videoIntroController?.videoDetail.value.pages != null &&
|
alignment: Alignment.center,
|
||||||
videoIntroController!.videoDetail.value.pages!.length > 1;
|
child: ComBtn(
|
||||||
bool isBangumi = bangumiIntroController?.bangumiDetail.value != null;
|
icon: const Icon(
|
||||||
if (!isSeason && !isPage && !isBangumi) {
|
Icons.list,
|
||||||
return const SizedBox.shrink();
|
size: 22,
|
||||||
}
|
color: Colors.white,
|
||||||
return SizedBox(
|
),
|
||||||
width: 42,
|
tooltip: '选集',
|
||||||
height: 30,
|
fuc: () {
|
||||||
child: Container(
|
int currentCid = widget.controller.cid;
|
||||||
width: 42,
|
String bvid = widget.controller.bvid;
|
||||||
height: 30,
|
final List episodes = [];
|
||||||
alignment: Alignment.center,
|
late Function changeFucCall;
|
||||||
child: ComBtn(
|
if (isSeason) {
|
||||||
icon: const Icon(
|
final List<SectionItem> sections =
|
||||||
Icons.list,
|
videoIntroController!.videoDetail.value.ugcSeason!.sections!;
|
||||||
size: 22,
|
for (int i = 0; i < sections.length; i++) {
|
||||||
color: Colors.white,
|
final List<EpisodeItem> episodesList = sections[i].episodes!;
|
||||||
),
|
episodes.addAll(episodesList);
|
||||||
tooltip: '选集',
|
}
|
||||||
fuc: () {
|
changeFucCall = videoIntroController!.changeSeasonOrbangu;
|
||||||
int currentCid = widget.controller.cid;
|
} else if (isPage) {
|
||||||
String bvid = widget.controller.bvid;
|
final List<Part> pages =
|
||||||
final List episodes = [];
|
videoIntroController!.videoDetail.value.pages!;
|
||||||
late Function changeFucCall;
|
episodes.addAll(pages);
|
||||||
if (isSeason) {
|
changeFucCall = videoIntroController!.changeSeasonOrbangu;
|
||||||
final List<SectionItem> sections = videoIntroController!
|
} else if (isBangumi) {
|
||||||
.videoDetail.value.ugcSeason!.sections!;
|
episodes.addAll(
|
||||||
for (int i = 0; i < sections.length; i++) {
|
bangumiIntroController!.bangumiDetail.value.episodes!);
|
||||||
final List<EpisodeItem> episodesList =
|
changeFucCall = bangumiIntroController!.changeSeasonOrbangu;
|
||||||
sections[i].episodes!;
|
}
|
||||||
episodes.addAll(episodesList);
|
ListSheet(
|
||||||
}
|
episodes: episodes,
|
||||||
changeFucCall = videoIntroController!.changeSeasonOrbangu;
|
bvid: bvid,
|
||||||
} else if (isPage) {
|
aid: IdUtils.bv2av(bvid),
|
||||||
final List<Part> pages =
|
currentCid: currentCid,
|
||||||
videoIntroController!.videoDetail.value.pages!;
|
changeFucCall: changeFucCall,
|
||||||
episodes.addAll(pages);
|
context: context,
|
||||||
changeFucCall = videoIntroController!.changeSeasonOrbangu;
|
).buildShowBottomSheet();
|
||||||
} else if (isBangumi) {
|
},
|
||||||
episodes.addAll(bangumiIntroController!
|
),
|
||||||
.bangumiDetail.value.episodes!);
|
),
|
||||||
changeFucCall =
|
|
||||||
bangumiIntroController!.changeSeasonOrbangu;
|
|
||||||
}
|
|
||||||
ListSheet(
|
|
||||||
episodes: episodes,
|
|
||||||
bvid: bvid,
|
|
||||||
aid: IdUtils.bv2av(bvid),
|
|
||||||
currentCid: currentCid,
|
|
||||||
changeFucCall: changeFucCall,
|
|
||||||
context: context,
|
|
||||||
).buildShowBottomSheet();
|
|
||||||
},
|
|
||||||
)));
|
|
||||||
}),
|
|
||||||
|
|
||||||
/// 画面比例
|
/// 画面比例
|
||||||
BottomControlType.fit: SizedBox(
|
BottomControlType.fit: SizedBox(
|
||||||
@@ -463,11 +478,11 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
[
|
[
|
||||||
BottomControlType.playOrPause,
|
BottomControlType.playOrPause,
|
||||||
BottomControlType.time,
|
BottomControlType.time,
|
||||||
// BottomControlType.pre,
|
if (anySeason) BottomControlType.pre,
|
||||||
// BottomControlType.next,
|
if (anySeason) BottomControlType.next,
|
||||||
BottomControlType.space,
|
BottomControlType.space,
|
||||||
BottomControlType.episode,
|
if (anySeason) BottomControlType.episode,
|
||||||
BottomControlType.fit,
|
if (_.isFullScreen.value) BottomControlType.fit,
|
||||||
BottomControlType.subtitle,
|
BottomControlType.subtitle,
|
||||||
BottomControlType.speed,
|
BottomControlType.speed,
|
||||||
BottomControlType.fullscreen,
|
BottomControlType.fullscreen,
|
||||||
@@ -500,18 +515,110 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
key: _playerKey,
|
key: _playerKey,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Obx(
|
Obx(
|
||||||
() => Video(
|
() => InteractiveViewer(
|
||||||
key: ValueKey('${_.videoFit.value}'),
|
panEnabled: false, // 启用平移 //单指平移会与横竖手势冲突
|
||||||
controller: videoController,
|
scaleEnabled: true, // 启用缩放
|
||||||
controls: NoVideoControls,
|
minScale: 1.0,
|
||||||
pauseUponEnteringBackgroundMode: !_.continuePlayInBackground.value,
|
maxScale: 2.0,
|
||||||
resumeUponEnteringForegroundMode: true,
|
onInteractionStart: (ScaleStartDetails details) {
|
||||||
// 字幕尺寸调节
|
if (details.pointerCount == 2) {
|
||||||
subtitleViewConfiguration: SubtitleViewConfiguration(
|
interacting = true;
|
||||||
style: subTitleStyle,
|
}
|
||||||
padding: const EdgeInsets.all(24.0),
|
},
|
||||||
textScaleFactor: MediaQuery.textScaleFactorOf(context)),
|
onInteractionUpdate: (ScaleUpdateDetails details) {
|
||||||
fit: _.videoFit.value,
|
if (interacting) return;
|
||||||
|
if (details.pointerCount == 2) {
|
||||||
|
interacting = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 锁定时禁用
|
||||||
|
if (_.controlsLock.value) return;
|
||||||
|
Offset delta = details.focalPointDelta;
|
||||||
|
if (delta.distance < 2) return;
|
||||||
|
RenderBox renderBox =
|
||||||
|
_playerKey.currentContext!.findRenderObject() as RenderBox;
|
||||||
|
// if onHorizontalDragUpdate
|
||||||
|
if (delta.dx.abs() > 5 * delta.dy.abs()) {
|
||||||
|
// live模式下禁用
|
||||||
|
if (_.videoType.value == 'live') return;
|
||||||
|
final int curSliderPosition =
|
||||||
|
_.sliderPosition.value.inMilliseconds;
|
||||||
|
final double scale = 90000 / renderBox.size.width;
|
||||||
|
final Duration pos = Duration(
|
||||||
|
milliseconds:
|
||||||
|
curSliderPosition + (delta.dx * scale).round());
|
||||||
|
final Duration result =
|
||||||
|
pos.clamp(Duration.zero, _.duration.value);
|
||||||
|
_.onUpdatedSliderProgress(result);
|
||||||
|
_.onChangedSliderStart();
|
||||||
|
} else if (delta.dx.abs() * 5 < delta.dy.abs()) {
|
||||||
|
// 垂直方向 音量/亮度调节
|
||||||
|
final double totalWidth = renderBox.size.width;
|
||||||
|
final double tapPosition = details.localFocalPoint.dx;
|
||||||
|
final double sectionWidth = totalWidth / 4;
|
||||||
|
if (tapPosition < sectionWidth) {
|
||||||
|
// 左边区域 👈
|
||||||
|
final double level = renderBox.size.height * 3;
|
||||||
|
final double brightness =
|
||||||
|
_brightnessValue.value - delta.dy / level;
|
||||||
|
final double result = brightness.clamp(0.0, 1.0);
|
||||||
|
setBrightness(result);
|
||||||
|
} else if (tapPosition < sectionWidth * 3) {
|
||||||
|
// 全屏
|
||||||
|
const double threshold = 7.0; // 滑动阈值
|
||||||
|
void fullScreenTrigger(bool status) async {
|
||||||
|
EasyThrottle.throttle(
|
||||||
|
'fullScreen', const Duration(milliseconds: 500), () {
|
||||||
|
// widget.controller.triggerFullScreen(status: status);
|
||||||
|
_.triggerFullScreen(status: status);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delta.dy > threshold) {
|
||||||
|
// 下滑
|
||||||
|
if (_.isFullScreen.value ^ fullScreenGestureReverse) {
|
||||||
|
fullScreenTrigger(fullScreenGestureReverse);
|
||||||
|
}
|
||||||
|
} else if (delta.dy < -threshold) {
|
||||||
|
// 上划
|
||||||
|
if (!_.isFullScreen.value ^ fullScreenGestureReverse) {
|
||||||
|
fullScreenTrigger(!fullScreenGestureReverse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 右边区域
|
||||||
|
final double level = renderBox.size.height * 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: (details) {
|
||||||
|
if (_.isSliderMoving.value) {
|
||||||
|
_.onChangedSliderEnd();
|
||||||
|
_.seekTo(_.sliderPosition.value, type: 'slider');
|
||||||
|
}
|
||||||
|
interacting = false;
|
||||||
|
},
|
||||||
|
child: Video(
|
||||||
|
key: ValueKey('${_.videoFit.value}'),
|
||||||
|
controller: videoController,
|
||||||
|
controls: NoVideoControls,
|
||||||
|
pauseUponEnteringBackgroundMode:
|
||||||
|
!_.continuePlayInBackground.value,
|
||||||
|
resumeUponEnteringForegroundMode: true,
|
||||||
|
// 字幕尺寸调节
|
||||||
|
subtitleViewConfiguration: SubtitleViewConfiguration(
|
||||||
|
style: subTitleStyle,
|
||||||
|
padding: const EdgeInsets.all(24.0),
|
||||||
|
textScaleFactor: MediaQuery.textScaleFactorOf(context)),
|
||||||
|
fit: _.videoFit.value,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
@@ -758,95 +865,6 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
onLongPressEnd: (LongPressEndDetails details) {
|
onLongPressEnd: (LongPressEndDetails details) {
|
||||||
_.setDoubleSpeedStatus(false);
|
_.setDoubleSpeedStatus(false);
|
||||||
},
|
},
|
||||||
|
|
||||||
/// 水平位置 快进 live模式下禁用
|
|
||||||
onHorizontalDragUpdate: (DragUpdateDetails details) {
|
|
||||||
// live模式下禁用 锁定时🔒禁用
|
|
||||||
if (_.videoType.value == 'live' || _.controlsLock.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// final double tapPosition = details.localPosition.dx;
|
|
||||||
final int curSliderPosition =
|
|
||||||
_.sliderPosition.value.inMilliseconds;
|
|
||||||
RenderBox renderBox =
|
|
||||||
_playerKey.currentContext!.findRenderObject() as RenderBox;
|
|
||||||
final double scale = 90000 / renderBox.size.width;
|
|
||||||
final Duration pos = Duration(
|
|
||||||
milliseconds:
|
|
||||||
curSliderPosition + (details.delta.dx * scale).round());
|
|
||||||
final Duration result =
|
|
||||||
pos.clamp(Duration.zero, _.duration.value);
|
|
||||||
_.onUpdatedSliderProgress(result);
|
|
||||||
_.onChangedSliderStart();
|
|
||||||
// _initTapPositoin = tapPosition;
|
|
||||||
},
|
|
||||||
onHorizontalDragEnd: (DragEndDetails details) {
|
|
||||||
if (_.videoType.value == 'live' || _.controlsLock.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_.onChangedSliderEnd();
|
|
||||||
_.seekTo(_.sliderPosition.value, type: 'slider');
|
|
||||||
},
|
|
||||||
// 垂直方向 音量/亮度调节
|
|
||||||
onVerticalDragUpdate: (DragUpdateDetails details) async {
|
|
||||||
RenderBox renderBox =
|
|
||||||
_playerKey.currentContext!.findRenderObject() as RenderBox;
|
|
||||||
|
|
||||||
/// 锁定时禁用
|
|
||||||
if (_.controlsLock.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final double totalWidth = renderBox.size.width;
|
|
||||||
final double tapPosition = details.localPosition.dx;
|
|
||||||
final double sectionWidth = totalWidth / 4;
|
|
||||||
final double delta = details.delta.dy;
|
|
||||||
if (lastFullScreenToggleTime != null &&
|
|
||||||
DateTime.now().difference(lastFullScreenToggleTime!) <
|
|
||||||
const Duration(milliseconds: 500)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (tapPosition < sectionWidth) {
|
|
||||||
// 左边区域 👈
|
|
||||||
final double level = renderBox.size.height * 3;
|
|
||||||
final double brightness =
|
|
||||||
_brightnessValue.value - delta / level;
|
|
||||||
final double result = brightness.clamp(0.0, 1.0);
|
|
||||||
setBrightness(result);
|
|
||||||
} else if (tapPosition < sectionWidth * 3) {
|
|
||||||
// 全屏
|
|
||||||
final double dy = details.delta.dy;
|
|
||||||
const double threshold = 7.0; // 滑动阈值
|
|
||||||
void fullScreenTrigger(bool status) async {
|
|
||||||
lastFullScreenToggleTime = DateTime.now();
|
|
||||||
await widget.controller.triggerFullScreen(status: status);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dy > _distance.value && dy > threshold) {
|
|
||||||
// 下滑退出全屏/进入全屏
|
|
||||||
if (_.isFullScreen.value ^ fullScreenGestureReverse) {
|
|
||||||
fullScreenTrigger(fullScreenGestureReverse);
|
|
||||||
}
|
|
||||||
_distance.value = 0.0;
|
|
||||||
} else if (dy < _distance.value && dy < -threshold) {
|
|
||||||
// 上划进入全屏/退出全屏
|
|
||||||
if (!_.isFullScreen.value ^ fullScreenGestureReverse) {
|
|
||||||
fullScreenTrigger(!fullScreenGestureReverse);
|
|
||||||
}
|
|
||||||
_distance.value = 0.0;
|
|
||||||
}
|
|
||||||
_distance.value = dy;
|
|
||||||
} else {
|
|
||||||
// 右边区域 👈
|
|
||||||
final double level = renderBox.size.height * 0.5;
|
|
||||||
EasyThrottle.throttle(
|
|
||||||
'setVolume', const Duration(milliseconds: 20), () {
|
|
||||||
final double volume = _volumeValue.value - delta / level;
|
|
||||||
final double result = volume.clamp(0.0, 1.0);
|
|
||||||
setVolume(result);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onVerticalDragEnd: (DragEndDetails details) {},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user