概述
Canvas组件是App Inventor 2中实现2D绘图和游戏的核心组件。它支持:
- 基本图形绘制(线、圆、矩形)
- 图片精灵(ImageSprite)动画
- 触摸/拖拽交互
- 碰撞检测
- 定时动画
Canvas组件核心属性
| 属性 | 说明 |
|---|---|
| Width/Height | 画布尺寸 |
| BackgroundColor | 背景颜色 |
| BackgroundImage | 背景图片 |
| PaintColor | 画笔颜色 |
| LineWidth | 线条宽度 |
| FontSize | 文字大小 |
核心事件
| 事件 | 参数 | 说明 |
|---|---|---|
| Touched(x, y) | 触摸坐标 | 点击画布 |
| Dragged(x, y, prevX, prevY) | 拖拽坐标 | 拖拽画布 |
| Flung(x, y, speed, heading) | 速度方向 | 快速滑动 |
基本图形绘制
绘制线条
when 画布1.Dragged(startX, startY, prevX, prevY, currentX, currentY, draggedAnySprite) {
画布1.DrawLine(prevX, prevY, currentX, currentY)
}
事件参数名必须与组件定义逐字一致(
startX、prevX…)。写成自己起的名字, 块里读到的就是没赋值的变量。
绘制圆形
when 画布1.Touched(x, y, touchedAnySprite) {
画布1.DrawCircle(x, y, 20, true)
}
绘制矩形
Canvas 没有画矩形的方法,用四条线拼,或者用 DrawShape 传四个角点:
procedure 画矩形(左, 上, 右, 下) {
画布1.DrawLine(左, 上, 右, 上)
画布1.DrawLine(右, 上, 右, 下)
画布1.DrawLine(右, 下, 左, 下)
画布1.DrawLine(左, 下, 左, 上)
}
when 画布1.Touched(x, y, touchedAnySprite) {
画矩形(x - 25, y - 25, x + 25, y + 25)
}
要实心矩形就用 DrawShape(点列表,第二个参数是是否填充):
when 画布1.Touched(x, y, touchedAnySprite) {
画布1.DrawShape([[x - 25, y - 25], [x + 25, y - 25], [x + 25, y + 25], [x - 25, y + 25]], true)
}
绘制文字
when 画布1.Touched(x, y, touchedAnySprite) {
画布1.DrawText("Hello!", x, y)
}
文字颜色跟随画布的 PaintColor 属性,不是 DrawText 的参数:
when 按钮_红字.Click() {
画布1.PaintColor = "&HFFFF0000"
画布1.DrawText("红色的字", 50, 50)
}
ImageSprite(图片精灵)
ImageSprite是Canvas上的可移动图片对象,是实现游戏角色的基础。
核心属性
| 属性 | 说明 |
|---|---|
| Picture | 精灵图片 |
| X, Y | 位置坐标 |
| Heading | 移动方向(0-360°,0=向上) |
| Speed | 移动速度 |
| Interval | 移动间隔(毫秒) |
| Rotates | 是否跟随方向旋转 |
| Visible | 是否可见 |
| Z | 层级(越大越前面) |
基本动画
when Screen1.Initialize() {
图像精灵1.Picture = "character.png"
图像精灵1.X = 100
图像精灵1.Y = 100
图像精灵1.Heading = 90 // 向右
图像精灵1.Speed = 5
图像精灵1.Interval = 50 // 50ms移动一次
图像精灵1.Enabled = true
}
方向控制(键盘/按钮)
when 按钮_上.Click() {
图像精灵1.Heading = 0 // 向上
}
when 按钮_下.Click() {
图像精灵1.Heading = 180 // 向下
}
when 按钮_左.Click() {
图像精灵1.Heading = 270 // 向左
}
when 按钮_右.Click() {
图像精灵1.Heading = 90 // 向右
}
碰撞检测
when 图像精灵1.CollidedWith(other) {
图像精灵1.Enabled = false
对话框1.ShowAlert("碰撞!游戏结束")
}
参数
other是撞上来的那个组件。要区分撞到了谁,在编辑器里把「组件块」(调色板里 每个组件都有一个同名的值块)拖进比较块与other比较即可 —— 组件块是拖出来的, 伪代码写不出来。另外ShowAlert只有一个参数,两个参数的是ShowMessageDialog。
到达边缘
when 图像精灵1.EdgeReached(edge) {
图像精灵1.Bounce(edge)
}
Bounce 会按撞上的那条边自动算出反弹方向,比自己加 180 度准确(斜着撞角落时尤其明显)。
实战案例:地球围绕太阳旋转
global 角度 = 0
global 太阳X = 200
global 太阳Y = 200
global 轨道半径 = 100
when Screen1.Initialize() {
画布1.DrawCircle(太阳X, 太阳Y, 30, true)
计时器1.TimerInterval = 50
计时器1.TimerEnabled = true
}
when 计时器1.Timer() {
角度 = 角度 + 3
if 角度 > 360 {
角度 = 0
}
var 地球X = 太阳X + 轨道半径 * cos(角度 * 3.1415926 / 180)
var 地球Y = 太阳Y + 轨道半径 * sin(角度 * 3.1415926 / 180)
画布1.Clear()
画布1.DrawCircle(太阳X, 太阳Y, 30, true)
画布1.DrawCircle(地球X, 地球Y, 12, true)
}
实战案例:翻翻乐(记忆配对游戏)
功能需求
- 4x4的卡片网格(8对)
- 点击翻开卡片
- 配对成功保持翻开,失败翻回
- 计分和计时
界面设计
- Canvas(填满屏幕)
- Clock(计时用)
- 标签_分数、标签_时间
积木块代码
global 卡片数据 = ["苹果", "香蕉", "橙子", "葡萄", "苹果", "香蕉", "橙子", "葡萄"]
global 卡片状态 = []
global 第一次选择 = 0
global 第二次选择 = 0
global 分数 = 0
when Screen1.Initialize() {
for i = 1 to 8 {
listadd(卡片状态, false)
}
绘制卡片()
}
procedure 绘制卡片() {
画布1.Clear()
for i = 1 to length(卡片数据) {
if select(卡片状态, i) {
画布1.DrawText(select(卡片数据, i), 20, i * 30)
} else {
画布1.DrawText("未翻开", 20, i * 30)
}
}
}
when 画布1.Touched(x, y, touchedAnySprite) {
var 索引 = max(1, min(length(卡片数据), floor(y / 30)))
if select(卡片状态, 索引) == false {
listreplace(卡片状态, 索引, true)
if 第一次选择 = 0 {
第一次选择 = 索引
} else {
第二次选择 = 索引
if select(卡片数据, 第一次选择) == select(卡片数据, 第二次选择) {
分数 = 分数 + 10
标签_分数.Text = join("分数:", 分数)
}
第一次选择 = 0
第二次选择 = 0
}
绘制卡片()
}
}
Canvas绘图方法汇总
参数名与顺序以此为准(写错顺序块能生成,跑起来是错的):
| 方法 | 参数 | 说明 |
|---|---|---|
| DrawLine | x1, y1, x2, y2 | 画线 |
| DrawPoint | x, y | 画点 |
| DrawCircle | centerX, centerY, radius, fill | 画圆 |
| DrawArc | left, top, right, bottom, startAngle, sweepAngle, useCenter, fill | 画弧/扇形 |
| DrawShape | pointList, fill | 按点列表画多边形(矩形用它或四条线) |
| DrawText | text, x, y | 画文字(文字在前) |
| DrawTextAtAngle | text, x, y, angle | 旋转文字 |
| Clear | — | 清除画布 |
| Save | — | 保存为图片,返回路径 |
| SaveAs | fileName | 另存为指定文件名 |
| GetPixelColor | x, y | 取该点颜色(含精灵) |
| GetBackgroundPixelColor | x, y | 取背景该点颜色 |
| SetBackgroundPixelColor | x, y, color | 设背景该点颜色 |
没有 DrawRect / DrawRectangle / DrawImage 这些方法 —— 矩形用 DrawShape 或四条
DrawLine;要显示图片就把 BackgroundImage 属性设成图片,或用 ImageSprite 的 Picture。
画笔颜色、粗细是画布的 PaintColor / LineWidth 属性,不是绘图方法的参数。
常见问题
Q1: 闪烁问题怎么解决?
Canvas频繁重绘会闪烁。解决方案:
- 使用双缓冲(先画完再显示)
- 减少重绘频率
- 使用Sprite而不是Canvas绘图
Q2: 精灵图片背景透明?
使用PNG格式的图片,去掉背景色。App Inventor的ImageSprite支持PNG透明通道。
Q3: 如何实现虚拟摇杆?
Canvas + Dragged事件实现虚拟摇杆:
global 摇杆中心X = 100
global 摇杆中心Y = 300
global 摇杆半径 = 50
when 画布1.Dragged(startX, startY, prevX, prevY, currentX, currentY, draggedAnySprite) {
var dx = currentX - 摇杆中心X
var dy = currentY - 摇杆中心Y
var 距离 = sqrt(dx ^ 2 + dy ^ 2)
var 摇杆X = 摇杆中心X
var 摇杆Y = 摇杆中心Y
if 距离 <= 摇杆半径 {
摇杆X = currentX
摇杆Y = currentY
} else if 距离 > 0 {
摇杆X = 摇杆中心X + 摇杆半径 * dx / 距离
摇杆Y = 摇杆中心Y + 摇杆半径 * dy / 距离
}
图像精灵1.Heading = atan2(-(摇杆Y - 摇杆中心Y), 摇杆X - 摇杆中心X) * 180 / 3.1415926
}
总结
| 功能 | 方案 |
|---|---|
| 自由绘画 | Canvas + Dragged |
| 游戏角色 | ImageSprite |
| 碰撞检测 | ImageSprite.Collided |
| 圆周运动 | 三角函数计算坐标 |
| 卡片游戏 | Canvas重绘 |
| 虚拟摇杆 | Canvas + 拖拽 |
版权声明:MIT App Inventor 官方文档采用 CC BY-SA 4.0 授权,本文档由 ai2claw 🐝 整理。
扫码添加客服咨询