基于PC版《狂热运输》游戏开发的网页版游戏,初期实现城市搭建项目地址。
画布功能
- canvas绘制坐标点,方便图标定位
- 鼠标滚轮控制画布“无损”放大缩小
功能实现
canvas绘制坐标点
固定宽高,创建白色背景空白画布
// 获取canvas
const canvasScene = document.getElementById('canvasScene');
// canvasWidth:画布宽度,canvasHeight:画布高度,arcInterval:点间隔,arcSize:圆半径
const canvasWidth = 1280, canvasHeight = 640,arcInterval= 10, arcSize = 1;
// 设置画布尺寸 固定在1280*640
canvasScene.width = canvasWidth;
canvasScene.height = canvasHeight;
let ctx = canvasScene.getContext("2d");
// 画布背景色白色
ctx.fillStyle = "white";
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
逐行绘制坐标点,点间隔为10,横向128个点,纵向64个点。
每个点重新开始一条新路径,先绘制线再填充颜色,每隔3个点增大圆点尺寸。
for (let h = 0; h <= canvasHeight / arcInterval; h++) {
for (let w = 0; w <= canvasWidth / arcInterval; w++) {
draw((w * arcInterval), (h * arcInterval), arcSize, arcInterval);
}
}
// x:x轴坐标 y:y轴坐标 r:圆半径 i:点间隔
const draw = (x, y, r, i) => {
i = i * 4;
// 每个点重新开始一条新路径
ctx.beginPath();
if (x % i == 0 && y % i == 0) {
r = r * 2
}
ctx.arc(x, y, r, 0, 2 * Math.PI, false);
ctx.strokeStyle = "#d3d3d3";
ctx.stroke();
ctx.fillStyle = "#d3d3d3";
ctx.fill();
ctx.closePath();
}
最终实现效果
鼠标滚轮控制缩放
在父元素上加滚轮监听事件
const setWheel = () => {
let element = document.getElementById("scene");
let scale = 1;
element.addEventListener('wheel', function (event) {
event.preventDefault();
let delta = Math.max(-1, Math.min(1, (event.wheelDelta || -event.detail)));
// 判断滚动方向 大于0向上滚动 小于0向下滚动
if (delta > 0) {
scale += 0.25;
} else {
scale -= 0.25;
}
// 数值范围控制
if (scale > 5) {
scale = 5
} else if (scale < 1) {
scale = 1
}
element.style.transform = 'scale(' + scale + ')';
});
}
但是放大后的实际效果,圆点模糊。
于是另辟蹊径,将
canvasWidth:画布宽度,canvasHeight:画布高度,arcInterval:点间隔,arcSize:圆半径全部放大10倍。
// canvasWidth:画布宽度,canvasHeight:画布高度,arcInterval:点间隔,arcSize:圆半径
const canvasWidth = 12800, canvasHeight = 6400,arcInterval= 100, arcSize = 10;
Canvas绘制性能问题
但是这里会有性能问题,随着画布逐渐扩大,绘制的点会更多,为防止阻塞主线程,使用worker在子线程中进行绘制。
// 获取canvas的控制权
const offscreen = document.querySelector('#canvasScene').transferControlToOffscreen();
const canvasWidth = 1280, canvasHeight = 640,arcInterval= 10;
const worker = new Worker('/public/unit/worker.js');
// 传递参数并且移交canvas的所有权给子线程
worker.postMessage({msg: 'init', canvas: offscreen, data: {canvasWidth, canvasHeight, arcInterval, arcSize}}, [offscreen]);
worker.onmessage = (e) => {
console.log(e, '绘制完成!');
}
参考这篇文章,使用“Control模式”,将Canvas的所有权交给worker创建的子线程来进行绘制渲染操作。
let offscreen, ctx;
onmessage = (e) => {
if (e.data.msg == 'init') {
let canvasInfo = e.data.data;
let canvas = e.data.canvas;
setArcList(canvas, canvasInfo);
}
}
const setArcList = (canvas, {canvasWidth, canvasHeight, arcSize, arcInterval}) => {
canvas.width = canvasWidth;
canvas.height = canvasHeight;
ctx = canvas.getContext("2d");
ctx.fillStyle = "white";
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
for (let h = 0; h <= canvasHeight / arcInterval; h++) {
for (let w = 0; w <= canvasWidth / arcInterval; w++) {
draw((w * arcInterval), (h * arcInterval), arcSize, arcInterval);
}
}
postMessage({}, []);
}
const draw = (x, y, r, i) => {
i = i * 4;
ctx.beginPath();
- if (x % i == 0 && y % i == 0) {
r = r * 2
}
ctx.arc(x, y, r, 0, 2 * Math.PI, false);
ctx.strokeStyle = "#d3d3d3";
ctx.stroke();
ctx.fillStyle = "#d3d3d3";
ctx.fill();
ctx.closePath();
}
至此,一个可放大的矩阵点画布绘制完成。后续进行其他功能开发。