fix(player): 使用根Overlay承载飘心

This commit is contained in:
Zhou Weidong committed 2026-10-26 18:54:05 +08:00
1 parent db3e153409
commit ed2cf4c18c
2 files changed
+71 -33

No files matched your search

@@ -123,47 +123,43 @@ class _PlayerDoubleTapHeartBurstState extends State<PlayerDoubleTapHeartBurst>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return IgnorePointer( return IgnorePointer(
child: SizedBox.shrink( child: AnimatedBuilder(
child: AnimatedBuilder( animation: _controller,
animation: _controller, builder: (context, _) {
builder: (context, _) { final elapsed =
final elapsed = _controller.value * _controller.duration!.inMilliseconds;
_controller.value * _controller.duration!.inMilliseconds; return Stack(
return Stack( fit: StackFit.expand,
fit: StackFit.expand, clipBehavior: Clip.none,
clipBehavior: Clip.none, children: [
children: [ for (var index = 0; index < _hearts.length; index++)
for (var index = 0; index < _hearts.length; index++) _heart(_hearts[index], elapsed, index),
_heart(_hearts[index], elapsed, index), if (kDebugMode)
if (kDebugMode) Positioned(
Positioned( left: widget.origin.dx + 8,
left: widget.origin.dx + 8, top: widget.origin.dy + 10,
top: widget.origin.dy, child: IgnorePointer(
child: IgnorePointer( child: Text(
child: Text( '${elapsed.round()}ms',
'${elapsed.round()}ms', key: const Key('heart-burst-elapsed-debug'),
key: const Key('heart-burst-elapsed-debug'), style: const TextStyle(
style: const TextStyle( color: Colors.white60,
color: Colors.white60, fontSize: 11,
fontSize: 11,
),
), ),
), ),
), ),
], ),
); ],
}, );
), },
), ),
); );
} }
Widget _heart(_HeartParticle heart, double elapsed, int index) { Widget _heart(_HeartParticle heart, double elapsed, int index) {
final progress = ((elapsed - heart.delay) / _heartDurationMs).clamp( final rawProgress = (elapsed - heart.delay) / _heartDurationMs;
0.0, if (rawProgress < 0) return const SizedBox.shrink();
1.0, final progress = rawProgress.clamp(0.0, 1.0);
);
if (progress <= 0) return const SizedBox.shrink();
final move = Curves.easeOutCubic.transform(progress); final move = Curves.easeOutCubic.transform(progress);
final scale = progress < _heartPopPortion final scale = progress < _heartPopPortion
? _heartStartScale + ? _heartStartScale +
@@ -57,6 +57,48 @@ void main() {
expect(_opacity(tester, 0), lessThan(0.5)); expect(_opacity(tester, 0), lessThan(0.5));
}); });
testWidgets('heart position is anchored to the overlay origin', (
tester,
) async {
const origin = Offset(300, 546);
await tester.pumpWidget(
const MaterialApp(
home: Scaffold(
body: Stack(
fit: StackFit.expand,
children: [
Positioned.fill(
child: PlayerDoubleTapHeartBurst(
burstId: 4,
origin: origin,
onCompleted: _noop,
),
),
],
),
),
),
);
final firstHeart = find.byKey(const Key('double-tap-heart-0'));
expect(
(tester.getCenter(firstHeart) - origin).distance,
lessThanOrEqualTo(1),
);
final debugTopLeft = tester.getTopLeft(
find.byKey(const Key('heart-burst-elapsed-debug')),
);
expect(
(debugTopLeft - const Offset(308, 556)).distance,
lessThanOrEqualTo(1),
);
await tester.pump(const Duration(milliseconds: 750));
final midpoint = tester.getCenter(firstHeart);
expect(midpoint.dy, lessThan(origin.dy));
expect((midpoint.dx - origin.dx).abs(), lessThanOrEqualTo(1));
});
testWidgets('develop heart burst completes after about 1590ms', ( testWidgets('develop heart burst completes after about 1590ms', (
tester, tester,
) async { ) async {