|
|
@@ -1,135 +0,0 @@
|
|
|
-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<int> _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;
|
|
|
- }
|
|
|
-}
|