19

使用 HTML5 画布创建图像时,是否可以设置自定义 DPI/PPI?我知道如何在画布上绘制并将其导出为图像,但如何确保输出图像具有特定的 DPI/PPI。我想使用 SVG 元素在画布上绘制是一种方法,但是当我将整个画布导出为图像时,这不会变平吗?或者计算设备 DPI,然后缩放图像以满足我的 DPI 要求,但这似乎不是正确的解决方案。

4

4 回答 4

63

画布有两种不同的“尺寸”:它们的 DOM 宽度/高度和它们的 CSS 宽度/高度。您可以通过增加 DOM 大小同时保持 CSS 大小固定来增加画布的分辨率,然后使用 .scale() 方法将您未来的所有绘图缩放到新的更大尺寸。这是一个例子:

function changeResolution(canvas, scaleFactor) {
    // Set up CSS size.
    canvas.style.width = canvas.style.width || canvas.width + 'px';
    canvas.style.height = canvas.style.height || canvas.height + 'px';

    // Resize canvas and scale future draws.
    canvas.width = Math.ceil(canvas.width * scaleFactor);
    canvas.height = Math.ceil(canvas.height * scaleFactor);
    var ctx = canvas.getContext('2d');
    ctx.scale(scaleFactor, scaleFactor);
}

画布默认分辨率为 96dpi(CSS 英寸,不基于实际屏幕)。所以 2 的 scaleFactor 给出 192dpi,3 是 288dpi,依此类推。事实上,这里有一个版本应该给出你想要的 DPI:

function setDPI(canvas, dpi) {
    // Set up CSS size.
    canvas.style.width = canvas.style.width || canvas.width + 'px';
    canvas.style.height = canvas.style.height || canvas.height + 'px';

    // Resize canvas and scale future draws.
    var scaleFactor = dpi / 96;
    canvas.width = Math.ceil(canvas.width * scaleFactor);
    canvas.height = Math.ceil(canvas.height * scaleFactor);
    var ctx = canvas.getContext('2d');
    ctx.scale(scaleFactor, scaleFactor);
}

玩得开心!请注意,这两个代码示例每个画布只能使用一次,它们假定当前 DOM 大小是原始大小(可以对其进行调整以更改)。在画布上进行任何绘图之前,还需要进行重新缩放。感谢这篇文章提供的方法和信息!

编辑:这是一个更强大的功能,可以扩展未来的绘图维护现有的画布内容。这可以调用多次重新缩放。

function setDPI(canvas, dpi) {
    // Set up CSS size.
    canvas.style.width = canvas.style.width || canvas.width + 'px';
    canvas.style.height = canvas.style.height || canvas.height + 'px';

    // Get size information.
    var scaleFactor = dpi / 96;
    var width = parseFloat(canvas.style.width);
    var height = parseFloat(canvas.style.height);

    // Backup the canvas contents.
    var oldScale = canvas.width / width;
    var backupScale = scaleFactor / oldScale;
    var backup = canvas.cloneNode(false);
    backup.getContext('2d').drawImage(canvas, 0, 0);

    // Resize the canvas.
    var ctx = canvas.getContext('2d');
    canvas.width = Math.ceil(width * scaleFactor);
    canvas.height = Math.ceil(height * scaleFactor);

    // Redraw the canvas image and scale future draws.
    ctx.setTransform(backupScale, 0, 0, backupScale, 0, 0);
    ctx.drawImage(backup, 0, 0);
    ctx.setTransform(scaleFactor, 0, 0, scaleFactor, 0, 0);
}
于 2014-09-25T20:42:09.657 回答
4

您无法(ugh)在任何浏览器中访问当前网页显示的 DPI:

从 JS/CSS 检测系统 DPI/PPI?

对于打印:您很可能无法<canvas>使用浏览器标准功能设置导出图像(PNG、JPEG)的 DPI。但是,如果您使用纯 Javascript 编码器图像编码器,您可以自由创建任何类型的二进制文件,并手动调整嵌入在二进制文件中的 DPI 值。

https://gist.github.com/1245476

于 2013-01-25T22:20:34.363 回答
2

如果您只想设置 PNG 的 dpi(即不增加像素数),那么此库可让您设置pHYs块(除其他外):

https://github.com/imaya/CanvasTool.PngEncoder

将 HTML5 画布导出为 base64 编码的 PNG 的最小示例:

        // convert dots per inch into dots per metre
        var pixelsPerM = dpi * 100 / 2.54;

        var param = {
            bitDepth : 8,
            colourType : 2,
            filterType : 0,
            height : canvas.height,
            interlaceMethod : 0,
            phys : {
                unit : 1,
                x : pixelsPerM,
                y : pixelsPerM
            },
            width : canvas.width
        };

        var array = canvas.getContext('2d').getImageData(0, 0, canvas.width,
                canvas.height).data;

        var png = new window.CanvasTool.PngEncoder(array, param).convert();

        var base64 = 'data:image/png;base64,' + btoa(png);
于 2017-10-16T12:30:25.200 回答
0

使用库 changedpi:

npm install changedpi --save

另见

还允许为 png 或 jpg 导出调整 px 大小和分辨率的示例代码:

Canvas2Image.saveAsImage('fileName.png', canvas, 2000, 3000, 300, 'png');

-

import Url from './url';
import * as ChangeDpi from 'changeDPI';

export default class Canvas2Image {
  static saveAsImage(fileName, canvas, width, height, dpi, type) {
    type = this._fixType(type);
    canvas = this._scaleCanvas(canvas, width, height);
    let dataUrl = canvas.toDataURL(type);
    let dataUrlWithDpi = ChangeDpi.changeDpiDataUrl(dataUrl, dpi)
    dataUrlWithDpi = dataUrlWithDpi.replace(type, 'image/octet-stream');
    Url.download(fileName, dataUrlWithDpi);
  }

  static _fixType(type) {
    type = type.toLowerCase().replace(/jpg/i, 'jpeg');
    const r = type.match(/png|jpeg|bmp|gif/)[0];
    return `image/${r}`;
  }

  static _scaleCanvas(canvas, width, height) {
    const w = canvas.width;
    const h = canvas.height;
    if (width === undefined) {
      width = w;
    }
    if (height === undefined) {
      height = h;
    }

    const retCanvas = document.createElement('canvas');
    const retCtx = retCanvas.getContext('2d');
    retCanvas.width = width;
    retCanvas.height = height;
    retCtx.drawImage(canvas, 0, 0, w, h, 0, 0, width, height);
    return retCanvas;
  }
}

-

export default class Url {
  static download(fileName, url) {
    const element = document.createElement('a');
    element.setAttribute('href', url);
    element.setAttribute('download', fileName);
    element.style.display = 'none';
    document.body.appendChild(element);
    element.click();
    document.body.removeChild(element);
  }

  static createUrlForBlob(blob) {
    return this._URL.createObjectURL(blob);
  }

  static clearBlobUrl(blobUrl) {
    this._URL.revokeObjectURL(blobUrl);
  }

  static get _URL() {
    return window.URL || window.webkitURL || window;
  }
}
于 2020-05-12T07:03:30.107 回答