2

前端线上图片生成马赛克_CRMEB中邦科技的技术博客_51CTO博客

 1 year ago
source link: https://blog.51cto.com/u_15723831/5986228
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前端线上图片生成马赛克

精选 原创

CRMEB众邦科技 2023-01-03 17:36:23 ©著作权

文章标签 马赛克 JavaScript 前端 图片 文章分类 PHP 前端开发 阅读数219

说起图片的马赛克,可能一般都是由后端实现然后传递图片到前端,但是前端也是可以通过canvas来为图片加上马赛克的,下面就通过码上掘金来进行一个简单的实现。

markup

<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d0b4e30cd0f648fcbe675992ddcb90f1~tplv-k3u1fbpfcp-watermark.image?" id="image" />

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();
});
}

代码运行效果

前端线上图片生成马赛克_JavaScript

最开始需要实现马赛克功能是需要通过canvas提供的一个获取到图片每一个像素的方法,我们都知道,图片本质上只是由像素组成的,越清晰的图片,就有着越高的像素,而像素的本质,就只是一个个拥有颜色的小方块而已,只要把一张图片放大多倍,就能够清楚的发现。

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 的色块都进行改变,最后在导出重绘后的图片,无论是改变原来的图片地址,或者是新加一张图片作为对比,就都是可行的了。

canvas 跨域问题

在使用 getImageData 获取图片的时候,如果使用的是线上图片,浏览器会爆出跨域的错误:

前端线上图片生成马赛克_图片_02

而上文中出现问题的图片是存放在本地的或者线上的,本地的图片默认是没有域名的,线上的图片并且是跨域的,所以浏览器都认为你是跨域,导致报错。

那么对于本地图片,我们只需要将图片放到和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 并且返回,这样就能够获取线上图片的数据了。

本文提供了一种前端生成马赛克图片的方案,并且对于线上的图片,也能够通过先异步下载图片在进行转换的策略,实现了图片添加马赛克的功能。

完整附件:​ ​点此下载​

  • 1
  • 收藏
  • 评论
  • 分享
  • 举报

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK