如何在 html canvas 中绘制更细但更清晰的线条?
How do I draw thin but sharper lines in html canvas?
我有以下 javascript 代码来绘制图形 sheet。但问题是当我打印输出时,细线看起来并不清晰。当您缩放 html 页面时,问题就很明显了。我想让线条更清晰。但是宽度应该是一样的。可能吗?请帮忙。
function drawBkg(canvasElem, squareSize, minorLineWidthStr, lineColStr)
{
var nLinesDone = 0;
var i, curX, curY;
var ctx = canvasElem.getContext('2d');
ctx.clearRect(0,0,canvasElem.width,canvasElem.height);
// draw the vertical lines
curX=0;
ctx.strokeStyle = lineColStr;
while (curX < canvasElem.width)
{
if (nLinesDone % 5 == 0)
ctx.lineWidth = 0.7;
else
ctx.lineWidth = minorLineWidthStr;
ctx.beginPath();
ctx.moveTo(curX, 0);
ctx.lineTo(curX, canvasElem.height);
ctx.stroke();
curX += squareSize;
nLinesDone++;
}
// draw the horizontal lines
curY=0;
nLinesDone = 0;
while (curY < canvasElem.height)
{
if (nLinesDone % 5 == 0)
ctx.lineWidth = 0.7;
else
ctx.lineWidth = minorLineWidthStr;
ctx.beginPath();
ctx.moveTo(0, curY);
ctx.lineTo(canvasElem.width, curY);
ctx.stroke();
curY += squareSize;
nLinesDone++;
}
}
drawBkg(byId('canvas'), 3.78, "0.35", "green");
您遇到的是您的屏幕 PPI and your printer's DPI。
Canvas输出的是光栅图,如果你设置它的大小为96px,分辨率为96ppi的显示器会输出为一英寸大的图像,但是300ppi的打印机会输出它作为 3.125 英寸的图像。
这样做时,打印操作将缩减图像的采样率,使其适合这个新尺寸。 (每个像素将相乘,因此它覆盖更大的区域)。
但是 canvas context2d 有一个 scale()
method,所以如果你所有的绘图都是基于矢量的1,你可以:
- 打印前创建一个更大的 canvas,
- 将其上下文的比例设置为所需的因子,
- 调用与较小 canvas
相同的绘图
- 如果您直接从浏览器的 "print the page" 打印,请将较大的 canvas
style.width
和 style.height
属性设置为 width
和 height
较小的属性,
- 将较小的 canvas 节点替换为较大的节点,
- 打印,
- 把大的canvas换成原来的
为此,您需要稍微重写您的函数,这样它就不会将传递的 canvas' width/height 作为值,而是您选择的值。
function drawBkg(ctx, width, height, squareSize, minorLineWidthStr, lineColStr) {
var nLinesDone = 0;
var i, curX, curY;
ctx.clearRect(0, 0, width, height);
// draw the vertical lines
curX = 0;
ctx.strokeStyle = lineColStr;
while (curX < width) {
if (nLinesDone % 5 == 0)
ctx.lineWidth = 0.7;
else
ctx.lineWidth = minorLineWidthStr;
ctx.beginPath();
ctx.moveTo(curX, 0);
ctx.lineTo(curX, height);
ctx.stroke();
curX += squareSize;
nLinesDone++;
}
// draw the horizontal lines
curY = 0;
nLinesDone = 0;
while (curY < height) {
if (nLinesDone % 5 == 0)
ctx.lineWidth = 0.7;
else
ctx.lineWidth = minorLineWidthStr;
ctx.beginPath();
ctx.moveTo(0, curY);
ctx.lineTo(width, curY);
ctx.stroke();
curY += squareSize;
nLinesDone++;
}
}
// your drawings
var smallCanvas = document.getElementById('smallCanvas');
var smallCtx = smallCanvas.getContext('2d');
drawBkg(smallCtx, smallCanvas.width, smallCanvas.height, 3.78, "0.35", "green");
// a function to get the screen's ppi
function getPPI() {
var test = document.createElement('div');
test.style.width = "1in";
test.style.height = 0;
document.body.appendChild(test);
var dpi = devicePixelRatio || 1;
var ppi = parseInt(getComputedStyle(test).width) * dpi;
document.body.removeChild(test);
return ppi;
}
function scaleAndPrint(outputDPI) {
var factor = outputDPI / getPPI();
var bigCanvas = smallCanvas.cloneNode();
// set the required size of our "printer version" canvas
bigCanvas.width = smallCanvas.width * factor;
bigCanvas.height = smallCanvas.height * factor;
// set the display size the same as the original one to don't brake the page's layout
var rect = smallCanvas.getBoundingClientRect();
bigCanvas.style.width = rect.width + 'px';
bigCanvas.style.height = rect.height + 'px';
var bigCtx = bigCanvas.getContext('2d');
// change the scale of our big context
bigCtx.scale(factor, factor);
// tell the function we want the height and width of the small canvas
drawBkg(bigCtx, smallCanvas.width, smallCanvas.height, 3.78, "0.35", "green");
// replace our original canvas with the bigger one
smallCanvas.parentNode.replaceChild(bigCanvas, smallCanvas);
// call the printer
print();
// set the original one back
bigCanvas.parentNode.replaceChild(smallCanvas, bigCanvas);
}
btn_o.onclick = function() { print(); };
btn_s.onclick = function() { scaleAndPrint(300);};
<button id="btn_o">print without scaling</button>
<button id="btn_s">print with scaling</button>
<br>
<canvas id="smallCanvas" width="250" height="500"></canvas>
1. canvas 上的所有绘图操作都是基于矢量的,除了 drawImage()
和 putImageData()
实现更清晰线条的最简单方法是使用过采样:您绘制分辨率大于屏幕分辨率的 canvas。
在 Javascript 中,如果您想按 X 倍进行过采样:
- 将canvas的宽度和高度更改为
width*X
和height*X
- 将 canvas 的上下文缩放 X
倍
- 将 Css 宽度和高度固定为初始宽度和高度,以在屏幕上保持相同的尺寸。
在下面的示例中,我首先 向下 采样了 canvas 以便于查看。您必须放大很多才能看到无上采样、2 倍和 4 倍之间的区别。
function overSampleCanvas(tgtCanvas, ctx, factor) {
var width = tgtCanvas.width;
var height = tgtCanvas.height;
tgtCanvas.width = 0 | (width * factor);
tgtCanvas.height = 0 | (height * factor);
tgtCanvas.style.width = width + 'px';
tgtCanvas.style.height = height + 'px';
ctx.scale(factor, factor);
}
// -------------------- example
var $ = document.getElementById.bind(document);
var cv05 = $('cv05'),
ctx05 = cv05.getContext('2d');
var cv = $('cv'),
ctx = cv.getContext('2d');
var cv2X = $('cv2X'),
ctx2X = cv2X.getContext('2d');
var cv4X = $('cv4X'),
ctx4X = cv4X.getContext('2d');
overSampleCanvas(cv05, ctx05, 0.5);
overSampleCanvas(cv2X, ctx2X, 2);
overSampleCanvas(cv4X, ctx4X, 4);
function drawCircle(ctx) {
ctx.beginPath();
ctx.arc(100, 100, 50, 0, 6.28);
ctx.fillStyle = '#AB6';
ctx.fill();
}
drawCircle(ctx05);
drawCircle(ctx);
drawCircle(ctx2X);
drawCircle(ctx4X);
canvas downsampled by 2X, normal, then upsampled by 2X, then 4X. <br>
<canvas id="cv05" width="100" height="100"></canvas>
<canvas id="cv" width="100" height="100"></canvas>
<canvas id="cv2X" width="100" height="100"></canvas>
<canvas id="cv4X" width="100" height="100"></canvas>
我有以下 javascript 代码来绘制图形 sheet。但问题是当我打印输出时,细线看起来并不清晰。当您缩放 html 页面时,问题就很明显了。我想让线条更清晰。但是宽度应该是一样的。可能吗?请帮忙。
function drawBkg(canvasElem, squareSize, minorLineWidthStr, lineColStr)
{
var nLinesDone = 0;
var i, curX, curY;
var ctx = canvasElem.getContext('2d');
ctx.clearRect(0,0,canvasElem.width,canvasElem.height);
// draw the vertical lines
curX=0;
ctx.strokeStyle = lineColStr;
while (curX < canvasElem.width)
{
if (nLinesDone % 5 == 0)
ctx.lineWidth = 0.7;
else
ctx.lineWidth = minorLineWidthStr;
ctx.beginPath();
ctx.moveTo(curX, 0);
ctx.lineTo(curX, canvasElem.height);
ctx.stroke();
curX += squareSize;
nLinesDone++;
}
// draw the horizontal lines
curY=0;
nLinesDone = 0;
while (curY < canvasElem.height)
{
if (nLinesDone % 5 == 0)
ctx.lineWidth = 0.7;
else
ctx.lineWidth = minorLineWidthStr;
ctx.beginPath();
ctx.moveTo(0, curY);
ctx.lineTo(canvasElem.width, curY);
ctx.stroke();
curY += squareSize;
nLinesDone++;
}
}
drawBkg(byId('canvas'), 3.78, "0.35", "green");
您遇到的是您的屏幕 PPI and your printer's DPI。
Canvas输出的是光栅图,如果你设置它的大小为96px,分辨率为96ppi的显示器会输出为一英寸大的图像,但是300ppi的打印机会输出它作为 3.125 英寸的图像。
这样做时,打印操作将缩减图像的采样率,使其适合这个新尺寸。 (每个像素将相乘,因此它覆盖更大的区域)。
但是 canvas context2d 有一个 scale()
method,所以如果你所有的绘图都是基于矢量的1,你可以:
- 打印前创建一个更大的 canvas,
- 将其上下文的比例设置为所需的因子,
- 调用与较小 canvas 相同的绘图
- 如果您直接从浏览器的 "print the page" 打印,请将较大的 canvas
style.width
和style.height
属性设置为width
和height
较小的属性, - 将较小的 canvas 节点替换为较大的节点,
- 打印,
- 把大的canvas换成原来的
为此,您需要稍微重写您的函数,这样它就不会将传递的 canvas' width/height 作为值,而是您选择的值。
function drawBkg(ctx, width, height, squareSize, minorLineWidthStr, lineColStr) {
var nLinesDone = 0;
var i, curX, curY;
ctx.clearRect(0, 0, width, height);
// draw the vertical lines
curX = 0;
ctx.strokeStyle = lineColStr;
while (curX < width) {
if (nLinesDone % 5 == 0)
ctx.lineWidth = 0.7;
else
ctx.lineWidth = minorLineWidthStr;
ctx.beginPath();
ctx.moveTo(curX, 0);
ctx.lineTo(curX, height);
ctx.stroke();
curX += squareSize;
nLinesDone++;
}
// draw the horizontal lines
curY = 0;
nLinesDone = 0;
while (curY < height) {
if (nLinesDone % 5 == 0)
ctx.lineWidth = 0.7;
else
ctx.lineWidth = minorLineWidthStr;
ctx.beginPath();
ctx.moveTo(0, curY);
ctx.lineTo(width, curY);
ctx.stroke();
curY += squareSize;
nLinesDone++;
}
}
// your drawings
var smallCanvas = document.getElementById('smallCanvas');
var smallCtx = smallCanvas.getContext('2d');
drawBkg(smallCtx, smallCanvas.width, smallCanvas.height, 3.78, "0.35", "green");
// a function to get the screen's ppi
function getPPI() {
var test = document.createElement('div');
test.style.width = "1in";
test.style.height = 0;
document.body.appendChild(test);
var dpi = devicePixelRatio || 1;
var ppi = parseInt(getComputedStyle(test).width) * dpi;
document.body.removeChild(test);
return ppi;
}
function scaleAndPrint(outputDPI) {
var factor = outputDPI / getPPI();
var bigCanvas = smallCanvas.cloneNode();
// set the required size of our "printer version" canvas
bigCanvas.width = smallCanvas.width * factor;
bigCanvas.height = smallCanvas.height * factor;
// set the display size the same as the original one to don't brake the page's layout
var rect = smallCanvas.getBoundingClientRect();
bigCanvas.style.width = rect.width + 'px';
bigCanvas.style.height = rect.height + 'px';
var bigCtx = bigCanvas.getContext('2d');
// change the scale of our big context
bigCtx.scale(factor, factor);
// tell the function we want the height and width of the small canvas
drawBkg(bigCtx, smallCanvas.width, smallCanvas.height, 3.78, "0.35", "green");
// replace our original canvas with the bigger one
smallCanvas.parentNode.replaceChild(bigCanvas, smallCanvas);
// call the printer
print();
// set the original one back
bigCanvas.parentNode.replaceChild(smallCanvas, bigCanvas);
}
btn_o.onclick = function() { print(); };
btn_s.onclick = function() { scaleAndPrint(300);};
<button id="btn_o">print without scaling</button>
<button id="btn_s">print with scaling</button>
<br>
<canvas id="smallCanvas" width="250" height="500"></canvas>
1. canvas 上的所有绘图操作都是基于矢量的,除了 drawImage()
和 putImageData()
实现更清晰线条的最简单方法是使用过采样:您绘制分辨率大于屏幕分辨率的 canvas。
在 Javascript 中,如果您想按 X 倍进行过采样:
- 将canvas的宽度和高度更改为
width*X
和height*X
- 将 canvas 的上下文缩放 X 倍
- 将 Css 宽度和高度固定为初始宽度和高度,以在屏幕上保持相同的尺寸。
在下面的示例中,我首先 向下 采样了 canvas 以便于查看。您必须放大很多才能看到无上采样、2 倍和 4 倍之间的区别。
function overSampleCanvas(tgtCanvas, ctx, factor) {
var width = tgtCanvas.width;
var height = tgtCanvas.height;
tgtCanvas.width = 0 | (width * factor);
tgtCanvas.height = 0 | (height * factor);
tgtCanvas.style.width = width + 'px';
tgtCanvas.style.height = height + 'px';
ctx.scale(factor, factor);
}
// -------------------- example
var $ = document.getElementById.bind(document);
var cv05 = $('cv05'),
ctx05 = cv05.getContext('2d');
var cv = $('cv'),
ctx = cv.getContext('2d');
var cv2X = $('cv2X'),
ctx2X = cv2X.getContext('2d');
var cv4X = $('cv4X'),
ctx4X = cv4X.getContext('2d');
overSampleCanvas(cv05, ctx05, 0.5);
overSampleCanvas(cv2X, ctx2X, 2);
overSampleCanvas(cv4X, ctx4X, 4);
function drawCircle(ctx) {
ctx.beginPath();
ctx.arc(100, 100, 50, 0, 6.28);
ctx.fillStyle = '#AB6';
ctx.fill();
}
drawCircle(ctx05);
drawCircle(ctx);
drawCircle(ctx2X);
drawCircle(ctx4X);
canvas downsampled by 2X, normal, then upsampled by 2X, then 4X. <br>
<canvas id="cv05" width="100" height="100"></canvas>
<canvas id="cv" width="100" height="100"></canvas>
<canvas id="cv2X" width="100" height="100"></canvas>
<canvas id="cv4X" width="100" height="100"></canvas>