持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情
牙叔教程 简单易懂
写教程的起因
有些新手不会用canvas, 一般画线和画框没问题, 但是不会擦除之前绘制的线,
最后屏幕上到处都是线, 到处都是框, 然后抱怨autojs写绘制一堆问题, 气死了, 之类的话
原因呢, 就是对canvas不熟悉, 下面我们来看看正确的绘制方法
绘制的正确流程
要绘制东西, 首先要有数据, 我们来构造数据,
直线
function Line(x1, y1, x2, y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
框
function Rectangle(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
编一些测试数据
第一: 计算屏幕宽高
let dw = device.width;
let dh = device.height;
第二:构造玩家
function Player(x, y, width, height, color) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
}
第三:编写玩家数据
let playerCount = 10;
let players = [];
for (var i = 0; i < playerCount; i++) {
let playerWidth = random(30, 100);
let playerHeight = 2 * playerWidth;
let color = random(0, 1) ? "#ff0000" : "#00ff00";
let player = new Player(
random(0, dw),
random(0, dh),
playerWidth,
playerHeight,
color
);
players.push(player);
}
console.log(players);
生成的假数据
[ { x: 879, y: 200, width: 69, height: 138, color: '#ff0000' },
{ x: 299, y: 313, width: 80, height: 160, color: '#ff0000' },
{ x: 649, y: 507, width: 68, height: 136, color: '#ff0000' },
{ x: 194, y: 83, width: 65, height: 130, color: '#00ff00' },
{ x: 677, y: 76, width: 63, height: 126, color: '#00ff00' },
{ x: 573, y: 420, width: 40, height: 80, color: '#00ff00' },
{ x: 781, y: 429, width: 33, height: 66, color: '#00ff00' },
{ x: 309, y: 34, width: 77, height: 154, color: '#ff0000' },
{ x: 177, y: 418, width: 95, height: 190, color: '#00ff00' },
{ x: 468, y: 265, width: 36, height: 72, color: '#00ff00' } ]
绘制数据
绘制的时候不能干扰屏幕操作, 所以要用悬浮窗;
绘制是全屏的, 有可能通知栏会影响高度, 这个根据实际情况调整, 我们只讲绘制的主要流程;
第一步:创建全屏悬浮窗画板
第一步: 创建悬浮窗
let w = floaty.rawWindow(
<vertical id="root" bg="#ff0000" gravity="center">
<button textSize="30sp">公众号: 牙叔教程</button>
</vertical>
);
w.setSize(-1, -1);
setInterval(() => {}, 1000);
第二步:确认全屏以后, 把button改成canvas
let w = floaty.rawWindow(
<vertical id="root" bg="#ff0000" gravity="center">
<canvas id="board"></canvas>
</vertical>
);
第三步:添加draw事件监听
w.board.on("draw", (canvas) => {
canvas.drawColor(colors.parseColor("#0000ff"));
});
现在画板是全屏的了
第二步:绘制玩家
让玩家自己绘制吧, 给player添加一个draw方法
Player.prototype.draw = function (canvas) {
let paint = new android.graphics.Paint();
paint.setColor(this.color);
paint.setStyle(android.graphics.Paint.Style.FILL);
canvas.drawRect(
this.x,
this.y,
this.x + this.width,
this.y + this.height,
paint
);
};
这个 每次draw都创建一个画笔paint, 显然太浪费了, paint我们作为全局变量,
let paint = new android.graphics.Paint();
paint.setStyle(android.graphics.Paint.Style.Stroke);
Player.prototype.draw = function (canvas) {
paint.setColor(this.color);
canvas.drawRect(
this.x,
this.y,
this.x + this.width,
this.y + this.height,
paint
);
};
这个只是画了一个矩形, 我们再把线也画上
Player.prototype.draw = function (canvas) {
paint.setColor(this.color);
canvas.drawRect(
this.x,
this.y,
this.x + this.width,
this.y + this.height,
paint
);
// 直线一律以屏幕顶部中心为起点
canvas.drawLine(halfDw, 0, this.x + this.width / 2, this.y, paint);
};
很好, 看起来差不多了,我们现在就去绘制玩家
w.board.on("draw", (canvas) => {
// canvas.drawColor(colors.parseColor("#0000ff"));
var len = players.length;
for (var i = 0; i < len; i++) {
let player = players[i];
player.draw(canvas);
}
});
怎么是全红的呢? 不是应该一个框 一个线 吗?
我不理解
为什么全屏都是红色, 而不是框
经过三天两夜的排查, 发现问题是画笔设置颜色有问题;
一般设置画笔颜色是
paint.setColor(this.color);
要改成
paint.setColor(colors.parseColor(this.color));
因为setColor的颜色是整数, 而我们的颜色是字符串, 要转换一下颜色;
这个问题, 在日志中是看不出来的, 因为没有报错, 只能靠猜;
改完颜色, 测试一下, 绘制正常
\
绘制所有玩家
看上去像是那么回事了, 线条有点粗, 改细一点
paint.setStrokeWidth(3);
定时刷新数据
游戏中的数据一直在变动, 我们模拟一下变动,加个定时器修改数据
setInterval(() => {
let newPlayers = [];
for (var i = 0; i < playerCount; i++) {
let playerWidth = random(30, 100);
let playerHeight = 2 * playerWidth;
let color = random(0, 1) ? "#ff0000" : "#00ff00";
let player = new Player(
random(0, dw),
random(0, dh),
playerWidth,
playerHeight,
color
);
newPlayers.push(player);
}
players = newPlayers;
}, 100);
可以看到, 直线和方框不停的绘制, 一会就啥都看不见了
我们把 清空画板 的命令加上去
可以看到, 现在绘制就正常了
接下来还有一个问题, 数据怎么更新
更新数据
前面我们更新数据使用的是 setInterval ,
这个是定时器, 是不阻塞的, 依靠的是js本身的循环机制去更新数据;
但是一般我们用的是同步的写法, 也就是一步两步三步四步五步, 这样的;
我们来用代码模拟一下同步更新数据的效果
for (var i = 0; i < 10; i++) {
sleep(1000);
log("i = " + i);
let newPlayers = [];
for (var i = 0; i < playerCount; i++) {
let playerWidth = random(30, 100);
let playerHeight = 2 * playerWidth;
let color = random(0, 1) ? "#ff0000" : "#00ff00";
let player = new Player(
random(0, dw),
random(0, dh),
playerWidth,
playerHeight,
color
);
newPlayers.push(player);
}
players = newPlayers;
}
运行日志
06-23 09:58:12.271 Script-61 Main [remote://main.js]/V: 开始运行[remote://main.js]
06-23 09:58:13.290 Script-61 Main [remote://main.js]/D: i = 0
06-23 09:58:13.293 Script-61 Main [remote://main.js]/V:
------------
[remote://main.js]运行结束,用时1.021000秒
我是循环10次,每次1秒钟;
但是日志里面,脚本1秒就结束了;
这是为什么?
我不理解???
经过三天两夜的排查, 发现问题是 变量提升, 因为内外两个循环都使用了 变量 i ;
把外部循环的 i 改成 j , 再次测试, 就没问题了
for (var j = 0; j < 100; j++) {
sleep(100);
let newPlayers = [];
for (var i = 0; i < playerCount; i++) {
let playerWidth = random(30, 100);
let playerHeight = 2 * playerWidth;
let color = random(0, 1) ? "#ff0000" : "#00ff00";
let player = new Player(
random(0, dw),
random(0, dh),
playerWidth,
playerHeight,
color
);
newPlayers.push(player);
}
players = newPlayers;
}
需要多线程吗
我一开始还以为要用多线程, 看来不用, canvas的绘制是跑在UI线程的, 更新数据的for循环是跑在非UI线程的;
他们是不会阻塞对方的
环境
雷电模拟器: 4.0.63
Android版本: 7.1.2
Autojs版本: 8.8.20
名人名言
思路是最重要的, 其他的百度, bing, stackoverflow, github, 安卓文档, autojs文档, 最后才是群里问问 --- 牙叔教程
声明
部分内容来自网络 本教程仅用于学习, 禁止用于其他用途