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