CSS를 사용하여 이미지에 모자이크를 추가하는 방법은 무엇입니까? 다음 기사에서는 CSS를 능숙하게 사용하여 이미지 모자이크의 스타일을 지정하는 방법을 공유할 것입니다. 도움이 되기를 바랍니다.
CSS에는 image-rendering이라는 흥미로운 기능이 있는데, 이 기능은 알고리즘을 사용하여 크기가 조정된 이미지를 더 잘 표시할 수 있습니다. (권장 학습: css 비디오 튜토리얼)
QR 코드의 더 작은 스크린샷이 있다고 가정합니다(아래 왼쪽에는 회로도만 있고 스캔할 수 없음). 10배 확대하면 이미지가 흐려집니다(오른쪽 아래). ) :
이때 확대된 이미지에 image-rendering: pixelated
기능을 추가하면 CSS가 알고리즘을 통해 이를 픽셀화하여 이미지 윤곽선의 가장자리를 더 선명하게 만듭니다. image-rendering: pixelated
的特性,CSS 会通过算法将其像素化展示,使其图像轮廓具有更锐利的边缘:
该特性非常适合应用在色彩单一、轮廓分明、需要被放大的图片上,可以营造出一种伪矢量的既视感(减少放大后的失真)。
对于色彩丰富、细节较多的照片,image-rendering: pixelated
使用后会营造出一种马赛克的外观:
这离本文标题所希望实现的马赛克效果还有段距离 —— 目前图片需要被放大后才能显示出效果,而我们希望能在保有原图尺寸的基础上,给图片覆盖等尺寸马赛克。
然而 image-rendering
特性对尺寸未发生缩放的元素是不会生效的:
MDN - This property has no effect on non-scaled images.
等尺寸马赛克的原理相当于先把一张照片模糊化,然后再经过锐化算法处理得到各种小方格。
image-rendering: pixelated
帮我们实现了“锐化”的步骤,我们得想想怎么实现“模糊”。
首先使用滤镜的模糊方案是行不通的,因为 image-rendering
和图像缩放系数强相关,所以应当思考可以怎样利用图片的缩放能力。
这里得说一句,WEB 上的图片像极了 Photoshop 里的智能对象 —— 你可以任意修改它的尺寸(例如放大很多倍让其变模糊),但最后再把图片改回原本的大小时,图片会变回原来的样子(没有任何失真)。
如何保留图片放大后的“模糊”信息,是优先需要解决的问题。
聪明的小伙伴已经想到了可以尝试使用 canvas
来处理,毕竟 canvas
可以轻松获取、绘制图像,且绘制出来的图像信息是纯数据的,而非图形对象(Image
),故经其放大绘制的图片数据再进行缩小绘制(到原尺寸)会失真(这正好是我们所希望发生的)。
但这里也存在一些坑:
image-rendering: pixelated
算法处理后显示的信息,canvas
是无法拿到的,因为那是显示层的东西。canvas
拿到的依旧是未经锐化的、模糊的原生图像内容;canvas
本身如果没有缩放的话,给 canvas
添加 image-rendering: pixelated
没有任何意义。这意味着你无法把图片在 canvas
外面放大锐化,然后再写入 canvas
去缩小绘制(并不断迭代处理)来得到锐化后的原尺寸图片。
在解决上述问题时,我们先来看看 canvas
一个有趣的特性。
如果我们在 canvas
标签里定义了宽高:
<canvas width="100" height="50" ></canvas>
同时又给 canvas
在样式中定义了另一个宽高:
canvas { width: 200px; height: 200px; }
那么 canvas
会以哪个尺寸来显示呢?
答案是以 CSS 的尺寸来显示,但画布的内容尺寸会以画布标签内定义的宽高为准。这意味着虽然我们看到的是 200px * 200px
image-rendering: pixelated
를 사용하면 모자이크 모양이 생성됩니다. 🎜🎜🎜🎜이 글의 제목이 이루고자 하는 모자이크 효과까지는 아직 멀었습니다—— At 현재 효과를 표시하려면 이미지를 확대해야 하며, 원본 이미지 크기를 유지하면서 동일한 크기의 모자이크로 이미지를 덮기를 희망합니다. 🎜🎜그러나 이미지 렌더링
기능은 크기가 조정되지 않은 요소에는 적용되지 않습니다. 🎜🎜🎜MDN🎜 - 이 속성은 크기가 조정되지 않은 이미지에는 영향을 미치지 않습니다.🎜🎜🎜2. 구덩이를 밟아 균일한 크기의 모자이크 구현 🎜🎜🎜균등한 크기의 모자이크의 원리는 먼저 사진을 흐리게 한 다음 샤프닝 알고리즘으로 처리하여 다양한 작은 사각형을 얻는 것과 같습니다. . 🎜🎜image-rendering: pixelated
는 "선명화" 단계를 달성하는 데 도움이 되지만 "흐림"을 달성하는 방법에 대해 생각해야 합니다. 🎜🎜우선,이미지 렌더링
은 이미지 배율 요소와 밀접한 관련이 있기 때문에 필터를 사용한 흐림 솔루션은 작동하지 않으므로 이미지의 배율 기능을 어떻게 사용할 것인지 생각해야 합니다. 🎜🎜여기서 웹상의 사진은 Photoshop의 스마트 개체와 매우 유사하다는 점을 말씀드리고 싶습니다. 크기를 마음대로 수정할 수 있지만(예를 들어 여러 번 확대하여 흐릿하게 만드는 등) 결국에는 크기를 변경할 수도 있습니다. 사진을 원래 크기로 되돌리면 사진이 왜곡 없이 원래 모양으로 돌아갑니다. 🎜🎜사진을 확대한 후 "흐릿한" 정보를 유지하는 방법은 해결해야 할 최우선 문제입니다. 🎜🎜똑똑한 친구들은 이미캔버스
를 사용하여 처리하는 것을 생각해 봤습니다. 결국캔버스
는 이미지를 쉽게 얻고 그릴 수 있으며, 그려진 이미지 정보는 순수한 데이터입니다. 은 그래픽 개체(Image
)가 아니므로 확대했다가 원래 크기로 축소하여 그린 이미지 데이터는 왜곡됩니다(정확히 우리가 원하는 일입니다). 🎜🎜그러나 여기에도 몇 가지 함정이 있습니다: 🎜🎜즉,
image-rendering: pixelated
알고리즘으로 외부 이미지를 처리한 후에 표시되는 정보는canvas , 그것이 디스플레이 레이어이기 때문입니다. <code>캔버스
는 여전히 선명하지 않고 네이티브 이미지 콘텐츠를 흐리게 합니다.캔버스
자체는 크기가 조정되지 않은 경우 크기가 조정되지 않습니다. >이미지 렌더링: 픽셀화는 의미가 없습니다.캔버스
외부에서는 이미지를 확대하고 선명하게 한 다음캔버스
에 써서 축소하고 그릴 수 없습니다(그리고 계속해서 )을 반복하여 선명한 원본 크기 이미지를 얻습니다. 🎜🎜🎜3. 흥미로운 캔버스 스트레칭🎜🎜🎜위 문제를 해결하면서 먼저캔버스
의 흥미로운 기능을 살펴보겠습니다. 🎜🎜canvas
태그에서 너비와 높이를 정의하는 경우: 🎜<input id="file" type="file" accept="image/*" /> <canvas id="canvas"></canvas> <img id="img-raw" / alt="CSS를 사용하여 이미지 모자이크 스타일화를 달성하는 방법을 살펴보겠습니다." > <label for="compress-times">压缩倍数:</label> <input id="compress-times" type="number" value="12"> <button>马赛克化</button>🎜 동시에 스타일에서canvas
에 대해 다른 너비와 높이를 정의하는 경우: 🎜canvas { display: block; border: gray solid 1px; width: 600px; height: 600px; image-rendering: pixelated; } img { display: none; }🎜 그러면캔버스
는 어떤 크기로 표시되나요? 🎜🎜답은 CSS 크기를 표시하는 것이지만, 캔버스의 콘텐츠 크기는 캔버스 태그에 정의된 너비와 높이를 기반으로 합니다. 즉,200px * 200px
의 캔버스가 표시되지만 실제로는 내용이 늘어납니다(너비 2배, 높이 4배). 🎜注:左边为画布,右边为原图
这也是
canvas
作为可替换元素的一个特性 —— CSS 无法修改其内容。试想一下,如果 CSS 可以动态地修改canvas
内容的尺寸,意味着canvas
的内容会被裁剪掉一部分,或者多出来一部分空白区域,这显然是不可取的。所以canvas
在保留内容完整的前提下,整体伸缩到样式规定尺寸,是合理的浏览器行为。利用
canvas
的这个特性,我们可以这样来实现等尺寸马赛克:
- 创建一个画布,通过样式规定好其宽高,并设置
image-rendering: pixelated
特性;- 计算图片最佳展示尺寸(以类似
background-size: contain
的形式展示);- 将画布的宽高(非样式)设置为样式宽高的
1/N
;- 绘制图像,绘制的图像宽高为最佳展示尺寸的
1/N
。如此一来,我们实际绘制了一个尺寸仅为最佳尺寸
1/N
的图像,再通过canvas
的N
倍放大又变回了视觉上的最佳尺寸。图像因为走的canvas
绘制,所以放大回最佳尺寸后会保持模糊,从而满足了image-rendering
的匹配需求。注:这里提到的“最佳尺寸”,指的是步骤 2 里“确保完整展示图像”所对应的最佳尺寸,而非图片原生尺寸。
四、代码实现
我们按照上方步骤来书写对应代码,当然我们希望灵活一些,例如上述的
N
可以由用户自定义。另外本章的代码可以在 Github 上获取。4.1 HTML 部分
主要为选择图片的
<input>
控件、画布、方便画布获取图像的<img alt="CSS를 사용하여 이미지 모자이크 스타일화를 달성하는 방법을 살펴보겠습니다." >
、供用户自定义缩放倍数的文本框、执行按钮:<input id="file" type="file" accept="image/*" /> <canvas id="canvas"></canvas> <img id="img-raw" / alt="CSS를 사용하여 이미지 모자이크 스타일화를 달성하는 방법을 살펴보겠습니다." > <label for="compress-times">压缩倍数:</label> <input id="compress-times" type="number" value="12"> <button>马赛克化</button>4.2 CSS 部分
我们需要通过样式规定好画布的外观尺寸,并配置
image-rendering: pixelated
特性。另外<img alt="CSS를 사용하여 이미지 모자이크 스타일화를 달성하는 방법을 살펴보겠습니다." >
标签只是一个传递用户所选图片到画布的中介,可以直接隐藏:canvas { display: block; border: gray solid 1px; width: 600px; height: 600px; image-rendering: pixelated; } img { display: none; }4.3 JS 部分
let imgBlobUrl; const file = document.getElementById('file'); const img = document.getElementById('img-raw'); const compressTimes = document.getElementById('compress-times'); const defaultCompressTimes = compressTimes.value | 0; const canvas = document.getElementById('canvas'); const button = document.querySelector('button'); const boundingRect = canvas.getBoundingClientRect(); const ctx = canvas.getContext('2d'); const canvas_w = boundingRect.width; const canvas_h = boundingRect.height; // 以 background-size: contain 形式设置图片尺寸 function matchImgSizeToCanvas(imgElem = img) { let w = imgElem.width; let h = imgElem.height; if (w > canvas_w || h > canvas_h) { let radio = Math.max(h / canvas_h, w / canvas_w); radio = Number(radio.toFixed(2)); imgElem.width = parseInt(w / radio); imgElem.height = parseInt(h / radio); } } // 绘制 1/N 大小的图像,画布宽高属性设为样式宽高的 1/N,从而实现画布内容的 N 倍放大 function run() { let ct = parseInt(compressTimes.value) || defaultCompressTimes; canvas.width = parseInt(canvas_w / ct); canvas.height = parseInt(canvas_h / ct); ctx.drawImage(img, 0, 0, parseInt(img.width / ct), parseInt(img.height / ct)); } function cleanCanvas() { ctx.clearRect(0, 0, canvas_w, canvas_h); } function reset() { img.removeAttribute('width'); img.removeAttribute('height'); cleanCanvas(); matchImgSizeToCanvas(img); run(); } file.addEventListener('change', function (e) { window.URL.revokeObjectURL(imgBlobUrl); const picFile = this.files[0]; imgBlobUrl = window.URL.createObjectURL(picFile); img.onload = function init() { reset(); } img.src = imgBlobUrl; }, false); button.addEventListener('click', reset, false);执行效果:
选中文件/点击按钮后,能按压缩倍数得到对应的像素风格艺术照。
五、Mosaic 插件封装
通过上方示例我们学习了如何利用
canvas
特性来设计等尺寸的马赛克效果,现在我们尝试把该功能封装为一个简易插件,可以让页面上的图片列表一键 Mosaicing。插件的实现方案也很简单 —— 用户点击按钮时,往图片容器上插入一个和容器等尺寸的画布(尺寸通过样式设置),再绘制覆盖画布的图像,并缩小画布的宽高属性来放大画布内容:
5.1 插件脚本
/** @file mosaic.js **/ class Mosaic { constructor(url, container, options = {}) { if (typeof container === 'string') { container = document.querySelector(container); } if (!url || !container?.style) { console.error('参数不正确'); } this.url = url; this.options = options; this.container = container; this.init(); } init() { const img = new Image(); const canvas = document.createElement('canvas'); canvas.style.position = 'absolute'; canvas.style.zIndex = 999; canvas.style.imageRendering = 'pixelated'; this.img = img; this.canvas = canvas; this.ctx = canvas.getContext('2d'); const containerBoundingRect = this.container.getBoundingClientRect(); const container_w = containerBoundingRect.width; const container_h = containerBoundingRect.height; // 通过样式初始化画布尺寸为容器尺寸 canvas.style.width = container_w + 'px'; canvas.style.height = container_h + 'px'; img.onload = () => { this.run(container_w, container_h); } img.src = this.url; } run(w, h) { // 缩小倍数,可以由参数传入,默认为 12 const compressTimes = parseInt(this.options.compressTimes) || 12; let compress_w = parseInt(w / compressTimes); let compress_h = parseInt(h / compressTimes); // 修改画布尺寸属性为 1/缩小倍数 this.canvas.width = compress_w; this.canvas.height = compress_h; // 绘制图片覆盖缩小后的画布 this.ctx.drawImage(this.img, 0, 0, compress_w, compress_h); this.container.prepend(this.canvas); this.img = null; } remove() { this.container.removeChild(this.canvas); this.canvas = null; } } export default Mosaic;5.2 插件使用页
/** @file plugin-demo.html **/ <head> <style> ul { list-style: none; margin: 0; padding: 0; } li { float: left; line-height: 0; margin: 0 20px 20px 0; } li>img { max-height: 180px; } div { display: block; clear: both; } </style> </head> <body> <ul> <li><img src="./assert/0.png" / alt="CSS를 사용하여 이미지 모자이크 스타일화를 달성하는 방법을 살펴보겠습니다." ></li> <li><img src="./assert/1.png" / alt="CSS를 사용하여 이미지 모자이크 스타일화를 달성하는 방법을 살펴보겠습니다." ></li> <li><img src="./assert/2.png" / alt="CSS를 사용하여 이미지 모자이크 스타일화를 달성하는 방법을 살펴보겠습니다." ></li> <li><img src="./assert/3.png" / alt="CSS를 사용하여 이미지 모자이크 스타일화를 달성하는 방법을 살펴보겠습니다." ></li> </ul> <div> <button id="generate">铺上马赛克</button> <button id="remove">移除马赛克</button> </div> <script type="module"> import Mosaic from './mosaic.js'; let liElems = document.querySelectorAll('li'); let mosaicList = []; document.querySelector('#generate').onclick = () => { remove(); for (let i = 0; i < liElems.length; i++) { let liElem = liElems[i]; let url = liElem.querySelector('img').src; let mosaic = new Mosaic(url, liElem); mosaicList.push(mosaic); } } function remove() { mosaicList.forEach((mosaic) => { mosaic.remove(); }); mosaicList.length = 0; } document.querySelector('#remove').onclick = remove; </script> </body>执行效果:
点击“铺上”或“移除”按钮,可以轻松实现/移除列表上各图片的像素风格化。
六、兼容性
image-rendering
的兼容性可以从 caniuse 上查到,目前覆盖率如下:影响较大的主要还是在 IE、UC,以及安卓 4.4.4 版本的浏览器,需要酌情考虑是否在产品上使用此 CSS 特性。
以上便是本文全部内容,相关代码可以在 Github 上获取(地址:https://github.com/VaJoy/BlogDemo3/tree/main/220226-pixelated)。
希望能令你有所收获,共勉~
(학습 영상 공유: 웹 프론트엔드 입문 튜토리얼)
위 내용은 CSS를 사용하여 이미지 모자이크 스타일화를 달성하는 방법을 살펴보겠습니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!