opt marquee

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-09-02 18:33:33 +08:00
parent 498ab2818e
commit f8226fcade
5 changed files with 112 additions and 73 deletions

View File

@@ -3,47 +3,34 @@ import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart';
class MarqueeText extends StatelessWidget {
final double maxWidth;
final String text;
final TextStyle? style;
final double spacing;
final double velocity;
final MarqueeController? controller;
const MarqueeText(
this.text, {
super.key,
required this.maxWidth,
this.style,
this.spacing = 0,
this.velocity = 25,
this.controller,
});
@override
Widget build(BuildContext context) {
final textPainter = TextPainter(
text: TextSpan(
text: text,
style: style,
),
textDirection: TextDirection.ltr,
maxLines: 1,
)..layout();
final width = textPainter.width;
final child = Text(
return NormalMarquee(
velocity: velocity,
spacing: spacing,
controller: controller,
child: Text(
text,
style: style,
maxLines: 1,
textDirection: TextDirection.ltr,
),
);
if (width > maxWidth) {
return NormalMarquee(
velocity: velocity,
spacing: spacing,
child: child,
);
} else {
return child;
}
}
}
@@ -52,6 +39,7 @@ abstract class Marquee extends SingleChildRenderObjectWidget {
final Clip clipBehavior;
final double spacing;
final double velocity;
final MarqueeController? controller;
const Marquee({
super.key,
@@ -60,6 +48,7 @@ abstract class Marquee extends SingleChildRenderObjectWidget {
this.direction = Axis.horizontal,
this.clipBehavior = Clip.hardEdge,
this.spacing = 0,
this.controller,
});
@override
@@ -83,6 +72,7 @@ class NormalMarquee extends Marquee {
super.direction,
super.clipBehavior,
super.spacing,
super.controller,
});
@override
@@ -91,6 +81,7 @@ class NormalMarquee extends Marquee {
velocity: velocity,
clipBehavior: clipBehavior,
spacing: spacing,
controller: controller,
);
}
@@ -120,6 +111,7 @@ abstract class MarqueeRender extends RenderBox
required double velocity,
required double spacing,
required this.clipBehavior,
this.controller,
}) : _spacing = spacing,
_velocity = velocity,
_direction = direction,
@@ -127,6 +119,8 @@ abstract class MarqueeRender extends RenderBox
Clip clipBehavior;
MarqueeController? controller;
Axis _direction;
Axis get direction => _direction;
set direction(Axis value) {
@@ -140,7 +134,7 @@ abstract class MarqueeRender extends RenderBox
if (_velocity == value) return;
_velocity = value;
_simulation = _simulation?.copyWith(initialValue: _delta, velocity: value);
ticker?.reset();
controller?.reset();
}
double _spacing;
@@ -155,7 +149,7 @@ abstract class MarqueeRender extends RenderBox
addSize: value - _spacing,
);
_spacing = value;
ticker?.reset();
controller?.reset();
}
double _delta = 0;
@@ -167,27 +161,18 @@ abstract class MarqueeRender extends RenderBox
@override
void detach() {
ticker?.stop();
controller?.dispose();
super.detach();
}
@override
void attach(PipelineOwner owner) {
super.attach(owner);
ticker?.start();
}
@override
void dispose() {
ticker?.dispose();
ticker = null;
controller?.dispose();
super.dispose();
}
late double _distance;
Ticker? ticker;
_MarqueeSimulation? _simulation;
@override
@@ -218,10 +203,11 @@ abstract class MarqueeRender extends RenderBox
if (_distance > 0) {
updateSize();
ticker ??= Ticker(_onTick)..start();
(controller ??= MarqueeController())
..ticker ??= Ticker(_onTick)
..initStart();
} else {
ticker?.dispose();
ticker = null;
controller?.dispose();
}
}
@@ -292,6 +278,7 @@ class _NormalMarqueeRender extends MarqueeRender {
required super.velocity,
required super.clipBehavior,
required super.spacing,
super.controller,
});
@override
@@ -395,3 +382,37 @@ extension on Ticker {
..start();
}
}
class MarqueeController {
MarqueeController({this.autoStart = true});
bool autoStart;
Ticker? ticker;
void initStart() {
if (autoStart) {
start();
}
}
void start() {
if (ticker != null) {
if (!ticker!.isTicking) {
ticker!.start();
}
}
}
void stop() {
ticker?.stop();
}
void reset() {
ticker?.reset();
}
void dispose() {
ticker?.dispose();
ticker = null;
}
}

