Canvas 绘制图片模糊问题?
在日常业务中,总能遇到使用
案例代码
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>canvas</title> <style> .container { display: flex; } </style> </head> <body> <div class="container"> <div> <p>原图:</p> <img style="width: 300px" src="https://c-ssl.duitang.com/uploads/blog/202107/13/20210713121231_46508.jpeg" alt="" /> </div> <div> <p>未优化:</p> <canvas id="canvas1" width="300"></canvas> </div> <div> <p>优化后:</p> <canvas id="canvas2" width="300"></canvas> </div> </div>
<script> function loadImage() { return new Promise((resolve, reject) => { const img = new Image() img.src = 'https://c-ssl.duitang.com/uploads/blog/202107/13/20210713121231_46508.jpeg' img.onload = () => { resolve(img) }
img.onerror = (e) => { reject(e) } }) }
function drawImage(canvas, canvasCtx, img) { const imgScale = img.width / img.height canvas.style.width = canvas.width + 'px' canvas.style.height = canvas.width / imgScale + 'px' canvas.width = canvas.width canvas.height = canvas.width / imgScale canvasCtx.drawImage( img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height, ) }
async function render() { const canvas1 = document.getElementById('canvas1') const ctx1 = canvas1.getContext('2d') const img = await loadImage()
drawImage(canvas1, ctx1, img) }
render() </script> </body></html>
效果图如👇🏻

对比原图,没有优化的
🤔为什么会出现这种情况呢?
在解决前,先了解一下像素问题,已了解可跳过。
像素问题解析
(有错误可邮件我!!
- 物理像素表示屏幕上有多少个发光点(颗粒
) ,逻辑像素(也叫设备独立像素)表示屏幕展示物体的视觉尺寸是多大。 - 为什么需要逻辑像素?因为物理像素仅仅表示像素的个数,并没有规定实际的尺寸,如果说一个矩形要用
10 个物理像素来渲染,那么不同的(物理)像素密度和排列方式展示出来的效果也就不一样(像素密度越大图形越小) ;因此为了保持各设备展示一样产生了逻辑像素。
比如:某屏幕的物理像素是
⭕ 在前端有两个很热门的问题?
- 为什么手机的设计稿大多数是
2 倍图? - 为什么设置
1px 手机看起来比设计稿粗?
第一个问题:因为现在大多数手机的
第二个问题:首先提一个要点,在屏幕没有缩放的情况下,
解决
不少人第一时间应该会想到使用
0.5px 解决,确实在ios 可以解决,但在android 中兼容不太好。
解决思路
canvas
存在一个 backingStorePixelRatio
属性,不过现在已经废弃。转而使用在浏览器 window
对象下的 devicePixelRatio
属性,它表示浏览器的设备像素比,也就是
举例:使用一张
而在style.width
和 style.height
是设置画布的实际渲染大小;canvas.width
和 canvas.height
设置的是画布的大小。
当两者都设定相同数值时,在canvas.width
比作图片像素,canvas style
比作设备像素 就比较好理解了。
所以我们可以通过 devicePixelRatio
属性来动态设置 canvas
画布大小,来实现
🗒️ 完整代码展示
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>canvas</title> <style> .container { display: flex; } </style> </head> <body> <div class="container"> <div> <p>原图:</p> <img style="width: 300px" src="https://c-ssl.duitang.com/uploads/blog/202107/13/20210713121231_46508.jpeg" alt="" /> </div> <div> <p>未优化:</p> <canvas id="canvas1" width="300"></canvas> </div> <div> <p>优化后:</p> <canvas id="canvas2" width="300"></canvas> </div> </div>
<script> function loadImage() { return new Promise((resolve, reject) => { const img = new Image() img.src = 'https://c-ssl.duitang.com/uploads/blog/202107/13/20210713121231_46508.jpeg' img.onload = () => { resolve(img) }
img.onerror = (e) => { reject(e) } }) }
function drawImage(canvas, canvasCtx, img, ratio = 1) { const imgScale = img.width / img.height canvasCtx.scale(ratio, ratio) canvas.style.width = canvas.width + 'px' canvas.style.height = canvas.width / imgScale + 'px' canvas.width = canvas.width * ratio canvas.height = canvas.width / imgScale canvasCtx.drawImage( img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height, ) }
async function render() { const pxRatio = window.devicePixelRatio || 1
const canvas1 = document.getElementById('canvas1') const ctx1 = canvas1.getContext('2d') const canvas2 = document.getElementById('canvas2') const ctx2 = canvas2.getContext('2d') const img = await loadImage()
drawImage(canvas1, ctx1, img, 1) drawImage(canvas2, ctx2, img, pxRatio) }
render() </script> </body></html>
