说起图片的马赛克,可能一般都是由后端实现然后传递图片到前端,但是前端也是可以通过canvas来为图片加上马赛克的,下面就通过码上掘金来进行一个简单的实现。
(资料图)
markup
style
body { margin: 0; padding: 0; width: 100vw; height: 100vh; display: flex; align-items: center; justify-content: center;}#image { width: 200px; height: auto; margin-right: 30px;}
script
let canvas = document.createElement("canvas");let ctx = canvas.getContext("2d");let img = document.getElementById("image");let img1 = new Image();getBase64(document.getElementById("image").src).then((res) => { img1.src = res;});img1.onload = function () { let w = img1.width; let h = img1.height; canvas.width = w; canvas.height = h; ctx.drawImage(img1, 0, 0); let pixeArr = ctx.getImageData(0, 0, w, h).data; let sampleSize = 40; for (let i = 0; i < h; i += sampleSize) { for (let j = 0; j < h; j += sampleSize) { let p = (j + i * w) * 4; ctx.fillStyle = "rgba(" + pixeArr[p] + "," + pixeArr[p + 1] + "," + pixeArr[p + 2] + "," + pixeArr[p + 3] + ")"; ctx.fillRect(j, i, sampleSize, sampleSize); } } // img.src = canvas.toDataURL("image/jpeg"); let img2 = new Image(); img2.src = canvas.toDataURL("image/jpeg"); img2.width = img.width; img2.height = img.height; document.body.appendChild(img2);};function getBase64(imgUrl) { return new Promise(function (resolve, reject) { window.URL = window.URL || window.webkitURL; let xhr = new XMLHttpRequest(); xhr.open("get", imgUrl, true); xhr.responseType = "blob"; xhr.onload = function () { if (this.status == 200) { let blob = this.response; let oFileReader = new FileReader(); oFileReader.onloadend = function (e) { let base64 = e.target.result; resolve(base64); }; oFileReader.readAsDataURL(blob); } }; xhr.send(); });}
代码运行效果
最开始需要实现马赛克功能是需要通过canvas提供的一个获取到图片每一个像素的方法,我们都知道,图片本质上只是由像素组成的,越清晰的图片,就有着越高的像素,而像素的本质,就只是一个个拥有颜色的小方块而已,只要把一张图片放大多倍,就能够清楚的发现。
通过 canvas 的 getImageData 这个方法,我们就能够拿到图像上所有像素组成的数组,并且需要生成马赛克,意味着我们需要把一个范围内的色块的颜色都改成一样的,也就是通过canvas来重绘图片,
let pixeArr = ctx.getImageData(0, 0, w, h).data;let sampleSize = 40;for (let i = 0; i < h; i += sampleSize) { for (let j = 0; j < h; j += sampleSize) { let p = (j + i * w) * 4; ctx.fillStyle = "rgba(" + pixeArr[p] + "," + pixeArr[p + 1] + "," + pixeArr[p + 2] + "," + pixeArr[p + 3] + ")"; ctx.fillRect(j, i, sampleSize, sampleSize); }}复制代码
通过双重循环来循环图片所有的色块,其中的跨度就是我们设定好的色块大小,色块调整的越大,马赛克后图片更模糊,越小,图片的模糊度就会降低。在通过 fillStyle 选取颜色,以及 fillRect 重绘 canvas 实现了将整个 canvas 的色块都进行改变,最后在导出重绘后的图片,无论是改变原来的图片地址,或者是新加一张图片作为对比,就都是可行的了。
在使用 getImageData 获取图片的时候,如果使用的是线上图片,浏览器会爆出跨域的错误:
而上文中出现问题的图片是存放在本地的或者线上的,本地的图片默认是没有域名的,线上的图片并且是跨域的,所以浏览器都认为你是跨域,导致报错。
那么对于本地图片,我们只需要将图片放到和html对应的文件夹下,子文件夹也是不可以的,就能够解决,对于线上的图片,我们可以采用先把它下载下来,再用方法来获取数据的这种方式来进行。
function getBase64(imgUrl) { return new Promise(function (resolve, reject) { window.URL = window.URL || window.webkitURL; let xhr = new XMLHttpRequest(); xhr.open("get", imgUrl, true); xhr.responseType = "blob"; xhr.onload = function () { if (this.status == 200) { let blob = this.response; let oFileReader = new FileReader(); oFileReader.onloadend = function (e) { let base64 = e.target.result; resolve(base64); }; oFileReader.readAsDataURL(blob); } }; xhr.send(); });}复制代码
下载图片就不说了,通过浏览器提供的 API 或者其他封装好的请求工具都是可以的,在请求成功之后,我们将图片转化为 base64 并且返回,这样就能够获取线上图片的数据了。
本文提供了一种前端生成马赛克图片的方案,并且对于线上的图片,也能够通过先异步下载图片在进行转换的策略,实现了图片添加马赛克的功能。
完整附件:点此下载