View File

@@ -80,8 +80,9 @@ class _MusicDetailPageState extends CommonDynPageState<MusicDetailPage> {
children: [
NetworkImgLayer(
src: info.data.mvCover,
width: 40,
height: 40,
width: 36,
height: 36,
type: ImageType.avatar,
),
Text(info.data.musicTitle!),
],
@@ -456,7 +457,7 @@ class _MusicDetailPageState extends CommonDynPageState<MusicDetailPage> {
behavior: HitTestBehavior.opaque,
child: MarqueeText(
item.musicTitle!,
maxWidth: maxWidth - 136, // 80 + 16 + 32 + 8
spacing: 30,
style: textTheme.titleMedium,
),
),

View File

@@ -114,8 +114,9 @@ class MusicVideoCardH extends StatelessWidget {
],
),
const SizedBox(height: 3),
BounceMarquee(
NormalMarquee(
velocity: 25,
spacing: 30,
child: Row(
spacing: 8,
children: [

View File

@@ -83,6 +83,9 @@ class HeaderControlState extends TripleState<HeaderControl> {
Timer? clock;
bool get isFullScreen => plPlayerController.isFullScreen.value;
Box setting = GStorage.setting;
MarqueeController? marqueeController;
MarqueeController get _marqueeController =>
marqueeController ??= MarqueeController(autoStart: false);
@override
void initState() {
@@ -97,6 +100,8 @@ class HeaderControlState extends TripleState<HeaderControl> {
@override
void dispose() {
clock?.cancel();
marqueeController?.dispose();
marqueeController = null;
super.dispose();
}
@@ -1911,12 +1916,9 @@ class HeaderControlState extends TripleState<HeaderControl> {
padding: isPortrait
? EdgeInsets.zero
: const EdgeInsets.only(right: 10),
child: LayoutBuilder(
builder: (context, constraints) {
return Obx(
child: Obx(
() {
final videoDetail =
introController.videoDetail.value;
final videoDetail = introController.videoDetail.value;
final String title;
if (videoDetail.videos == 1) {
title = videoDetail.title!;
@@ -1924,24 +1926,22 @@ class HeaderControlState extends TripleState<HeaderControl> {
title =
videoDetail.pages
?.firstWhereOrNull(
(e) =>
e.cid == videoDetailCtr.cid.value,
(e) => e.cid == videoDetailCtr.cid.value,
)
?.pagePart ??
videoDetail.title!;
}
return MarqueeText(
title,
maxWidth: constraints.maxWidth,
spacing: 30,
velocity: 30,
controller: _marqueeController,
style: const TextStyle(
color: Colors.white,
fontSize: 16,
),
);
},
);
},
),
),
if (introController.isShowOnlineTotal)

View File

@@ -176,7 +176,23 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
super.initState();
_controlsListener = plPlayerController.showControls.listen((bool val) {
final visible = val && !plPlayerController.controlsLock.value;
visible ? animationController.forward() : animationController.reverse();
if (visible) {
animationController.forward();
widget
.videoDetailController
?.headerCtrKey
.currentState
?.marqueeController
?.start();
} else {
animationController.reverse();
widget
.videoDetailController
?.headerCtrKey
.currentState
?.marqueeController
?.stop();
}
});
animationController = AnimationController(
vsync: this,