Canvas绘制图片模糊问题? 您所在的位置:网站首页 canvas1 Canvas绘制图片模糊问题?

Canvas绘制图片模糊问题?

2023-03-23 11:14| 来源: 网络整理| 查看: 265

Canvas绘制图片模糊问题? #

在日常业务中,总能遇到使用 canvas 绘制图片的场景,今天就聊一聊 canvas 绘制图片且出现模糊不清的问题?

案例代码

htmlDOCTYPE html> canvas .container { display: flex; } 原图: 未优化: 优化后: 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() 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980

效果图如👇🏻

(图片是网上百度的,如果图片出现跨域问题,可以通过添加属性 img.setAttribute('crossOrigin', 'anonymous') 来解决)

对比原图,没有优化的 canvas 绘制确实有明显清晰度下降。

🤔为什么会出现这种情况呢? #

canvas 渲染出的东西本质是一张位图。高dpi显示设备意味着每平方英寸有更多的像素,如在 Retina 屏幕上相当于使用两个像素点来渲染一个像素,相当于图片被放大了一倍,因此绘制出来的东西会变得模糊。

在解决前,先了解一下像素问题,已了解可跳过。

像素问题解析 #

(有错误可邮件我!!!)

物理像素表示屏幕上有多少个发光点(颗粒),逻辑像素(也叫设备独立像素)表示屏幕展示物体的视觉尺寸是多大。为什么需要逻辑像素?因为物理像素仅仅表示像素的个数,并没有规定实际的尺寸,如果说一个矩形要用10个物理像素来渲染,那么不同的(物理)像素密度和排列方式展示出来的效果也就不一样(像素密度越大图形越小);因此为了保持各设备展示一样产生了逻辑像素。

比如:某屏幕的物理像素是 375x667,那么表示该屏幕每行有375个颗粒和每列有667个颗粒组成;假设这个屏幕的逻辑像素也是 375x667,那么1个逻辑像素等于1个物理像素;假设屏幕的物理像素是 750x1334,逻辑像素依旧是 375x667,这时1个逻辑像素等于2个物理像素。这也就是屏幕的 dpr 概念,dpr = 物理像素 / 设备独立像素。

⭕ 在前端有两个很热门的问题?

为什么手机的设计稿大多数是2倍图?为什么设置 1px 手机看起来比设计稿粗?

第一个问题:因为现在大多数手机的 dpr 都是为2,使用2倍图会在设备中显示更加精细,就好比使用一行100个物理像素的屏幕和一行50个物理像素的屏幕,同时渲染黑色到白色的渐变,明显100个物理像素的屏幕会过渡得更加自然。(记住一个物理像素只能像素一种颜色,不能说一半黑一半灰)

第二个问题:首先提一个要点,在屏幕没有缩放的情况下,1css像素等于1个逻辑像素,也就是说等于2个物理像素;(场景设计稿提供的是2倍图750x1334,设备逻辑像素375x667),设计稿中的1px相对等于1个逻辑像素,如果缩放到设备中显示,相当于每一条边进行等比缩放,所以实际需要设备中的0.5个逻辑像素显示,但你已经使用 1px(css像素),那么当然会比设计稿看起来粗。 其实这个问题个人觉得使用dpr来解析并不惬当,只是一个简单的缩放问题而已,即拿一个750px的图在375px的设备上显示,整体的设计稿缩小了,但写1px时你没去转换导致的。

解决1px问题,网上有很多,这里就不重复说了。

不少人第一时间应该会想到使用0.5px解决,确实在ios可以解决,但在android中兼容不太好。

解决思路 #

canvas 存在一个 backingStorePixelRatio 属性,不过现在已经废弃。转而使用在浏览器 window 对象下的 devicePixelRatio 属性,它表示浏览器的设备像素比,也就是 dpr。

举例:使用一张 100x100 像素大小的图片,去Retina 屏幕上实际会占据 200×200 像素的空间,相当于图片被放大了一倍,因此图片会变得模糊。 (图片并没有 dpr 概念,图片的参数 100x100像素,即表示 100x100像素尺寸,也表示 100x 100个像素点;Retina 屏幕 的dpr 是2,显示 100x100 像素尺寸(逻辑像素),因此需要使用 2个物理像素点去渲染 1个 像素点)

而在 canvas 中的 style.width 和 style.height 是设置画布的实际渲染大小;canvas.width 和 canvas.height 设置的是画布的大小。

当两者都设定相同数值时,在 dpr 为 1 的屏幕下是没有问题的,而在 dpr 大于 1 的屏幕下,就出现 canvas 被放大的问题;或许你把 canvas.width 比作图片像素,canvas style 比作设备像素 就比较好理解了。

所以我们可以通过 devicePixelRatio 属性来动态设置 canvas 画布大小,来实现 dpr 为 1 的时候,使用 1 倍图,dpr 为 2 时使用 2 倍图,以此类推。

🗒️ 完整代码展示

htmlDOCTYPE html> canvas .container { display: flex; } 原图: 未优化: 优化后: 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() 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586

🥳🥳🥳 效果图



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有