import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; import 'package:puzzleweave/play/board.dart'; final _log = Logger('overlayer.dart'); class HintItem { final ui.Image finger; final Rect srcRect; final Rect destRect; final CurveTween tween; HintItem(this.finger, this.srcRect, this.destRect) : tween = CurveTween(curve: Curves.easeInOut); Rect getCurrent(double t) { return Rect.lerp(srcRect, destRect, tween.transform(t))!; } // 动画的前 10% 和后 10% 渐入渐出,中间 80% 完全不透明 double getOpacity(double t) { if (t < 0.1) { return t * 10; } else if (t >= 0.1 && t <= 0.9) { return 1; } else { return (1 - t) * 10; } } } class OverLayer { final Board board; final TickerProvider tickerProvider; OverlayEntry? _overlayEntry; bool _isDisposed = false; final ValueNotifier _notifier = ValueNotifier(0); late AnimationController hintAnimation; Listenable get notifiers => Listenable.merge([_notifier, hintAnimation]); OverLayer(this.board, this.tickerProvider) { hintAnimation = AnimationController(value: 0, vsync: tickerProvider, duration: const Duration(milliseconds: 1200)); } void invalidate() { _notifier.value++; } void setup(BuildContext context) { if (_overlayEntry != null) return; // 创建并插入 OverlayEntry _overlayEntry = OverlayEntry( builder: (context) { return Positioned.fill( // 关键:IgnorePointer 确保手势引导动画不会阻碍底层的 BoardPlay 交互 child: IgnorePointer( child: RepaintBoundary(child: CustomPaint(painter: OverLayerPainter(this))), ), ); }, ); Overlay.of(context).insert(_overlayEntry!); } HintItem? _hintItem; HintItem? get hintItem => _hintItem; void doHint(HintItem hintItem) { ///上一次hint没有完成 if (_isDisposed || _hintItem != null) { return; } _hintItem = hintItem; hintAnimation.reset(); hintAnimation.repeat(); // 重复播放动画 } void stopHint() { if (!_isDisposed && _hintItem != null) { hintAnimation.stop(); _hintItem = null; invalidate(); } } bool get isHinting => _hintItem != null && hintAnimation.isAnimating; destroy() { _isDisposed = true; // 标记已销毁 _overlayEntry?.remove(); _overlayEntry = null; hintAnimation.dispose(); } } class OverLayerPainter extends CustomPainter { final OverLayer overLayer; OverLayerPainter(this.overLayer) : super(repaint: overLayer.notifiers); @override void paint(Canvas canvas, Size size) { // canvas.clipRect(Offset.zero & size); if (overLayer.hintItem != null) { _paintHintItem(canvas, overLayer.hintItem!); } } void _paintHintItem(Canvas canvas, HintItem hintItem) { Rect srcRect = Offset.zero & Size(hintItem.finger.width.toDouble(), hintItem.finger.height.toDouble()); Rect currentRect = hintItem.getCurrent(overLayer.hintAnimation.value); // 完善点: 计算并应用透明度 double opacity = hintItem.getOpacity(overLayer.hintAnimation.value); // 创建 Paint,通过 color.withOpacity 控制透明度 final paint = Paint() ..color = Colors.white.withOpacity(opacity) ..isAntiAlias = true; // 绘制图片 canvas.drawImageRect(hintItem.finger, srcRect, currentRect, paint); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return false; } }