overlayer.dart 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. import 'dart:ui' as ui;
  2. import 'package:flutter/material.dart';
  3. import 'package:logging/logging.dart';
  4. import 'package:puzzleweave/play/board.dart';
  5. final _log = Logger('overlayer.dart');
  6. class HintItem {
  7. final ui.Image finger;
  8. final Rect srcRect;
  9. final Rect destRect;
  10. final CurveTween tween;
  11. HintItem(this.finger, this.srcRect, this.destRect) : tween = CurveTween(curve: Curves.easeInOut);
  12. Rect getCurrent(double t) {
  13. return Rect.lerp(srcRect, destRect, tween.transform(t))!;
  14. }
  15. // 动画的前 10% 和后 10% 渐入渐出,中间 80% 完全不透明
  16. double getOpacity(double t) {
  17. if (t < 0.1) {
  18. return t * 10;
  19. } else if (t >= 0.1 && t <= 0.9) {
  20. return 1;
  21. } else {
  22. return (1 - t) * 10;
  23. }
  24. }
  25. }
  26. class OverLayer {
  27. final Board board;
  28. final TickerProvider tickerProvider;
  29. OverlayEntry? _overlayEntry;
  30. final ValueNotifier<int> _notifier = ValueNotifier(0);
  31. late AnimationController hintAnimation;
  32. Listenable get notifiers => Listenable.merge([_notifier, hintAnimation]);
  33. OverLayer(this.board, this.tickerProvider) {
  34. hintAnimation = AnimationController(value: 0, vsync: tickerProvider, duration: const Duration(milliseconds: 1200));
  35. }
  36. void invalidate() {
  37. _notifier.value++;
  38. }
  39. void setup(BuildContext context) {
  40. if (_overlayEntry != null) return;
  41. // 创建并插入 OverlayEntry
  42. _overlayEntry = OverlayEntry(
  43. builder: (context) {
  44. return Positioned.fill(
  45. // 关键:IgnorePointer 确保手势引导动画不会阻碍底层的 BoardPlay 交互
  46. child: IgnorePointer(
  47. child: RepaintBoundary(child: CustomPaint(painter: OverLayerPainter(this))),
  48. ),
  49. );
  50. },
  51. );
  52. Overlay.of(context).insert(_overlayEntry!);
  53. }
  54. HintItem? _hintItem;
  55. HintItem? get hintItem => _hintItem;
  56. void doHint(HintItem hintItem) {
  57. ///上一次hint没有完成
  58. if (_hintItem != null) {
  59. return;
  60. }
  61. _hintItem = hintItem;
  62. hintAnimation.reset();
  63. hintAnimation.repeat(); // 重复播放动画
  64. }
  65. void stopHint() {
  66. if (_hintItem != null) {
  67. hintAnimation.stop();
  68. _hintItem = null;
  69. invalidate();
  70. }
  71. }
  72. bool get isHinting => _hintItem != null && hintAnimation.isAnimating;
  73. destroy() {
  74. _overlayEntry?.remove();
  75. hintAnimation.dispose();
  76. }
  77. }
  78. class OverLayerPainter extends CustomPainter {
  79. final OverLayer overLayer;
  80. OverLayerPainter(this.overLayer) : super(repaint: overLayer.notifiers);
  81. @override
  82. void paint(Canvas canvas, Size size) {
  83. // canvas.clipRect(Offset.zero & size);
  84. if (overLayer.hintItem != null) {
  85. _paintHintItem(canvas, overLayer.hintItem!);
  86. }
  87. }
  88. void _paintHintItem(Canvas canvas, HintItem hintItem) {
  89. Rect srcRect = Offset.zero & Size(hintItem.finger.width.toDouble(), hintItem.finger.height.toDouble());
  90. Rect currentRect = hintItem.getCurrent(overLayer.hintAnimation.value);
  91. // 完善点: 计算并应用透明度
  92. double opacity = hintItem.getOpacity(overLayer.hintAnimation.value);
  93. // 创建 Paint,通过 color.withOpacity 控制透明度
  94. final paint = Paint()
  95. ..color = Colors.white.withOpacity(opacity)
  96. ..isAntiAlias = true;
  97. // 绘制图片
  98. canvas.drawImageRect(hintItem.finger, srcRect, currentRect, paint);
  99. }
  100. @override
  101. bool shouldRepaint(covariant CustomPainter oldDelegate) {
  102. return false;
  103. }
  104. }