mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
opt: video page v
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -63,8 +63,8 @@ class SubDetailController extends GetxController {
|
|||||||
if (subList.length >= mediaCount) {
|
if (subList.length >= mediaCount) {
|
||||||
loadingText.value = '没有更多了';
|
loadingText.value = '没有更多了';
|
||||||
}
|
}
|
||||||
|
currentPage += 1;
|
||||||
}
|
}
|
||||||
currentPage += 1;
|
|
||||||
isLoadingMore = false;
|
isLoadingMore = false;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
import 'dart:ui';
|
||||||
import 'package:PiliPlus/common/constants.dart';
|
import 'package:PiliPlus/common/constants.dart';
|
||||||
import 'package:PiliPlus/common/widgets/icon_button.dart';
|
import 'package:PiliPlus/common/widgets/icon_button.dart';
|
||||||
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
||||||
@@ -50,7 +51,7 @@ import '../../../utils/id_utils.dart';
|
|||||||
import 'widgets/header_control.dart';
|
import 'widgets/header_control.dart';
|
||||||
|
|
||||||
class VideoDetailController extends GetxController
|
class VideoDetailController extends GetxController
|
||||||
with GetSingleTickerProviderStateMixin {
|
with GetTickerProviderStateMixin {
|
||||||
/// 路由传参
|
/// 路由传参
|
||||||
String bvid = Get.parameters['bvid']!;
|
String bvid = Get.parameters['bvid']!;
|
||||||
RxInt cid = int.parse(Get.parameters['cid']!).obs;
|
RxInt cid = int.parse(Get.parameters['cid']!).obs;
|
||||||
@@ -128,8 +129,87 @@ class VideoDetailController extends GetxController
|
|||||||
StreamSubscription<Duration>? positionSubscription;
|
StreamSubscription<Duration>? positionSubscription;
|
||||||
|
|
||||||
late final scrollKey = GlobalKey<ExtendedNestedScrollViewState>();
|
late final scrollKey = GlobalKey<ExtendedNestedScrollViewState>();
|
||||||
late final RxString direction = 'horizontal'.obs;
|
late String _direction = 'horizontal';
|
||||||
late final RxDouble scrollRatio = 0.0.obs;
|
late final RxDouble scrollRatio = 0.0.obs;
|
||||||
|
late final ScrollController scrollCtr = ScrollController()
|
||||||
|
..addListener(scrollListener);
|
||||||
|
late bool isExpanding = false;
|
||||||
|
late bool isCollapsing = false;
|
||||||
|
late final AnimationController animationController = AnimationController(
|
||||||
|
vsync: this,
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
);
|
||||||
|
late final double minVideoHeight = Get.width * 9 / 16;
|
||||||
|
late final double maxVideoHeight = max(Get.height * 0.65, Get.width);
|
||||||
|
late double videoHeight = minVideoHeight;
|
||||||
|
|
||||||
|
void setVideoHeight() {
|
||||||
|
String direction = firstVideo.width != null && firstVideo.height != null
|
||||||
|
? firstVideo.width! > firstVideo.height!
|
||||||
|
? 'horizontal'
|
||||||
|
: 'vertical'
|
||||||
|
: 'horizontal';
|
||||||
|
if (_direction != direction) {
|
||||||
|
_direction = direction;
|
||||||
|
double videoHeight =
|
||||||
|
direction == 'vertical' ? maxVideoHeight : minVideoHeight;
|
||||||
|
if (this.videoHeight != videoHeight) {
|
||||||
|
if (videoHeight > this.videoHeight) {
|
||||||
|
// current minVideoHeight
|
||||||
|
isExpanding = true;
|
||||||
|
animationController.forward(
|
||||||
|
from: (minVideoHeight - scrollCtr.offset) / maxVideoHeight);
|
||||||
|
this.videoHeight = maxVideoHeight;
|
||||||
|
} else {
|
||||||
|
// current maxVideoHeight
|
||||||
|
final currentHeight =
|
||||||
|
(maxVideoHeight - scrollCtr.offset).toPrecision(2);
|
||||||
|
double minVideoHeightPrecise = minVideoHeight.toPrecision(2);
|
||||||
|
if (currentHeight == minVideoHeightPrecise) {
|
||||||
|
isExpanding = true;
|
||||||
|
this.videoHeight = minVideoHeight;
|
||||||
|
animationController.forward(from: 1);
|
||||||
|
} else if (currentHeight < minVideoHeightPrecise) {
|
||||||
|
// expande
|
||||||
|
isExpanding = true;
|
||||||
|
animationController.forward(from: currentHeight / minVideoHeight);
|
||||||
|
this.videoHeight = minVideoHeight;
|
||||||
|
} else {
|
||||||
|
// collapse
|
||||||
|
isCollapsing = true;
|
||||||
|
animationController.forward(
|
||||||
|
from: scrollCtr.offset / (maxVideoHeight - minVideoHeight));
|
||||||
|
this.videoHeight = minVideoHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (scrollCtr.offset != 0) {
|
||||||
|
isExpanding = true;
|
||||||
|
animationController.forward(from: 1 - scrollCtr.offset / videoHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scrollListener() {
|
||||||
|
if (scrollCtr.hasClients) {
|
||||||
|
if (scrollCtr.offset == 0) {
|
||||||
|
scrollRatio.value = 0;
|
||||||
|
} else {
|
||||||
|
double offset = scrollCtr.offset - (videoHeight - minVideoHeight);
|
||||||
|
if (offset > 0) {
|
||||||
|
scrollRatio.value = clampDouble(
|
||||||
|
offset.toPrecision(2) /
|
||||||
|
(minVideoHeight - kToolbarHeight).toPrecision(2),
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
scrollRatio.value = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool imageStatus = false;
|
bool imageStatus = false;
|
||||||
|
|
||||||
@@ -993,11 +1073,7 @@ class VideoDetailController extends GetxController
|
|||||||
? null
|
? null
|
||||||
: Duration(milliseconds: data.timeLength!),
|
: Duration(milliseconds: data.timeLength!),
|
||||||
// 宽>高 水平 否则 垂直
|
// 宽>高 水平 否则 垂直
|
||||||
direction: firstVideo.width != null && firstVideo.height != null
|
direction: _direction,
|
||||||
? ((firstVideo.width! - firstVideo.height!) > 0
|
|
||||||
? 'horizontal'
|
|
||||||
: 'vertical')
|
|
||||||
: null,
|
|
||||||
bvid: bvid,
|
bvid: bvid,
|
||||||
cid: cid.value,
|
cid: cid.value,
|
||||||
enableHeart: enableHeart,
|
enableHeart: enableHeart,
|
||||||
@@ -1087,11 +1163,7 @@ class VideoDetailController extends GetxController
|
|||||||
baseUrl: videoUrl,
|
baseUrl: videoUrl,
|
||||||
codecs: 'avc1',
|
codecs: 'avc1',
|
||||||
quality: VideoQualityCode.fromCode(data.quality!)!);
|
quality: VideoQualityCode.fromCode(data.quality!)!);
|
||||||
direction.value = firstVideo.width != null && firstVideo.height != null
|
setVideoHeight();
|
||||||
? firstVideo.width! > firstVideo.height!
|
|
||||||
? 'horizontal'
|
|
||||||
: 'vertical'
|
|
||||||
: 'horizontal';
|
|
||||||
currentDecodeFormats = VideoDecodeFormatsCode.fromString('avc1')!;
|
currentDecodeFormats = VideoDecodeFormatsCode.fromString('avc1')!;
|
||||||
currentVideoQa = VideoQualityCode.fromCode(data.quality!)!;
|
currentVideoQa = VideoQualityCode.fromCode(data.quality!)!;
|
||||||
if (autoPlay.value) {
|
if (autoPlay.value) {
|
||||||
@@ -1162,11 +1234,7 @@ class VideoDetailController extends GetxController
|
|||||||
firstVideo = videosList.firstWhere(
|
firstVideo = videosList.firstWhere(
|
||||||
(e) => e.codecs!.startsWith(currentDecodeFormats.code),
|
(e) => e.codecs!.startsWith(currentDecodeFormats.code),
|
||||||
orElse: () => videosList.first);
|
orElse: () => videosList.first);
|
||||||
direction.value = firstVideo.width != null && firstVideo.height != null
|
setVideoHeight();
|
||||||
? firstVideo.width! > firstVideo.height!
|
|
||||||
? 'horizontal'
|
|
||||||
: 'vertical'
|
|
||||||
: 'horizontal';
|
|
||||||
|
|
||||||
// videoUrl = enableCDN
|
// videoUrl = enableCDN
|
||||||
// ? VideoUtils.getCdnUrl(firstVideo)
|
// ? VideoUtils.getCdnUrl(firstVideo)
|
||||||
@@ -2020,6 +2088,11 @@ class VideoDetailController extends GetxController
|
|||||||
@override
|
@override
|
||||||
void onClose() {
|
void onClose() {
|
||||||
tabCtr.dispose();
|
tabCtr.dispose();
|
||||||
|
if (Get.currentRoute.startsWith('/videoV')) {
|
||||||
|
scrollCtr.removeListener(scrollListener);
|
||||||
|
scrollCtr.dispose;
|
||||||
|
animationController.dispose();
|
||||||
|
}
|
||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -197,6 +197,8 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
videoDetailController.animationController.addListener(animListener);
|
||||||
|
|
||||||
WidgetsBinding.instance.addObserver(this);
|
WidgetsBinding.instance.addObserver(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,10 +245,14 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
|||||||
try {
|
try {
|
||||||
bool isPlaying = status == PlayerStatus.playing;
|
bool isPlaying = status == PlayerStatus.playing;
|
||||||
if (isPlaying) {
|
if (isPlaying) {
|
||||||
if (scrollController.offset != 0) {
|
if (videoDetailController.isExpanding.not &&
|
||||||
isExpanding = true;
|
videoDetailController.scrollCtr.offset != 0 &&
|
||||||
animationController.value = 0;
|
videoDetailController.animationController.isAnimating.not) {
|
||||||
animationController.forward();
|
videoDetailController.isExpanding = true;
|
||||||
|
videoDetailController.animationController.forward(
|
||||||
|
from: 1 -
|
||||||
|
videoDetailController.scrollCtr.offset /
|
||||||
|
videoDetailController.videoHeight);
|
||||||
} else {
|
} else {
|
||||||
videoDetailController.scrollKey.currentState?.setState(() {});
|
videoDetailController.scrollKey.currentState?.setState(() {});
|
||||||
}
|
}
|
||||||
@@ -371,14 +377,11 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
|||||||
videoDetailController.skipTimer = null;
|
videoDetailController.skipTimer = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
animationController.removeListener(animListener);
|
videoDetailController.animationController.removeListener(animListener);
|
||||||
animationController.dispose();
|
|
||||||
if (videoDetailController.showReply) {
|
if (videoDetailController.showReply) {
|
||||||
videoDetailController.scrollKey.currentState?.innerController
|
videoDetailController.scrollKey.currentState?.innerController
|
||||||
.removeListener(innerScrollListener);
|
.removeListener(innerScrollListener);
|
||||||
}
|
}
|
||||||
scrollController.removeListener(scrollListener);
|
|
||||||
scrollController.dispose();
|
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
@@ -557,40 +560,30 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
late final AnimationController animationController = AnimationController(
|
|
||||||
vsync: this,
|
|
||||||
duration: const Duration(milliseconds: 200),
|
|
||||||
)..addListener(animListener);
|
|
||||||
late final ScrollController scrollController = ScrollController()
|
|
||||||
..addListener(scrollListener);
|
|
||||||
late final double defVideoHeight = MediaQuery.sizeOf(context).width * 9 / 16;
|
|
||||||
late double videoHeight = MediaQuery.sizeOf(context).width * 9 / 16;
|
|
||||||
late bool isExpanding = false;
|
|
||||||
|
|
||||||
void animListener() {
|
void animListener() {
|
||||||
if (animationController.isForwardOrCompleted) {
|
if (videoDetailController.animationController.isForwardOrCompleted &&
|
||||||
|
videoDetailController.scrollKey.currentState?.mounted == true) {
|
||||||
|
cal();
|
||||||
videoDetailController.scrollKey.currentState?.setState(() {});
|
videoDetailController.scrollKey.currentState?.setState(() {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void scrollListener() {
|
late double animHeight;
|
||||||
if (scrollController.hasClients) {
|
void cal() {
|
||||||
if (scrollController.offset == 0) {
|
if (videoDetailController.isExpanding) {
|
||||||
videoDetailController.scrollRatio.value = 0;
|
animHeight = clampDouble(
|
||||||
} else {
|
videoDetailController.videoHeight *
|
||||||
double offset =
|
videoDetailController.animationController.value,
|
||||||
scrollController.offset - (videoHeight - defVideoHeight);
|
kToolbarHeight,
|
||||||
if (offset > 0) {
|
videoDetailController.videoHeight);
|
||||||
videoDetailController.scrollRatio.value = clampDouble(
|
} else if (videoDetailController.isCollapsing) {
|
||||||
offset.toPrecision(2) /
|
animHeight = clampDouble(
|
||||||
(defVideoHeight - kToolbarHeight).toPrecision(2),
|
videoDetailController.maxVideoHeight -
|
||||||
0.0,
|
(videoDetailController.maxVideoHeight -
|
||||||
1.0,
|
videoDetailController.minVideoHeight) *
|
||||||
);
|
videoDetailController.animationController.value,
|
||||||
} else {
|
videoDetailController.minVideoHeight,
|
||||||
videoDetailController.scrollRatio.value = 0;
|
videoDetailController.maxVideoHeight);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -638,7 +631,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
|||||||
toolbarHeight: 0,
|
toolbarHeight: 0,
|
||||||
),
|
),
|
||||||
if (videoDetailController.scrollRatio.value != 0 &&
|
if (videoDetailController.scrollRatio.value != 0 &&
|
||||||
scrollController.offset != 0)
|
videoDetailController.scrollCtr.offset != 0)
|
||||||
AppBar(
|
AppBar(
|
||||||
backgroundColor: Theme.of(context)
|
backgroundColor: Theme.of(context)
|
||||||
.colorScheme
|
.colorScheme
|
||||||
@@ -651,33 +644,37 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: Obx(
|
body: Builder(
|
||||||
() {
|
builder: (context) {
|
||||||
if (videoDetailController.direction.value == 'vertical') {
|
|
||||||
videoHeight = max(context.height * 0.7, context.width);
|
|
||||||
} else {
|
|
||||||
videoHeight = context.width * 9 / 16;
|
|
||||||
}
|
|
||||||
return ExtendedNestedScrollView(
|
return ExtendedNestedScrollView(
|
||||||
key: videoDetailController.scrollKey,
|
key: videoDetailController.scrollKey,
|
||||||
physics: const NeverScrollableScrollPhysics(
|
physics: const NeverScrollableScrollPhysics(
|
||||||
parent: ClampingScrollPhysics(),
|
parent: ClampingScrollPhysics(),
|
||||||
),
|
),
|
||||||
controller: scrollController,
|
controller: videoDetailController.scrollCtr,
|
||||||
onlyOneScrollInBody: true,
|
onlyOneScrollInBody: true,
|
||||||
pinnedHeaderSliverHeightBuilder: () {
|
pinnedHeaderSliverHeightBuilder: () {
|
||||||
double height = isFullScreen
|
double height = isFullScreen
|
||||||
? MediaQuery.sizeOf(context).height
|
? MediaQuery.sizeOf(context).height
|
||||||
: isExpanding
|
: videoDetailController.isExpanding ||
|
||||||
? clampDouble(videoHeight * animationController.value,
|
videoDetailController.isCollapsing
|
||||||
kToolbarHeight, videoHeight) +
|
? animHeight
|
||||||
45
|
: videoDetailController.isCollapsing ||
|
||||||
: plPlayerController?.playerStatus.status.value ==
|
plPlayerController
|
||||||
PlayerStatus.playing
|
?.playerStatus.status.value ==
|
||||||
? defVideoHeight + 45
|
PlayerStatus.playing
|
||||||
|
? videoDetailController.minVideoHeight
|
||||||
: kToolbarHeight;
|
: kToolbarHeight;
|
||||||
if (isExpanding && animationController.value == 1) {
|
if (videoDetailController.isExpanding &&
|
||||||
isExpanding = false;
|
videoDetailController.animationController.value == 1) {
|
||||||
|
videoDetailController.isExpanding = false;
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
videoDetailController.scrollKey.currentState
|
||||||
|
?.setState(() {});
|
||||||
|
});
|
||||||
|
} else if (videoDetailController.isCollapsing &&
|
||||||
|
videoDetailController.animationController.value == 1) {
|
||||||
|
videoDetailController.isCollapsing = false;
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
videoDetailController.scrollKey.currentState
|
videoDetailController.scrollKey.currentState
|
||||||
?.setState(() {});
|
?.setState(() {});
|
||||||
@@ -693,7 +690,10 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
|||||||
pinned: true,
|
pinned: true,
|
||||||
expandedHeight: isFullScreen
|
expandedHeight: isFullScreen
|
||||||
? MediaQuery.sizeOf(context).height
|
? MediaQuery.sizeOf(context).height
|
||||||
: videoHeight,
|
: videoDetailController.isExpanding ||
|
||||||
|
videoDetailController.isCollapsing
|
||||||
|
? animHeight
|
||||||
|
: videoDetailController.videoHeight,
|
||||||
flexibleSpace: Stack(
|
flexibleSpace: Stack(
|
||||||
children: [
|
children: [
|
||||||
Obx(
|
Obx(
|
||||||
@@ -731,7 +731,10 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
|||||||
: MediaQuery.of(context)
|
: MediaQuery.of(context)
|
||||||
.padding
|
.padding
|
||||||
.top)
|
.top)
|
||||||
: videoHeight,
|
: videoDetailController.isExpanding ||
|
||||||
|
videoDetailController.isCollapsing
|
||||||
|
? animHeight
|
||||||
|
: videoDetailController.videoHeight,
|
||||||
width: context.width,
|
width: context.width,
|
||||||
child: PopScope(
|
child: PopScope(
|
||||||
canPop: !isFullScreen &&
|
canPop: !isFullScreen &&
|
||||||
@@ -740,7 +743,13 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
|||||||
Orientation.portrait),
|
Orientation.portrait),
|
||||||
onPopInvokedWithResult:
|
onPopInvokedWithResult:
|
||||||
_onPopInvokedWithResult,
|
_onPopInvokedWithResult,
|
||||||
child: videoPlayer(videoWidth, videoHeight),
|
child: videoPlayer(
|
||||||
|
videoWidth,
|
||||||
|
videoDetailController.isExpanding ||
|
||||||
|
videoDetailController.isCollapsing
|
||||||
|
? animHeight
|
||||||
|
: videoDetailController.videoHeight,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -828,7 +837,8 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
|||||||
);
|
);
|
||||||
return videoDetailController.scrollRatio.value ==
|
return videoDetailController.scrollRatio.value ==
|
||||||
0 ||
|
0 ||
|
||||||
scrollController.offset == 0
|
videoDetailController.scrollCtr.offset ==
|
||||||
|
0
|
||||||
? const SizedBox.shrink()
|
? const SizedBox.shrink()
|
||||||
: Positioned.fill(
|
: Positioned.fill(
|
||||||
bottom: -2,
|
bottom: -2,
|
||||||
|
|||||||
Reference in New Issue
Block a user