mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
opt: video seek preview
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -410,7 +410,7 @@ class _NineGridViewState extends State<NineGridView> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget? child = Container();
|
||||
Widget? child;
|
||||
double? realWidth = widget.width;
|
||||
double? realHeight = widget.height;
|
||||
switch (widget.type) {
|
||||
|
||||
@@ -1924,8 +1924,7 @@ List<SettingsModel> get extraSettings => [
|
||||
),
|
||||
SettingsModel(
|
||||
settingsType: SettingsType.sw1tch,
|
||||
title: '视频进度条缩略图',
|
||||
subtitle: '滑动进度条时显示视频缩略图',
|
||||
title: '滑动跳转预览视频缩略图',
|
||||
leading: Icon(Icons.preview_outlined),
|
||||
setKey: SettingBoxKey.showSeekPreview,
|
||||
defaultVal: true,
|
||||
|
||||
@@ -512,7 +512,7 @@ class PlPlayerController {
|
||||
if (showSeekPreview) {
|
||||
videoShot = null;
|
||||
showPreview.value = false;
|
||||
localPosition.value = Offset.zero;
|
||||
previewDx.value = 0;
|
||||
}
|
||||
|
||||
if (_videoPlayerController != null &&
|
||||
@@ -1575,7 +1575,7 @@ class PlPlayerController {
|
||||
late bool _isQueryingVideoShot = false;
|
||||
Map? videoShot;
|
||||
late final RxBool showPreview = false.obs;
|
||||
late final Rx<Offset> localPosition = Offset.zero.obs;
|
||||
late final RxDouble previewDx = 0.0.obs;
|
||||
|
||||
void getVideoShot() async {
|
||||
if (_isQueryingVideoShot) {
|
||||
|
||||
@@ -688,6 +688,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
if (_gestureType == 'horizontal') {
|
||||
// live模式下禁用
|
||||
if (plPlayerController.videoType.value == 'live') return;
|
||||
|
||||
final int curSliderPosition =
|
||||
plPlayerController.sliderPosition.value.inMilliseconds;
|
||||
final double scale = 90000 / renderBox.size.width;
|
||||
@@ -698,6 +699,16 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
pos.clamp(Duration.zero, plPlayerController.duration.value);
|
||||
plPlayerController.onUpdatedSliderProgress(result);
|
||||
plPlayerController.onChangedSliderStart();
|
||||
if (plPlayerController.showSeekPreview) {
|
||||
try {
|
||||
plPlayerController.previewDx.value = result.inMilliseconds /
|
||||
plPlayerController.duration.value.inMilliseconds *
|
||||
context.size!.width;
|
||||
if (plPlayerController.showPreview.value.not) {
|
||||
plPlayerController.showPreview.value = true;
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
} else if (_gestureType == 'left') {
|
||||
// 左边区域 👈
|
||||
final double level = renderBox.size.height * 3;
|
||||
@@ -744,6 +755,9 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
}
|
||||
},
|
||||
onInteractionEnd: (ScaleEndDetails details) {
|
||||
if (plPlayerController.showSeekPreview) {
|
||||
plPlayerController.showPreview.value = false;
|
||||
}
|
||||
if (plPlayerController.isSliderMoving.value) {
|
||||
plPlayerController.onChangedSliderEnd();
|
||||
plPlayerController.seekTo(
|
||||
@@ -1036,8 +1050,8 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
ClipRect(
|
||||
child: AppBarAni(
|
||||
if (plPlayerController.showControls.value)
|
||||
AppBarAni(
|
||||
controller: animationController,
|
||||
visible: !plPlayerController.controlsLock.value &&
|
||||
plPlayerController.showControls.value,
|
||||
@@ -1048,7 +1062,6 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
buildBottomControl: buildBottomControl(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -1061,27 +1074,27 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
final int max = plPlayerController.durationSeconds.value;
|
||||
final int buffer = plPlayerController.bufferedSeconds.value;
|
||||
if (plPlayerController.showControls.value) {
|
||||
return Container();
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
if (defaultBtmProgressBehavior ==
|
||||
BtmProgressBehavior.alwaysHide.code) {
|
||||
return const SizedBox();
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
if (defaultBtmProgressBehavior ==
|
||||
BtmProgressBehavior.onlyShowFullScreen.code &&
|
||||
!isFullScreen) {
|
||||
return const SizedBox();
|
||||
return const SizedBox.shrink();
|
||||
} else if (defaultBtmProgressBehavior ==
|
||||
BtmProgressBehavior.onlyHideFullScreen.code &&
|
||||
isFullScreen) {
|
||||
return const SizedBox();
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
if (plPlayerController.videoType.value == 'live') {
|
||||
return Container();
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
if (value > max || max <= 0) {
|
||||
return const SizedBox();
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return Positioned(
|
||||
bottom: -1,
|
||||
@@ -1147,14 +1160,18 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
onDragUpdate: (duration) {
|
||||
plPlayerController
|
||||
.onUpdatedSliderProgress(duration.timeStamp);
|
||||
if (plPlayerController.showPreview.value.not) {
|
||||
plPlayerController.showPreview.value = true;
|
||||
if (plPlayerController.showSeekPreview) {
|
||||
if (plPlayerController.showPreview.value.not) {
|
||||
plPlayerController.showPreview.value = true;
|
||||
}
|
||||
plPlayerController.previewDx.value =
|
||||
duration.localPosition.dx;
|
||||
}
|
||||
plPlayerController.localPosition.value =
|
||||
duration.localPosition;
|
||||
},
|
||||
onSeek: (duration) {
|
||||
plPlayerController.showPreview.value = false;
|
||||
if (plPlayerController.showSeekPreview) {
|
||||
plPlayerController.showPreview.value = false;
|
||||
}
|
||||
plPlayerController.onChangedSliderEnd();
|
||||
plPlayerController
|
||||
.onChangedSlider(duration.inSeconds.toDouble());
|
||||
@@ -1374,7 +1391,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
]),
|
||||
)));
|
||||
} else {
|
||||
return const SizedBox();
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -1424,7 +1441,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
},
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
const Spacer(),
|
||||
// Expanded(
|
||||
@@ -1470,7 +1487,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
},
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -1486,38 +1503,42 @@ Widget buildSeekPreviewWidget(PlPlayerController plPlayerController) {
|
||||
return Obx(() {
|
||||
if (plPlayerController.showPreview.value.not) {
|
||||
return SizedBox.shrink(
|
||||
key: ValueKey(plPlayerController.localPosition.value),
|
||||
key: ValueKey(plPlayerController.previewDx.value),
|
||||
);
|
||||
}
|
||||
if (plPlayerController.videoShot == null) {
|
||||
plPlayerController.getVideoShot();
|
||||
return SizedBox.shrink(
|
||||
key: ValueKey(plPlayerController.localPosition.value),
|
||||
key: ValueKey(plPlayerController.previewDx.value),
|
||||
);
|
||||
} else if (plPlayerController.videoShot!['status'] == false) {
|
||||
return SizedBox.shrink(
|
||||
key: ValueKey(plPlayerController.localPosition.value),
|
||||
key: ValueKey(plPlayerController.previewDx.value),
|
||||
);
|
||||
}
|
||||
|
||||
return LayoutBuilder(
|
||||
key: ValueKey(plPlayerController.localPosition.value),
|
||||
key: ValueKey(plPlayerController.previewDx.value),
|
||||
builder: (context, constraints) {
|
||||
try {
|
||||
double scale = 1.5;
|
||||
double scale = plPlayerController.isFullScreen.value &&
|
||||
plPlayerController.direction.value == 'horizontal'
|
||||
? 4
|
||||
: 2.5;
|
||||
// offset
|
||||
double left =
|
||||
(plPlayerController.localPosition.value.dx - 48 * scale / 2)
|
||||
.clamp(8, constraints.maxWidth - 48 * scale - 8);
|
||||
double left = (plPlayerController.previewDx.value - 48 * scale / 2)
|
||||
.clamp(8, constraints.maxWidth - 48 * scale - 8);
|
||||
|
||||
// index
|
||||
// int index = plPlayerController.sliderPositionSeconds.value ~/ 5;
|
||||
int index = (List<int>.from(
|
||||
plPlayerController.videoShot!['data']['index'])
|
||||
.where((item) =>
|
||||
item <= plPlayerController.sliderPositionSeconds.value)
|
||||
.length -
|
||||
2);
|
||||
int index = max(
|
||||
0,
|
||||
(List<int>.from(plPlayerController.videoShot!['data']['index'])
|
||||
.where((item) =>
|
||||
item <=
|
||||
plPlayerController.sliderPositionSeconds.value)
|
||||
.length -
|
||||
2));
|
||||
|
||||
// pageIndex
|
||||
int pageIndex = (index ~/ 100).clamp(
|
||||
@@ -1545,22 +1566,26 @@ Widget buildSeekPreviewWidget(PlPlayerController plPlayerController) {
|
||||
return Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
padding: EdgeInsets.only(left: left),
|
||||
child: SizedBox(
|
||||
width: 48 * scale,
|
||||
height: 27 * scale,
|
||||
child: UnconstrainedBox(
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
child: Align(
|
||||
widthFactor: 0.1,
|
||||
heightFactor: 0.1,
|
||||
alignment: alignment,
|
||||
child: CachedNetworkImage(
|
||||
width: 480 * scale,
|
||||
height: 270 * scale,
|
||||
imageUrl: parseUrl(plPlayerController.videoShot!['data']
|
||||
['image'][pageIndex]),
|
||||
),
|
||||
child: UnconstrainedBox(
|
||||
child: ClipRRect(
|
||||
// clipBehavior: Clip.antiAlias,
|
||||
// decoration: BoxDecoration(
|
||||
// border: Border.all(
|
||||
// color: Colors.white,
|
||||
// strokeAlign: BorderSide.strokeAlignOutside,
|
||||
// ),
|
||||
// borderRadius: BorderRadius.circular(scale == 2.5 ? 6 : 10),
|
||||
// ),
|
||||
borderRadius: BorderRadius.circular(scale == 2.5 ? 6 : 10),
|
||||
child: Align(
|
||||
widthFactor: 0.1,
|
||||
heightFactor: 0.1,
|
||||
alignment: alignment,
|
||||
child: CachedNetworkImage(
|
||||
width: 480 * scale,
|
||||
height: 270 * scale,
|
||||
imageUrl: parseUrl(plPlayerController.videoShot!['data']
|
||||
['image'][pageIndex]),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -1569,7 +1594,7 @@ Widget buildSeekPreviewWidget(PlPlayerController plPlayerController) {
|
||||
} catch (e) {
|
||||
debugPrint('seek preview: $e');
|
||||
return SizedBox.shrink(
|
||||
key: ValueKey(plPlayerController.localPosition.value),
|
||||
key: ValueKey(plPlayerController.previewDx.value),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -30,9 +30,7 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
||||
//阅读器限制
|
||||
Timer? accessibilityDebounce;
|
||||
double lastAnnouncedValue = -1;
|
||||
return Container(
|
||||
color: Colors.transparent,
|
||||
height: 120,
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
@@ -104,11 +102,13 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
||||
onDragUpdate: (duration) {
|
||||
double newProgress =
|
||||
duration.timeStamp.inSeconds / max;
|
||||
if (controller!.showPreview.value.not) {
|
||||
controller!.showPreview.value = true;
|
||||
if (controller!.showSeekPreview) {
|
||||
if (controller!.showPreview.value.not) {
|
||||
controller!.showPreview.value = true;
|
||||
}
|
||||
controller!.previewDx.value =
|
||||
duration.localPosition.dx;
|
||||
}
|
||||
controller!.localPosition.value =
|
||||
duration.localPosition;
|
||||
if ((newProgress - lastAnnouncedValue).abs() > 0.02) {
|
||||
accessibilityDebounce?.cancel();
|
||||
accessibilityDebounce =
|
||||
@@ -123,7 +123,9 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
||||
.onUpdatedSliderProgress(duration.timeStamp);
|
||||
},
|
||||
onSeek: (duration) {
|
||||
controller!.showPreview.value = false;
|
||||
if (controller!.showSeekPreview) {
|
||||
controller!.showPreview.value = false;
|
||||
}
|
||||
controller!.onChangedSliderEnd();
|
||||
controller!
|
||||
.onChangedSlider(duration.inSeconds.toDouble());
|
||||
@@ -168,7 +170,7 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 16,
|
||||
bottom: 18,
|
||||
child: buildSeekPreviewWidget(controller!),
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user