canvas 画布
canvas 画布
canvas 是 ScriptX 里专门处理位图画布绘制的模块对象。你可以把它理解成一层“更适合 JS 脚本写法”的 Android Canvas 包装。
它现在主要做三件事:
- 创建或包裹一张可绘制位图
- 提供一组常用
Paint/Path/Rect/Matrix辅助构造器 - 把最常见的绘制、变换、保存恢复操作挂到画布实例上
先记住这 10 条
canvas.create(...)返回的是 ScriptX 的画布实例,不是 Android 原生Bitmap。canvas.createBitmap(...)才是直接返回 AndroidBitmap。canvas.wrap(source)/canvas.from(source)能包Bitmap、Image、现成画布实例。- 被
wrap(...)的图如果不是可写ARGB_8888,当前实现会自动复制一份可写副本。 canvas.newPaint()默认带Paint.ANTI_ALIAS_FLAG。surface.drawColor(color, mode)里的mode必须是实际PorterDuff.Mode枚举对象,传字符串当前不会自动帮你转。surface.drawRect(rect, paint)、drawOval(rect, paint)、drawArc(rect, ...)这几类 API 都能吃Rect、RectF或[left, top, right, bottom]数组。surface.drawBitmap(...)既能吃Bitmap,也能吃images模块返回的Image,还可以吃另一个canvas画布实例。save()/restore()/restoreToCount()是画布状态栈,做平移、旋转、缩放时很常用。- 最终想落盘时,一般是拿
surface.getBitmap()再交给images.save(...)或 Java 自己去写文件。
一个最小工作流
const surface = canvas.create(320, 180, "ARGB_8888");
const paint = canvas.newPaint();
paint.setARGB(255, 255, 0, 0);
surface.drawColor("#ffffff");
surface.drawRect(20, 20, 120, 80, paint);
images.save(images.copy(canvas.wrap(surface).getBitmap ? images.matToImage(surface.getBitmap()) : null), "./out.png");
上面这段只是为了先建立感觉。实际更常见、也更顺手的写法是:
const surface = canvas.create(320, 180);
const paint = canvas.newPaint();
paint.setARGB(255, 255, 0, 0);
surface.drawColor("#ffffff");
surface.drawRect(20, 20, 120, 80, paint);
const bitmap = surface.getBitmap();
const image = images.copy(images.fromBytes(images.toBytes(images.matToImage ? images.matToImage(bitmap) : null)));
真正落盘时,通常还是更建议直接像回归脚本那样把 Bitmap 交给 Java 或其他现有流程。下面正文重点放在 API 本身。
canvas.create(width, height, config?)
创建一张新的可绘制画布。
参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
width | number | string | 1 | 小于 1 会被抬到 1 |
height | number | string | 1 | 小于 1 会被抬到 1 |
config | Bitmap.Config | string | ARGB_8888 | 位图配置 |
config 可填值
| 可填值 | 实际配置 |
|---|---|
Bitmap.Config 实例 | 直接使用 |
"ALPHA_8" | Bitmap.Config.ALPHA_8 |
"RGB_565" | Bitmap.Config.RGB_565 |
"ARGB_4444" | Bitmap.Config.ARGB_4444 |
"RGBA_F16" | Bitmap.Config.RGBA_F16 |
| 其他值或不传 | Bitmap.Config.ARGB_8888 |
返回值
surface
也就是一张带内部 Canvas 的 ScriptX 画布实例。
示例
const surface = canvas.create(400, 240, "ARGB_8888");
log(surface.width());
log(surface.height());
canvas.createBitmap(width, height, config?)
直接创建 Android Bitmap。
返回值
Bitmap
适合什么时候用
- 你后面还要把它交给 Java 原生 API
- 你已经有自己的
Canvas(bitmap)逻辑 - 你想先拿位图,再
canvas.wrap(bitmap)
示例
const bitmap = canvas.createBitmap(128, 128, "ARGB_8888");
const surface = canvas.wrap(bitmap);
canvas.wrap(source)
把现成的图像来源包成可绘制画布实例。
参数
| 参数 | 类型 | 说明 |
|---|---|---|
source | Bitmap | Image | surface | 只能是这 3 类 |
返回值
surface
真实行为
- 如果原图已经是“可写 +
ARGB_8888”,直接包它 - 否则会先复制成一份可写
ARGB_8888
示例
const img = images.read("./assets/logo.png");
const surface = canvas.wrap(img);
canvas.from(source)
canvas.wrap(source) 的别名。
示例
const surface = canvas.from(images.read("./assets/logo.png"));
canvas.newPaint()
创建一个带抗锯齿标记的 Paint。
返回值
Paint
示例
const paint = canvas.newPaint();
paint.setStyle(canvas.Paint.Style.FILL);
paint.setARGB(255, 255, 0, 0);
canvas.newPath()
创建一个空 Path。
返回值
Path
canvas.newRect(left?, top?, right?, bottom?)
创建 Rect。
参数
- 传 4 个值:按
left, top, right, bottom - 不够 4 个:返回一个空
Rect()
返回值
Rect
canvas.newRectF(left?, top?, right?, bottom?)
创建 RectF。
返回值
RectF
canvas.newMatrix()
创建 Matrix。
返回值
Matrix
canvas.newPicture()
创建 Picture。
返回值
Picture
canvas.Canvas
暴露 Android 原生 Canvas 类。
适合什么时候看它
- 你想直接用 Android 原生静态常量或构造
- 你已经熟悉 Java / Android 图形 API
canvas.Paint
暴露 Android 原生 Paint 类。
当前还顺手挂了哪些内部枚举
| 名字 | 说明 |
|---|---|
canvas.Paint.Style | FILL、STROKE、FILL_AND_STROKE |
canvas.Paint.Cap | BUTT、ROUND、SQUARE |
canvas.Paint.Join | MITER、ROUND、BEVEL |
canvas.Paint.Align | LEFT、CENTER、RIGHT |
canvas.Path
暴露 Android 原生 Path 类。
canvas.Rect
暴露 Android 原生 Rect 类。
canvas.RectF
暴露 Android 原生 RectF 类。
canvas.Matrix
暴露 Android 原生 Matrix 类。
canvas.Picture
暴露 Android 原生 Picture 类。
canvas.Bitmap
暴露 Android 原生 Bitmap 类。
当前顺手挂出的内部枚举
canvas.Bitmap.Config
canvas.PorterDuff
暴露 Android 原生 PorterDuff 类。
当前顺手挂出的内部枚举
canvas.PorterDuff.Mode
画布实例方法
下面这些方法都挂在 canvas.create(...)、canvas.wrap(...)、canvas.from(...) 返回的 surface 实例上。
surface.getWidth()
返回底层位图宽度。
surface.getHeight()
返回底层位图高度。
surface.width()
getWidth() 的短写。
surface.height()
getHeight() 的短写。
surface.getBitmap()
拿到底层 Bitmap。
surface.drawRGB(r, g, b)
直接用 RGB 填充;每个通道都会被夹到 0 ~ 255。
surface.drawARGB(a, r, g, b)
直接用 ARGB 填充;每个通道都会被夹到 0 ~ 255。
surface.drawColor(color)
按颜色值填充整张画布。
颜色值写法
这里走的是 ScriptX 统一颜色解析,所以常见的:
"#ff0000"0xffff0000[255, 0, 0]
都能用。
surface.drawColor(color, mode)
带混合模式填充整张画布。
mode 要怎么传
当前最稳的是直接传真实枚举对象,例如:
surface.drawColor("#00000000", canvas.PorterDuff.Mode.CLEAR);
纯字符串当前不会自动转成 Mode。
surface.drawPaint(paint)
用整支 Paint 填充。
surface.drawPoint(x, y, paint)
画单个点。
surface.drawPoints(points, paint)
画多个点。
points 格式
必须是“数字数组”,按下面格式解释:
[x1, y1, x2, y2, x3, y3]
当前接受:
- JS 数组
FloatArrayDoubleArrayIntArray
surface.drawLine(startX, startY, stopX, stopY, paint)
画一条线。
surface.drawLines(points, paint)
按连续点对批量画线。
points 格式
[x1, y1, x2, y2, x3, y3, x4, y4]
它会按 (x1,y1)->(x2,y2)、(x3,y3)->(x4,y4) 这种成对方式去画。
surface.drawRect(left, top, right, bottom, paint)
按四个坐标画矩形。
surface.drawRect(rect, paint)
按对象或数组画矩形。
rect 可填值
| 写法 | 说明 |
|---|---|
Rect | 直接用 |
RectF | 直接用 |
[left, top, right, bottom] | 自动转 RectF |
surface.drawOval(oval, paint)
在一个 RectF 包围盒里画椭圆。
oval 可填值
和 drawRect(rect, paint) 一样,支持:
RectRectF[left, top, right, bottom]
surface.drawCircle(cx, cy, radius, paint)
画圆。
surface.drawArc(oval, startAngle, sweepAngle, useCenter, paint)
画弧形。
参数说明
| 参数 | 说明 |
|---|---|
oval | 弧形所在包围盒 |
startAngle | 起始角度 |
sweepAngle | 扫过角度 |
useCenter | 是否连到圆心形成扇形 |
surface.drawRoundRect(rect, rx, ry, paint)
画圆角矩形。
surface.drawPath(path, paint)
画路径。
surface.drawBitmap(bitmap, left, top, paint?)
把另一张位图画到当前画布上。
bitmap 可填值
| 可填值 | 说明 |
|---|---|
Bitmap | 直接使用 |
Image | 会取内部位图 |
surface | 会取这个画布自己的底层位图 |
示例
const overlay = canvas.createBitmap(20, 20);
const overlaySurface = canvas.wrap(overlay);
overlaySurface.drawColor("#ff00ff");
surface.drawBitmap(overlay, 60, 48);
surface.drawPicture(picture)
绘制 Picture。
surface.drawText(text, x, y, paint)
画文字。
surface.drawTextOnPath(text, path, hOffset, vOffset, paint)
沿路径画文字。
surface.translate(dx, dy)
平移画布坐标系。
surface.scale(sx, sy)
按原点缩放。
surface.scale(sx, sy, px, py)
以指定支点缩放。
surface.rotate(degrees)
按原点旋转。
surface.rotate(degrees, px, py)
以指定支点旋转。
surface.skew(sx, sy)
斜切变换。
surface.save()
把当前画布状态压栈。
返回值
number
也就是一个 saveCount,之后可以交给 restoreToCount(...)。
surface.restore()
恢复最近一次 save() 的状态。
surface.restoreToCount(saveCount)
直接恢复到指定栈层。
surface.concat(matrix)
把一个 Matrix 乘到当前画布变换里。
示例
const matrix = canvas.newMatrix();
matrix.postTranslate(4, 2);
const saveCount = surface.save();
surface.concat(matrix);
surface.drawRect(0, 60, 6, 66, paint);
surface.restoreToCount(saveCount);
一段完整例子
const surface = canvas.create(90, 70, "ARGB_8888");
const fillPaint = canvas.newPaint();
fillPaint.setStyle(canvas.Paint.Style.FILL);
fillPaint.setARGB(255, 255, 0, 0);
const bluePaint = canvas.newPaint();
bluePaint.setStyle(canvas.Paint.Style.FILL);
bluePaint.setARGB(255, 0, 0, 255);
surface.drawColor("#ffffff");
surface.drawRect(6, 6, 24, 24, fillPaint);
surface.drawCircle(42, 14, 8, bluePaint);
const saveCount = surface.save();
surface.translate(20, 34);
surface.drawRect(0, 0, 8, 8, fillPaint);
surface.restoreToCount(saveCount);
log(surface.toString());
最后一个容易忽略的点
canvas 这一页里的很多对象其实都是 Android 原生类:
PaintPathRectRectFMatrixBitmap
所以你在脚本里看到很多示例会直接继续调:
paint.setStyle(...)
path.moveTo(...)
matrix.postTranslate(...)
这不是文档偷懒,而是当前实现本来就把原生类直接暴露出来了。ScriptX 负责的是“给你一个更顺手的入口”,不是再把整套 Android 图形 API 全部重新包一遍。
