Flutter Flame 1.15.0:如何用指尖/鼠标自由绘制圆形等图形?
在Flutter Flame 1.15.0中实现自由手绘图形(圆形、矩形等)
你的代码问题分析及修复
你当前的代码无法显示图形主要有以下几个原因:
- 坐标不匹配:使用
info.eventPosition.global获取的是屏幕全局坐标,但render方法中的Canvas基于游戏局部坐标系,存在CameraComponent时两者不对应,应改用info.eventPosition.game获取游戏内坐标。 - 渲染顺序错误:先绘制路径再调用
super.render(canvas),导致自定义图形被后续组件(如Level、Camera)覆盖,需调换顺序。 - 缺少手势起始/结束处理:仅监听
onPanUpdate会丢失拖动起始点,且无法在拖动结束时完成路径或重置状态。 - 线条样式不足:默认Paint线条过细可能导致图形不可见,需添加
strokeWidth并设置样式为stroke。
修复后的代码:
import 'dart:async'; import 'dart:ui'; import 'package:catcher/components/level.dart'; import 'package:catcher/config.dart'; import 'package:flame/components.dart'; import 'package:flame/events.dart'; import 'package:flame/game.dart'; import 'package:flutter/material.dart'; class Catcher extends FlameGame with PanDetector { late CameraComponent cam; List<String> levelNames = [ 'level-01', 'level-02', ]; int currentLevelIndex = 0; List<Vector2> points = []; // 存储已完成的路径,实现持久显示 List<Path> savedPaths = []; final Paint _paint = Paint() ..color = Colors.black ..strokeWidth = 3 ..style = PaintingStyle.stroke; @override FutureOr<void> onLoad() async { await images.loadAllImages(); _loadLevel(); return super.onLoad(); } void _loadLevel() { Level world = Level(levelName: levelNames[currentLevelIndex]); cam = CameraComponent.withFixedResolution( world: world, width: gameWidth, height: gameHeight); cam.viewfinder.anchor = Anchor.topLeft; addAll([cam, world]); } @override void render(Canvas canvas) { super.render(canvas); // 先渲染游戏原有组件,再绘制自定义路径 // 渲染已保存的历史路径 for (var path in savedPaths) { canvas.drawPath(path, _paint); } // 渲染当前正在绘制的路径 if (points.length > 1) { Path path = Path(); path.moveTo(points[0].x, points[0].y); for (int i = 1; i < points.length; i++) { path.lineTo(points[i].x, points[i].y); } canvas.drawPath(path, _paint); } } @override void onPanStart(DragStartInfo info) { points.clear(); // 记录拖动起始点 points.add(info.eventPosition.game); super.onPanStart(info); } @override void onPanUpdate(DragUpdateInfo info) { points.add(info.eventPosition.game); super.onPanUpdate(info); } @override void onPanEnd(DragEndInfo info) { // 拖动结束后保存当前路径 if (points.length > 1) { Path path = Path(); path.moveTo(points[0].x, points[0].y); for (int i = 1; i < points.length; i++) { path.lineTo(points[i].x, points[i].y); } savedPaths.add(path); } points.clear(); super.onPanEnd(info); } }
如何绘制圆形、矩形等规则形状
除了手绘自由路径,还可以通过以下方式实现规则图形的绘制:
1. 绘制圆形
以拖动起始点为圆心,拖动终点到起始点的距离为半径绘制圆形:
@override void onPanEnd(DragEndInfo info) { if (points.length >= 2) { final center = points[0]; final radius = points.last.distanceTo(center); Path circlePath = Path()..addOval(Rect.fromCircle(center: center.toOffset(), radius: radius)); savedPaths.add(circlePath); } points.clear(); super.onPanEnd(info); }
2. 绘制矩形
根据拖动的起始点和终点计算矩形边界,绘制矩形:
@override void onPanEnd(DragEndInfo info) { if (points.length >= 2) { final start = points[0]; final end = points.last; final left = min(start.x, end.x); final top = min(start.y, end.y); final right = max(start.x, end.x); final bottom = max(start.y, end.y); Path rectPath = Path()..addRect(Rect.fromLTRB(left, top, right, bottom)); savedPaths.add(rectPath); } points.clear(); super.onPanEnd(info); }
3. 切换绘制模式
添加状态变量,实现自由手绘、圆形、矩形的模式切换:
enum DrawMode { freehand, circle, rectangle } DrawMode currentMode = DrawMode.freehand; @override void onPanEnd(DragEndInfo info) { if (points.length < 2) { points.clear(); super.onPanEnd(info); return; } Path newPath = Path(); switch(currentMode) { case DrawMode.freehand: newPath.moveTo(points[0].x, points[0].y); for (var p in points.skip(1)) { newPath.lineTo(p.x, p.y); } break; case DrawMode.circle: final center = points[0]; final radius = points.last.distanceTo(center); newPath.addOval(Rect.fromCircle(center: center.toOffset(), radius: radius)); break; case DrawMode.rectangle: final start = points[0]; final end = points.last; final left = min(start.x, end.x); final top = min(start.y, end.y); final right = max(start.x, end.x); final bottom = max(start.y, end.y); newPath.addRect(Rect.fromLTRB(left, top, right, bottom)); break; } savedPaths.add(newPath); points.clear(); super.onPanEnd(info); }
内容的提问来源于stack exchange,提问作者Shiva Raj Badu
相关产品推荐
相关产品推荐

