作者: semlinker
转发链接:https://mp.weixin.qq.com/s/i3ynTtPJOECoAYfqHFoo3Q
前言本文给小伙伴们隆重介绍用于图片处理的十个 「“小帮手”」,他们各个身怀绝技,拥有模糊、压缩、裁剪、旋转、合成、比对等技能。相信认识他们之后,你将能够轻松应对大多数的图片处理场景。
该章节你将会学到以下知识:
十个图片处理 「“小帮手”」 已经已经迫不及待想与你见面,还在犹豫什么?赶紧出发吧!
目录推荐10个常用的图片处理小帮手(下)「值得收藏」 本篇
2.10 Sharp❝
High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images. Uses the libvips library.
https://github.com/lovell/sharp
❞
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=>{...});
「在线示例」
❝
https://segmentfault.com/a/1190000012903787
❞
该示例是来自阿宝哥 18 年写的 “Sharp 牛刀小试之生成专属分享图片” 这篇文章,主要是利用 Sharp 提供的图片合成功能为每个用户生成专属的分享海报,感兴趣的小伙伴可以阅读一下原文哟。
constsharp=require("sharp");
constTextToSVG=require("text-to-svg");
constpath=require("path");
//加载字体文件
consttextToSVG=TextToSVG.loadSync(path.join(__dirname,"./simhei.ttf"));
//创建圆形SVG,用于实现头像裁剪
constroundedCorners=newBuffer(
'<svg><circler="90"cx="90"cy="90"/></svg>'
);
//设置SVG文本元素相关参数
constattributes={fill:"white"};
constsvgOptions={
x:0,
y:0,
fontSize:32,
anchor:"top",
attributes:attributes
};
/**
*使用文本生成SVG
*@param{*}text
*@param{*}options
*/
functiontextToSVGFn(text,options=svgOptions){
returntextToSVG.getSVG(text,options);
}
/**
*图层叠加生成分享图片
*@param{*}options
*
*/
asyncfunctiongenShareImage(options){
const{backgroudPath,avatarPath,qrcodePath,
userName,words,likes,outFilePath
}=options;
//背景图片
constbackgroudBuffer=sharp(path.join(__dirname,backgroudPath)).toBuffer({
resolveWithObject:true
});
constbackgroundImageInfo=awaitbackgroudBuffer;
//头像图片
constavatarBuffer=awaitgenCircleAvatar(path.join(__dirname,avatarPath));
//二维码图片
constqrCodeBuffer=awaitsharp(path.join(__dirname,qrcodePath))
.resize(180)
.toBuffer({
resolveWithObject:true
});
//用户名
constuserNameSVG=textToSVGFn(userName);
//用户数据
constuserDataSVG=textToSVGFn(`写了${words}个字收获${likes}个赞`);
constuserNameBuffer=awaitsharp(newBuffer(userNameSVG)).toBuffer({
resolveWithObject:true
});
constuserDataBuffer=awaitsharp(newBuffer(userDataSVG)).toBuffer({
resolveWithObject:true
});
constbuffers=[avatarBuffer,qrCodeBuffer,userNameBuffer,userDataBuffer];
//图层叠加参数列表
constoverlayOptions=[
{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
}
];
//组合多个图层:图片 文字图层
returnbuffers
.reduce((input,overlay,index)=>{
returninput.then(result=>{
console.dir(overlay.info);
returnsharp(result.data)
.overlayWith(overlay.data,overlayOptions[index])
.toBuffer({resolveWithObject:true});
});
},backgroudBuffer)
.then((data)=>{
returnsharp(data.data).toFile(outFilePath);
}).catch(error=>{
thrownewError('GenerateShareImageFailed.');
});
}
/**
*生成圆形的头像
*@param{*}avatarPath头像路径
*/
functiongenCircleAvatar(avatarPath){
returnsharp(avatarPath)
.resize(180,180)
.overlayWith(roundedCorners,{cutout:true})
.png()
.toBuffer({
resolveWithObject:true
});
}
module.exports={
genShareImage
};
三、阿宝哥有话说3.1 如何区分图片的类型
「计算机并不是通过图片的后缀名来区分不同的图片类型,而是通过 “魔数”(Magic Number)来区分。」 对于某一些类型的文件,起始的几个字节内容都是固定的,根据这几个字节的内容就可以判断文件的类型。
常见图片类型对应的模数如下表所示:
文件类型文件后缀魔数JPEGjpg/jpeg0xFFD8FFPNGpng0x89504E47GIFgif0x47494638(GIF8)BMPbmp0x424D
这里我们以阿宝哥的头像(abao.png)为例,验证一下该图片的类型是否正确:
在日常开发过程中,如果遇到检测图片类型的场景,我们可以直接利用一些现成的第三方库。比如,你想要判断一张图片是否为 PNG 类型,这时你可以使用 is-png 这个库,它同时支持浏览器和 Node.js,使用示例如下:
「Node.js」
//npminstallread-chunk
constreadChunk=require('read-chunk');
constisPng=require('is-png');
constbuffer=readChunk.sync('unicorn.png',0,8);
isPng(buffer);
//=>true
「Browser」
(async()=>{
constresponse=awaitfetch('unicorn.png');
constbuffer=awaitresponse.arrayBuffer();
isPng(newUint8Array(buffer));
//=>true
})();
3.2 如何获取图片的尺寸
图片的尺寸、位深度、色彩类型和压缩算法都会存储在文件的二进制数据中,我们继续以阿宝哥的头像(abao.png)为例,来了解一下实际的情况:
❝
528(十进制) => 0x0210
560(十进制)=> 0x0230
❞
因此如果想要获取图片的尺寸,我们就需要依据不同的图片格式对图片二进制数据进行解析。幸运的是,我们不需要自己做这件事,image-size 这个 Node.js 库已经帮我们实现了获取主流图片类型文件尺寸的功能:
「同步方式」
varsizeOf=require('image-size');
vardimensions=sizeOf('images/abao.png');
console.log(dimensions.width,dimensions.height);
「异步方式」
varsizeOf=require('image-size');
sizeOf('images/abao.png',function(err,dimensions){
console.log(dimensions.width,dimensions.height);
});
image-size 这个库功能还是蛮强大的,除了支持 PNG 格式之外,还支持 BMP、GIF、ICO、JPEG、SVG 和 WebP 等格式。
3.3 如何预览本地图片利用 HTML FileReader API,我们也可以方便的实现图片本地预览功能,具体代码如下:
<inputtype="file"accept="image/*"onchange="loadFile(event)">
<imgid="output"/>
<script>constloadFile=function(event){
constreader=newFileReader();
reader.onload=function(){
constoutput=document.querySelector('output');
output.src=reader.result;
};
reader.readAsDataURL(event.target.files[0]);
};</script>
在完成本地图片预览之后,可以直接把图片对应的 Data URLs 数据提交到服务器。针对这种情形,服务端需要做一些相关处理,才能正常保存上传的图片,这里以 Express 为例,具体处理代码如下:
constapp=require('express')();
app.post('/upload',function(req,res){
letimgData=req.body.imgData;//获取POST请求中的base64图片数据
letbase64Data=imgData.replace(/^data:image\/\w ;base64,/,"");
letdataBuffer=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,其他参数会被忽略。
下面我们来看一下具体如何实现图片压缩:
functioncompress(base64,quality,mimeType){
letcanvas=document.createElement("canvas");
letimg=document.createElement("img");
img.crossOrigin="anonymous";
returnnewPromise((resolve,reject)=>{
img.src=base64;
img.onload=()=>{
lettargetWidth,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;
letctx=canvas.getContext("2d");
ctx.clearRect(0,0,targetWidth,targetHeight);//清除画布
ctx.drawImage(img,0,0,canvas.width,canvas.height);
letimageData=canvas.toDataURL(mimeType,quality/100);
resolve(imageData);
};
});
}
对于返回的 Data URL 格式的图片数据,为了进一步减少传输的数据量,我们可以把它转换为 Blob 对象:
functiondataUrlToBlob(base64,mimeType){
letbytes=window.atob(base64.split(",")[1]);
letab=newArrayBuffer(bytes.length);
letia=newUint8Array(ab);
for(leti=0;i<bytes.length;i ){
ia[i]=bytes.charCodeAt(i);
}
returnnewBlob([ab],{type:mimeType});
}
在转换完成后,我们就可以压缩后的图片对应的 Blob 对象封装在 FormData 对象中,然后再通过 AJAX 提交到服务器上:
functionuploadFile(url,blob){
letformData=newFormData();
letrequest=newXMLHttpRequest();
formData.append("image",blob);
request.open("POST",url,true);
request.send(formData);
}
3.5 如何操作位图像素数据
如果想要操作图片像素数据,我们可以利用 CanvasRenderingContext2D 提供的 getImageData 来获取图片像素数据,其中 getImageData() 返回一个 ImageData 对象,用来描述 canvas 区域隐含的像素数据,这个区域通过矩形表示,起始点为(sx, sy)、宽为 sw、高为 sh。其中 getImageData方法的语法如下:
ctx.getImageData(sx,sy,sw,sh);
相应的参数说明如下:
在获取到图片的像素数据之后,我们就可以对获取的像素数据进行处理,比如进行灰度化或反色处理。当完成处理后,若要在页面上显示处理效果,则我们需要利用 CanvasRenderingContext2D 提供的另一个 API —— putImageData。
该 API 是 Canvas 2D API 将数据从已有的 ImageData 对象绘制到位图的方法。如果提供了一个绘制过的矩形,则只绘制该矩形的像素。此方法不受画布转换矩阵的影响。putImageData 方法的语法如下:
voidctx.putImageData(imagedata,dx,dy);
voidctx.putImageData(imagedata,dx,dy,dirtyX,dirtyY,dirtyWidth,dirtyHeight);
相应的参数说明如下:
介绍完相关的 API,下面我们来举一个实际例子:
<!DOCTYPEhtml>
<htmllang="zh-CN">
<head>
<metacharset="UTF-8"/>
<metaname="viewport"content="width=device-width,initial-scale=1.0"/>
<title>图片反色和灰度化处理</title>
</head>
<bodyonload="loadImage()">
<div>
<buttonid="invertbtn">反色</button>
<buttonid="grayscalebtn">灰度化</button>
</div>
<canvasid="canvas"width="800"height="600"></canvas>
<script>functionloadImage(){
varimg=newImage();
img.crossOrigin="";
img.onload=function(){
draw(this);
};
//这是阿宝哥的头像哟
img.src="https://avatars3.githubusercontent.com/u/4220799";
}
functiondraw(img){
varcanvas=document.getElementById("canvas");
varctx=canvas.getContext("2d");
ctx.drawImage(img,0,0);
img.style.display="none";
varimageData=ctx.getImageData(0,0,canvas.width,canvas.height);
vardata=imageData.data;
varinvert=function(){
for(vari=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);
};
vargrayscale=function(){
for(vari=0;i<data.length;i =4){
varavg=(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);
};
varinvertbtn=document.getElementById("invertbtn");
invertbtn.addEventListener("click",invert);
vargrayscalebtn=document.getElementById("grayscalebtn");
grayscalebtn.addEventListener("click",grayscale);
}
</script>
</body>
</html>
需要注意的在调用 getImageData 方法获取图片像素数据时,你可能会遇到跨域问题,比如:
UncaughtDOMException:Failedtoexecute'getImageData'on'CanvasRenderingContext2D':Thecanvashasbeentaintedbycross-origindata.
对于这个问题,你可以阅读 「张鑫旭」 大神 “解决canvas图片getImageData,toDataURL跨域问题” 这一篇文章。
3.6 如何实现图片隐写「隐写术是一门关于信息隐藏的技巧与科学,所谓信息隐藏指的是不让除预期的接收者之外的任何人知晓信息的传递事件或者信息的内容。」 隐写术的英文叫做 Steganography,来源于特里特米乌斯的一本讲述密码学与隐写术的著作 Steganographia,该书书名源于希腊语,意为 “隐秘书写”。
目前有多种方案可以实现图片隐写,以下是几种常见的方案:
篇幅有限,这里我们就不继续展开,分别介绍每种方案。
本篇已完结
推荐JavaScript经典实例学习资料文章《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
《》
作者: semlinker
转发链接:https://mp.weixin.qq.com/s/i3ynTtPJOECoAYfqHFoo3Q
Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved