Files
2026-09-15 15:44:13 +07:00

98 lines
2.8 KiB
Dart

import 'package:flutter/material.dart';
import 'package:hgdj/assets_tool/images.dart';
/// 双击点赞爱心动效的触发器。onDoubleTapDown 里 [markAt] 记落点,onDoubleTap 里 [burst] 放动画
class LikeBurstController extends ChangeNotifier {
Offset? at;
/// 记下双击落点(相对 Stack 左上角)
void markAt(Offset offset) => at = offset;
/// 在落点弹一颗爱心;没记过落点就不弹
void burst() {
if (at == null) return;
notifyListeners();
}
}
/// 双击处弹出的爱心:弹起放大 → 停顿 → 淡出。必须直接挂在 Stack 里
class LikeBurstView extends StatefulWidget {
const LikeBurstView({
super.key,
required this.controller,
this.icon = 'like_red.png',
this.size = 90,
});
final LikeBurstController controller;
final String icon; // assets/images/common 下的图名
final double size;
@override
State<LikeBurstView> createState() => _LikeBurstViewState();
}
class _LikeBurstViewState extends State<LikeBurstView>
with SingleTickerProviderStateMixin {
late final AnimationController _ctr;
Offset? _at;
@override
void initState() {
super.initState();
_ctr = AnimationController(
vsync: this, duration: const Duration(milliseconds: 600))
..addStatusListener((status) {
if (status == AnimationStatus.completed && mounted)
setState(() => _at = null);
});
widget.controller.addListener(_onBurst);
}
@override
void didUpdateWidget(covariant LikeBurstView oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.controller != widget.controller) {
oldWidget.controller.removeListener(_onBurst);
widget.controller.addListener(_onBurst);
}
}
@override
void dispose() {
widget.controller.removeListener(_onBurst);
_ctr.dispose();
super.dispose();
}
void _onBurst() {
setState(() => _at = widget.controller.at);
_ctr.forward(from: 0); // 连点就从头重放
}
@override
Widget build(BuildContext context) {
final at = _at;
if (at == null) return const SizedBox.shrink();
return Positioned(
left: at.dx - widget.size / 2,
top: at.dy - widget.size / 2,
child: IgnorePointer(
child: FadeTransition(
opacity: TweenSequence<double>([
TweenSequenceItem(tween: Tween(begin: 0.0, end: 1.0), weight: 20),
TweenSequenceItem(tween: ConstantTween(1.0), weight: 40),
TweenSequenceItem(tween: Tween(begin: 1.0, end: 0.0), weight: 40),
]).animate(_ctr),
child: ScaleTransition(
scale: Tween(begin: 0.5, end: 1.3).animate(
CurvedAnimation(parent: _ctr, curve: Curves.easeOutBack)),
child: Image.asset(widget.icon.commonImgPath,
width: widget.size, height: widget.size),
),
),
),
);
}
}