Я могу предложить два способа решения вашей проблемы.Во-первых, используйте ctx.createImageData (w, h) для создания объекта imageData, который содержит массив растровых изображений (ImageData.data, это Uint8ClampedArray). Как только вы закончите манипулировать данными, его можно поместить на холст с помощью ctx.putImageData (ImageData, 0,0).
Или вы можете использовать решение на основе WebGL, чтобы нарисовать свои линии для вас.(Если вы хотите отключить сглаживание для получения пиксельных линий, контекст gl просто необходимо создать с отключенным сглаживанием).
Использование WebGL предпочтительнее, так как любое решение, написанное в JS на данный момент, может реально работать только на одномпиксель за раз (Web Workers с буфером общего массива могут предоставить вам параллельный многопоточный JS, но он был отключен во всех браузерах в начале этого года).
ниже - модуль на базе WebGL, который можетиспользоваться для быстрого рисования линий различной толщины и цвета.
(Для проверки скорости фрагмент ниже рисует 10000 линий).
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
background-color: black;
}
canvas {
display: block;
margin-top: 30px;
margin-left: auto;
margin-right: auto;
border: solid 1px white;
border-radius: 10px;
width: 180px;
height: 160px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="application/javascript">
var glLine = function() {
"use strict";
var width = 1;
var height = 1;
var lineWidth = 1;
var tmpBuffer = new Float32Array(12);
var canvas = document.createElement("canvas");
var gl = canvas.getContext("webgl",{antialias: false,preserveDrawingBuffer: true});
gl.clearColor(0.0,0.0,0.0,0.0);
var buffer = function() {
var b = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,b);
gl.bufferData(gl.ARRAY_BUFFER,tmpBuffer,gl.DYNAMIC_DRAW);
}();
var uInvResolution = null;
var uColour = null;
var program = function() {
var vs = gl.createShader(gl.VERTEX_SHADER);
var fs = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(vs,`
precision lowp float;
attribute vec2 aPosition;
uniform vec2 uInvResolution;
void main() {
vec2 vPosition = vec2(
aPosition.x * uInvResolution.x * 2.0 - 1.0,
-(aPosition.y * uInvResolution.y * 2.0 - 1.0)
);
gl_Position = vec4(vPosition,0.0,1.0);
}
`);
gl.shaderSource(fs,`
precision lowp float;
uniform vec4 uColour;
void main() {
gl_FragColor = uColour;
}
`);
gl.compileShader(vs);
gl.compileShader(fs);
var p = gl.createProgram();
gl.attachShader(p,vs);
gl.attachShader(p,fs);
gl.linkProgram(p);
gl.deleteShader(vs);
gl.deleteShader(fs);
gl.useProgram(p);
uInvResolution = gl.getUniformLocation(p,"uInvResolution");
uColour = gl.getUniformLocation(p,"uColour");
return p;
}();
gl.vertexAttribPointer(0,2,gl.FLOAT,gl.FALSE,8,0);
gl.enableVertexAttribArray(0);
addEventListener("unload",function() {
gl.deleteBuffer(buffer);
gl.deleteProgram(program);
gl = null;
});
return {
clear: function() {
gl.clear(gl.COLOR_BUFFER_BIT);
},
draw: function(x1,y1,x2,y2) {
var x = x2 - x1;
var y = y2 - y1;
var invL = 1.0 / Math.sqrt(x * x + y * y);
x = x * invL;
y = y * invL;
var hLineWidth = lineWidth * 0.5;
var bl_x = x1 - y * hLineWidth;
var bl_y = y1 + x * hLineWidth;
var br_x = x1 + y * hLineWidth;
var br_y = y1 - x * hLineWidth;
var tl_x = x2 - y * hLineWidth;
var tl_y = y2 + x * hLineWidth;
var tr_x = x2 + y * hLineWidth;
var tr_y = y2 - x * hLineWidth;
tmpBuffer[0] = tr_x;
tmpBuffer[1] = tr_y;
tmpBuffer[2] = bl_x;
tmpBuffer[3] = bl_y;
tmpBuffer[4] = br_x;
tmpBuffer[5] = br_y;
tmpBuffer[6] = tr_x;
tmpBuffer[7] = tr_y;
tmpBuffer[8] = tl_x;
tmpBuffer[9] = tl_y;
tmpBuffer[10] = bl_x;
tmpBuffer[11] = bl_y;
gl.bufferSubData(gl.ARRAY_BUFFER,0,tmpBuffer);
gl.drawArrays(gl.TRIANGLES,0,6);
},
setColour: function(r,g,b,a) {
gl.uniform4f(
uColour,
r * 0.00392156862745098,
g * 0.00392156862745098,
b * 0.00392156862745098,
a * 0.00392156862745098
);
},
setLineWidth: function(width) {
lineWidth = width;
},
setSize: function(_width,_height) {
width = _width;
height = _height;
canvas.width = width;
canvas.height = height;
gl.uniform2f(uInvResolution,1.0 / width,1.0 / height);
gl.viewport(0,0,width,height);
gl.clear(gl.COLOR_BUFFER_BIT);
},
getImage: function() {
return canvas;
}
};
}();
void function() {
"use strict";
var canvasWidth = 180;
var canvasHeight = 160;
var canvas = null;
var ctx = null;
onload = function() {
canvas = document.getElementById("canvas");
canvas.width = canvasWidth;
canvas.height = canvasHeight;
ctx = canvas.getContext("2d");
glLine.setSize(canvasWidth,canvasHeight);
ctx.fillStyle = "gray";
ctx.fillRect(0,0,canvasWidth,canvasHeight);
for (var i = 0, l = 10000; i < l; ++i) {
glLine.setColour(
(Math.random() * 255) | 0,
(Math.random() * 255) | 0,
(Math.random() * 255) | 0,
255
);
glLine.setLineWidth(
3 + (Math.random() * 5) | 0
);
glLine.draw(
Math.random() * canvasWidth,
Math.random() * canvasHeight,
Math.random() * canvasWidth,
Math.random() * canvasHeight
);
}
ctx.drawImage(glLine.getImage(),0,0);
}
}();
</script>
</body>
</html>