2.6Resemble.js❝
图像分析与比较
❞
Resemble.js 使用 HTMLCanvas 和 JavaScript 来分析和比较图像。 兼容低于 8.0 的 Node.js 版本。
「使用范例」
// 比较两张图片
var diff = resemble(file)
.compareTo(file2)
.ignoreColors()
.onComplete(function(data) {
console.log(data);
/*
{
misMatchPercentage : 100, // %
isSameDimensions: true, // or false
dimensionDifference: { width: 0, height: -1 },
getImageDataUrl: function(){}
}
*/
});
《网上例子》
❝
❞
2.7异食癖❝
在浏览器中高质量、高速地调整图像大小
❞
Pica 可用于在浏览器中调整图像大小而无需像素化,并且速度相当快。 它手动选择最佳的可用技术:webworkers、webassemble、createImageBitmap、纯 JS。
通过Pica,您可以实现以下功能:
「使用范例」
const pica = require('pica')();
// 调整画布/图片的大小
pica.resize(from, to, {
unsharpAmount: 80,
unsharpRadius: 0.6,
unsharpThreshold: 2
})
.then(result => console.log('resize done!'));
// 调整大小并转换为Blob
pica.resize(from, to)
.then(result => pica.toBlob(result, 'image/jpeg', 0.90))
.then(blob => console.log('resized to canvas & created blob!'));
《网上例子》
❝
❞
2.8tui.图像编辑器❝
使用画布的全功能照片图像编辑器。它非常简单,并且带有很棒的滤镜。
❞
tui.image-editor 是一个使用 HTML5Canvas 的全功能图像编辑器。 它易于使用并提供强大的过滤器。 同时支持图像的裁剪、翻转、旋转、绘图、形状、文本、遮罩和图像过滤等操作。
tui.image-editor的浏览器兼容性如下:
「使用范例」
// Image editor
var imageEditor = new tui.ImageEditor("#tui-image-editor-container", {
includeUI: {
loadImage: {
path: "img/sampleImage2.png",
name: "SampleImage",
},
theme: blackTheme, // or whiteTheme
initMenu: "filter",
menuBarPosition: "bottom",
},
cssMaxWidth: 700,
cssMaxHeight: 500,
usageStatistics: false,
});
window.onresize = function () {
imageEditor.ui.resizeEditor();
};
网上例子
❝
❞
2.9gif.js❝
JavaScriptGIFencoding 库
❞
gif.js 是一个运行在浏览器端的 JavaScript GIF 编码器。 它使用类型字段和 WebWorker 在后台渲染每个帧,使其速度非常快。 该库适用于支持 WebWorkers、FileAPI 和 TypedArrays 的浏览器。
gif.js的浏览器兼容性如下:
「使用范例」
var gif = new GIF({
workers: 2,
quality: 10
});
// add an image element
gif.addFrame(imageElement);
// or a canvas element
gif.addFrame(canvasElement, {delay: 200});
// or copy the pixels from a canvas context
gif.addFrame(ctx, {copy: true});
gif.on('finished', function(blob) {
window.open(URL.createObjectURL(blob));
});
gif.render();
《网上例子》
❝
❞
2.10锐利❝
高性能 Node.js 图像处理,调整 JPEG、PNG、WebP 和 TIFF 图像大小的最快模块。使用 libvips 库。
❞
Sharp 的一个典型用例是将常见格式的大图像转换为较小的、网络友好的 JPEG、PNG 和 WebP 格式。 因为它内部使用 libvips,所以调整图像大小通常比使用 ImageMagick 和 GraphicsMagick 设置快 4-5 倍。 不仅支持图像调整大小,Sharp还支持旋转、提取、合成、伽马校准等功能。
Sharp 支持读取 JPEG、PNG、WebP、TIFF、GIF 和 SVG 图像。 输出图像可以是 JPEG、PNG、WebP 和 TIFF 格式,或未压缩的原始像素数据。
「使用范例」
// 改变图像尺寸
sharp(inputBuffer)
.resize(320, 240)
.toFile('output.webp', (err, info) => { ... });
// 旋转输入图像并改变图片尺寸
sharp('input.jpg')
.rotate()
.resize(200)
.toBuffer()
.then( data => { ... })
.catch( err => { ... });
《网上例子》
❝
❞
这个例子来自阿宝哥2018年写的文章《夏普疯狂测试:生成专属分享图片》,主要利用夏普提供的图像合成功能,为每个用户生成专属分享海报。 有兴趣的男同伴可以看一下这里是原文。
const sharp = require("sharp");
const TextToSVG = require("text-to-svg");
const path = require("path");
// 加载字体文件
const textToSVG = TextToSVG.loadSync(path.join(__dirname, "./simhei.ttf"));
// 创建圆形SVG,用于实现头像裁剪
const roundedCorners = new Buffer(
''
);
// 设置SVG文本元素相关参数
const attributes = { fill: "white" };
const svgOptions = {
x: 0,
y: 0,
fontSize: 32,
anchor: "top",
attributes: attributes
};
/**
* 使用文本生成SVG
* @param {*} text
* @param {*} options
*/
function textToSVGFn(text, options = svgOptions) {
return textToSVG.getSVG(text, options);
}
/**
* 图层叠加生成分享图片
* @param {*} options
*
*/
async function genShareImage(options) {
const { backgroudPath, avatarPath, qrcodePath,
userName, words, likes, outFilePath
} = options;
// 背景图片
const backgroudBuffer = sharp(path.join(__dirname, backgroudPath)).toBuffer({
resolveWithObject: true
});
const backgroundImageInfo = await backgroudBuffer;
// 头像图片
const avatarBuffer = await genCircleAvatar(path.join(__dirname, avatarPath));
// 二维码图片
const qrCodeBuffer = await sharp(path.join(__dirname, qrcodePath))
.resize(180)
.toBuffer({
resolveWithObject: true
});
// 用户名
const userNameSVG = textToSVGFn(userName);
// 用户数据
const userDataSVG = textToSVGFn(`写了${words}个字 收获${likes}个赞`);
const userNameBuffer = await sharp(new Buffer(userNameSVG)).toBuffer({
resolveWithObject: true
});
const userDataBuffer = await sharp(new Buffer(userDataSVG)).toBuffer({
resolveWithObject: true
});
const buffers = [avatarBuffer, qrCodeBuffer, userNameBuffer, userDataBuffer];
// 图层叠加参数列表
const overlayOptions = [
{ top: 150, left: 230 },
{ top: 861, left: 227 },
{
top: 365,
left: (backgroundImageInfo.info.width - userNameBuffer.info.width) / 2
},
{
top: 435,
left: (backgroundImageInfo.info.width - userDataBuffer.info.width) / 2
}
];
// 组合多个图层:图片+文字图层
return buffers
.reduce((input, overlay, index) => {
return input.then(result => {
console.dir(overlay.info);
return sharp(result.data)
.overlayWith(overlay.data, overlayOptions[index])
.toBuffer({ resolveWithObject: true });
});
}, backgroudBuffer)
.then((data) => {
return sharp(data.data).toFile(outFilePath);
}).catch(error => {
throw new Error('Generate Share Image Failed.');
});
}
/**
* 生成圆形的头像
* @param {*} avatarPath 头像路径
*/
function genCircleAvatar(avatarPath) {
return sharp(avatarPath)
.resize(180, 180)
.overlayWith(roundedCorners, { cutout: true })
.png()
.toBuffer({
resolveWithObject: true
});
}
module.exports = {
genShareImage
};
3、阿宝哥有话要说 3.1 如何区分图片类型
“计算机并不是通过图片的后缀来区分不同的图片类型,而是通过‘幻数’(MagicNumber)来区分。” 对于某些类型的文件,前几个字节的内容是固定的,根据这些字节的内容,可以确定文件的类型。
常见图像类型对应的幻数如下表所示:
文件类型文件后缀幻数
JPEG
jpg/jpeg
0xFFD8FF
巴布亚新几内亚
PNG
0x89504E47
动图
动图
0x47494638 (GIF8)
骨形态发生蛋白
图像格式
0x424D
这里我们以阿宝的头像(abao.png)为例,验证一下图片的类型是否正确:
在日常开发过程中,如果遇到检查图片类型的场景,我们可以直接使用一些现成的第三方库。 例如,如果你想判断一张图片是否是PNG类型,你可以使用is-png库,它同时支持浏览器和Node.js。 使用示例如下:
“Node.js”
// npm install read-chunk
const readChunk = require('read-chunk');
const isPng = require('is-png');
const buffer = readChunk.sync('unicorn.png', 0, 8);
isPng(buffer);
//=> true
“浏览器”
(async () => {
const response = await fetch('unicorn.png');
const buffer = await response.arrayBuffer();
isPng(new Uint8Array(buffer));
//=> true
})();
3.2 如何获取图片的规格
图像的规格、位深度、颜色类型和压缩算法将存储在文件的二进制补码数据中。 我们继续以阿宝头像(abao.png)为例来了解一下实际情况:
❝
528(十的补码)=> 0x0210
560(十的补码)=> 0x0230
❞
因此,如果我们想要获取图片的规格,就需要根据不同的图片格式来分析图片的补码数据。 幸运的是,我们不需要自己做这件事。 image-size Node.js库已经帮助我们实现了获取主流图像类型的文件规格的功能:
「同步方法」
var sizeOf = require('image-size');
var dimensions = sizeOf('images/abao.png');
console.log(dimensions.width, dimensions.height);
“异步方法”
var sizeOf = require('image-size');
sizeOf('images/abao.png', function (err, dimensions) {
console.log(dimensions.width, dimensions.height);
});
图像大小库非常强大。 它不仅支持PNG格式,还支持BMP、GIF、ICO、JPEG、SVG和WebP格式。
3.3 如何预览本地图片
借助HTMLFileReaderAPI,我们还可以方便地实现本地图片预览功能,具体代码如下:
<input type="file" accept="image/*" onchange="loadFile(event)">
<img id="output"/>
<script>
const loadFile = function(event) {
const reader = new FileReader();
reader.onload = function(){
const output = document.querySelector('output');
output.src = reader.result;
};
reader.readAsDataURL(event.target.files[0]);
};
</script>
完成本地图片预览后,您可以直接将图片对应的DataURLs数据提交到服务器。 针对这些情况,服务器需要做一些相关处理才能正常保存上传的图片。 这里我们以 Express 为例。 具体处理代码如下:
const app = require('express')();
app.post('/upload', function(req, res){
let imgData = req.body.imgData; // 获取POST请求中的base64图片数据
let base64Data = imgData.replace(/^data:image/w+;base64,/, "");
let dataBuffer = Buffer.from(base64Data, 'base64');
fs.writeFile("image.png", dataBuffer, function(err) {
if(err){
res.send(err);
}else{
res.send("图片上传成功!");
}
});
});
3.4 如何实现图像压缩
在某些场合,我们希望在上传本地图片时,先将图片进行一定程度的压缩,然后再提交到服务器,从而减少传输的数据量。 为了在后端实现图像压缩,我们可以使用Canvas对象提供的toDataURL()方法,该方法接收两个可选参数,type和encoderOptions。
其中type表示图片格式,默认为image/png。 EncoderOptions用来表示图片的质量。 当指定的图片格式为image/jpeg或image/webp时,图片的质量可以从0到1选择。如果超出取值范围,则使用默认值0.92,其他参数将被忽略。
我们来看看如何实现图像压缩:
function compress(base64, quality, mimeType) {
let canvas = document.createElement("canvas");
let img = document.createElement("img");
img.crossOrigin = "anonymous";
return new Promise((resolve, reject) => {
img.src = base64;
img.onload = () => {
let targetWidth, targetHeight;
if (img.width > MAX_WIDTH) {
targetWidth = MAX_WIDTH;
targetHeight = (img.height * MAX_WIDTH) / img.width;
} else {
targetWidth = img.width;
targetHeight = img.height;
}
canvas.width = targetWidth;
canvas.height = targetHeight;
let ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, targetWidth, targetHeight); // 清除画布
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
let imageData = canvas.toDataURL(mimeType, quality / 100);
resolve(imageData);
};
});
}
对于返回的DataURL格式的图像数据,为了进一步减少传输的数据量,我们可以将其转换为Blob对象:
function dataUrlToBlob(base64, mimeType) {
let bytes = window.atob(base64.split(",")[1]);
let ab = new ArrayBuffer(bytes.length);
let ia = new Uint8Array(ab);
for (let i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
return new Blob([ab], { type: mimeType });
}
转换完成后,我们可以将压缩图片对应的Blob对象封装在FormData对象中,然后通过AJAX提交给服务器:
function uploadFile(url, blob) {
let formData = new FormData();
let request = new XMLHttpRequest();
formData.append("image", blob);
request.open("POST", url, true);
request.send(formData);
}
3.5 如何操作位图质量数据
如果想要操作图像像素数据,我们可以使用CanvasRenderingContext2D提供的getImageData来获取图像像素数据,其中getImageData()返回一个ImageData对象来描述画布区域包含的像素数据。 该区域由一个圆圈表示。 起点为(sx,sy),宽度为sw,高度为sh。 getImageData方法的句型如下:
ctx.getImageData(sx, sy, sw, sh);
对应参数说明如下:
获得图像的像素数据后,我们可以对获得的像素数据进行处理,例如灰度或反色处理。 当处理完成后,如果我们想将处理结果显示在页面上,我们需要使用CanvasRenderingContext2D提供的另一个API——putImageData。
此 API 是 Canvas2DAPI 将数据从现有 ImageData 对象绘制到位图的方式。 如果提供了草绘圆,则仅绘制圆的像素。 该方法不受画布变换矩阵的影响。 putImageData方法的句型如下:
void ctx.putImageData(imagedata, dx, dy);
void ctx.putImageData(imagedata, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight);
对应参数说明如下:
介绍完相关的API之后,我们来举一个实际的例子:
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>图片反色和灰度化处理</title>
</head>
<body onload="loadImage()">
<div>
<button id="invertbtn">反色</button>
<button id="grayscalebtn">灰度化</button>
</div>
<canvas id="canvas" width="800" height="600"></canvas>
<script>
function loadImage() {
var img = new Image();
img.crossOrigin = "";
img.onload = function () {
draw(this);
};
// 这是阿宝哥的头像哟
img.src = "https://avatars3.githubusercontent.com/u/4220799";
}
function draw(img) {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
img.style.display = "none";
var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = imageData.data;
var invert = function () {
for (var i = 0; i < data.length; i += 4) {
data[i] = 255 - data[i]; // red
data[i + 1] = 255 - data[i + 1]; // green
data[i + 2] = 255 - data[i + 2]; // blue
}
ctx.putImageData(imageData, 0, 0);
};
var grayscale = function () {
for (var i = 0; i < data.length; i += 4) {
var avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = avg; // red
data[i + 1] = avg; // green
data[i + 2] = avg; // blue
}
ctx.putImageData(imageData, 0, 0);
};
var invertbtn = document.getElementById("invertbtn");
invertbtn.addEventListener("click", invert);
var grayscalebtn = document.getElementById("grayscalebtn");
grayscalebtn.addEventListener("click", grayscale);
}
</script>
</body>
</html>
需要注意的是,调用getImageData方法获取图像像素数据时,可能会遇到跨域问题,例如:
Uncaught DOMException: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data.
对于这个问题,可以阅读“张鑫旭”的文章《解决canvas图片getImageData、toDataURL跨域问题》。
3.6 如何实现图像隐写
“隐写术是关于信息隐藏的方法和科学。所谓信息隐藏是指不让除预期接收者之外的任何人知道信息的传输或信息的内容。” 英文隐写术全称Steganography,源自Trithemius的一本关于密码学和隐写术的专着Steganographia。 书名以西班牙语开头,意为“秘密写作”。
右图是阿宝哥使用在线图像隐写工具将“全栈修真之路”六个字隐藏到原图中,然后使用相应的解密工具揭示了隐藏信息的结果:
(在线图像隐写体验地址:)
目前实现图像隐写的解决方案有很多。 以下是一些常见的解决方案:
由于篇幅有限,这里不再继续展开,分别介绍各个方案。 有兴趣的男士可以阅读《隐写术:图片隐写术(一)》一文。
4. 参考资源
支持
如果您认为本文内容对您有所启发javascript图片预览,我想请您帮我三个小忙:
点击“在看”,让更多人看到此内容(不管你喜不喜欢,你就是流氓-_-)
关注我的官方网站muyiy.cn,让我们建立长期合作关系
关注公众号“高级后端进阶”javascript图片预览,公众号后台回复“面试题”给您发送中级后端笔试题,回复“加群”即可加入笔试互助交流群
》》面试官正在用的题库,快来看看吧》》