canvas绘图不清晰的解决方案
canvas 作图经常会遇到canvas绘制的图片模糊不清问题,这种问题实际上是画布尺寸与画布范围内实际像素不一致造成的。了解 dpr 可以使用window.matchMedia()
检查devicePixelRatio
的值是否发生更改(例如,如果用户将窗口拖动到带有不同的像素密度的屏幕)
dpr
Window
接口的devicePixelRatio
返回当前显示设备的物理像素分辨率与CSS像素分辨率之比。 此值也可以解释为像素大小的比率:一个CSS像素的大小与一个物理像素的大小。 简单来说,它告诉浏览器应使用多少屏幕实际像素来绘制单个CSS像素。
语法: value = window.devicePixelRatio;
原因分析
假设dpr = 2;图片大小为60x60px;
1.DOM呈现图片过程
图片——》浏览器css像素(显示尺寸)——》屏幕实际像素
60x60 ——》 30x30 ——》 60x60
图片像素——》实际像素
1: 1
2.canvas绘制过程
图片像素——》canvas像素(画布尺寸)——》css像素(显示尺寸)——》屏幕实际像素
60x60 ——》 30x30 ——》 30x30 ——》 60x60
图片像素——》画布像素——》实际像素
4: 1: 4
也就是说,canvas的绘制过程中图片到画布的过程中进行了像素的抽稀,画布到屏幕像素时又进行了插值,所以造成图片质量下降。
解决方案
除了以下解决方法,还可以使用CanvasRenderingContext2D.scale()方法,把canvas的上下文同比放大,这样我们在绘制的时候就不需要再次对坐标进行处理,直接按照原始的值绘制即可。
function setupCanvas2(width, height){
// create canvas
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
canvas.style.width = `${width}px`
canvas.style.height = `${height}px`
document.querySelector('.container').appendChild(canvas)
// scale canvas
const dpr = window.devicePixelRatio || 1
canvas.width = width * dpr
canvas.height = height * dpr
ctx.scale(dpr, dpr)
return {
canvas,
ctx
}
}
放大画布的尺寸,但是canvas显示尺寸不变;
图片像素——》canvas像素(画布尺寸)——》css像素(显示尺寸)——》屏幕实际像素
60x60 ——》 60x60 ——》 30x30 ——》 60x60
图片像素 ——》 实际像素
1: ——》 1
而canvas的设计的时候正好有对象的属性来分别管理画布尺寸和显示尺寸;canvas的width、height属性用于管理画布尺寸;canvas的style属性中的width、height正好是显示尺寸。
也就是说解决方案就是设置画布尺寸与实际像素的一致,显示尺寸为正常显示尺寸;
假设canvas的显示尺寸为窗口宽度,创建canvas的时候指定canvas的width属性为2 * body.clientHeight;style.width.clientHeight + 'px'
;
// 计算设备的dpr
function setupCanvas(canvas){
const dpr = window.devicePixelRatio || 1
const rect = canvas.getBoundingClientRect()
canvas.width = rect.width * dpr
canvas.height = rect.height * dpr
canvas.style.width = rect.width + 'px'
canvas.style.height = rect.height + 'px'
return canvas
}
File、Blob、dataURL 和 canvas 的应用与转换
1. File
(1) 通常情况下, File
对象是来自用户在一个 input 元素上选择文件后返回的 FileList 对象,也可以是来自由拖放操作生成的 DataTransfer
对象,或者来自 HTMLCanvasElement
上的 mozGetAsFile()
API。
(2) File
对象是特殊类型的 Blob
,且可以用在任意的 Blob 类型的 context
中。比如:FileReader
, URL.createObjectURL()
, createImageBitmap()
, 及 XMLHttpRequest.send()
都能处理 Blob
和 File
2. Blob
(1) Blob 对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成 ReadableStream
来用于数据操作。
(2) Blob 表示的不一定是JavaScript原生格式的数据。File 接口基于Blob,继承了 blob 的功能并将其扩展使其支持用户系统上的文件。
3. dataURL
(1) Data URLs
,即前缀为 data: 协议的URL,其允许内容创建者向文档中嵌入小文件。
(2) Data URLs
由四个部分组成:前缀(data:)、指示数据类型的MIME类型、如果非文本则为可选的base64标记、数据本身:data:[][;base64]
, 比如一张 png 格式图片,转化为 base64
字符串形式:data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAgAElEQVR4XuxdB5g
4. canvas
(1) Canvas API
提供了一个通过JavaScript 和 HTML的 canvas 元素来绘制图形的方式。它可以用于动画、游戏画面、数据可视化、图片编辑以及实时视频处理等方面。
文件格式转换关系图
相互转化
- File、Blob 转化成 dataURL
FileReader 对象允许 Web 应用程序异步读取文件(或原始数据缓冲区)内容,使用 File 或 Blob 对象指定要读取的文件或数据。
function fileToDataURL(file) {
let reader = new FileReader()
reader.readAsDataURL(file)
// reader 读取文件成功的回调
reader.onload = function(e) {
return reader.result
}
}
dataURL(base64)
转化成 Blob(二进制)对象
function dataURLToBlob(fileDataURL) {
let arr = fileDataURL.split(','),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while(n --) {
u8arr[n] = bstr.charCodeAt(n)
}
return new Blob([u8arr], {type: mime})
}
- File, Blob 文件数据绘制到 canvas
思路:File, Blob ——> dataURL ——> canvas
function fileAndBlobToCanvas(fileDataURL) {
let img = new Image()
img.src = fileDataURL
let canvas = document.createElement('canvas')
if(!canvas.getContext) {
alert('浏览器不支持canvas')
return;
}
let ctx = canvas.getContext('2d')
document.getElementById('container').appendChild(canvas)
img.onload = function() {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
}
}
- 从 canvas 中获取文件 dataURL
function canvasToDataURL() {
let canvas = document.createElement('canvas')
let canvasDataURL = canvas.toDataURL('image/png', 1.0)
return canvasDataURL
}
- createObjectURL: 直接用 URL 对象,引用保存在 File 和 Blob 中数据的 URL。使用对象 URL 的好处是可以不必把文件内容读取到 JavaScript 中 而直接使用文件内容。为此,只要在需要文件内容的地方提供对象 URL 即可。
function file2Image(file, callback) {
var image = new Image();
var URL = window.webkitURL || window.URL;
if (URL) {
var url = URL.createObjectURL(file);
image.onload = function() {
callback(image);
window.revokeObjectURL(url);
};
image.src = url;
} else {
image.onload = function() {
callback(image);
}
image.src = fileToDataURL(file)
}
}
svg和canvas绘图的区别
SVG
- 一种使用XML描述的2D图形的语言
- SVG基于XML意味着,SVG DOM中的每个元素都是可用的,可以为某个元素附加Javascript事件处理器。
- 在 SVG 中,每个被绘制的图形均被视为对象。如果 SVG 对象的属性发生变化,那么浏览器能够自动重现图形。
Canvas
- 依赖分辨率, 可以说是位图
- 不支持事件处理器 Canvas 绘制的图像 都在Canvas这个画布里面,是Canvas的一部分,不能用js获取已经绘制好的图形元素。
Canvas就像动画,每次显示全部的一帧的内容,想改变里面某个元素的位置或者变化需要在下一帧中全部重新显示。
而SVG绘图时,每个图形都是以DOM节点的形式插入到页面中,可以用js或其他方法直接操作
- 弱的文本渲染能力
- 能够以 .png 或 .jpg 格式保存结果图像
- 最适合图像密集型的游戏,其中的许多对象会被频繁重绘
SVG
- 不依赖分辨率. 绘制的是矢量图
- 支持事件处理器
- 最适合带有大型渲染区域的应用程序(比如谷歌地图)
- 复杂度高会减慢渲染速度(任何过度使用 DOM 的应用都不快)
- 不适合游戏应用
Canvas 和 SVG 的工作机制不同
Canvas是逐像素进行渲染的,一旦图形绘制完成,就不会继续被浏览器关注。而SVG是通过DOM操作来显示的。
所以Canvas的文本渲染能力弱,而SVG最适合带有大型渲染区域的应用程序,比如地图。
而Canvas 最适合有许多对象要被频繁重绘的图形密集型游戏。
而SVG由于DOM操作 在复杂度高的游戏应用中 会减慢渲染速度。所以不适合在游戏应用。