本文阿宝哥会为小伙伴们盛大介绍用于图片处理的十个 「“小辅佐”」,他们各个身怀绝技,具有迷糊、紧缩、裁剪、旋转、组成、比对等技术。相信知道他们之后,你将可以轻松应对大多数的* f { = D图片处理场景。
阅读阿宝_ N 6 a ;哥近期抢手文章(感谢掘友的鼓励与支撑):
-
1.2W字 | 了不起的 TypeScript 入门教程(1100+ 个) -
让人眼前一亮的 10 大 TS 项目(660+ 个) -
一文读懂 TypeScript 泛型及运用(h [ S 7.8K字)(519+ 个) -
前端存储除了 localStorage 还有啥(380+ 个) -
你不知道的 Web Workers (上)[7q 8 _ Y K F L i.8K 字 | 多图预警](253+ 个)
❞
不过在介绍 「“小辅佐”」 前,阿宝哥会先介绍一些图片相关的根底知识。此外,为了让小伙伴们可以学习更多图片相关的知识,阿宝哥精心准备W w T ] W了 「“阿宝哥有话说”」 章节。该章节你将会学到以下知识:
-
怎样差异图片的类型(非文件后缀名); -
怎样获取图片的规范(非右键检查图片信息); -
怎P K U ^ E么预览本地图片(非图片阅读器); -
怎样完结图W = ! g片紧缩(非图片紧缩东西); -
怎样M r g操作位图像素数据(非 PS 等图片处理软件); -
怎样完结图片隐 ` n ) P $ R O写(非肉眼可见)。x X * R
十个图片处理 “小辅佐” 现已现已刻不容缓想与你碰头,还在犹疑什么?赶紧动身吧!
一、根底知识
1.1 位图
「位图图像(bitmapU g F 7 D v),亦称为点阵图像或栅格图像,是由称作像素(图片元素)的单个点组成的; x C G r I h L &。」 这些点可以进行不同的摆放和染色以构成图样。当扩展位图时,可以看见赖以构成整^ i P w s个图像的许多单个方块。扩展位图规范的效果是增大单个像素,然后使线条和形状显得良莠不齐。
「用数码相机拍照的照片、扫U = b 1 F Z W W描仪扫描的图片以及核算机截屏图等都归于位图。」 位图的特% Z /点是可以表现色彩的改动和色彩的细微过渡,– , z @ y发生传神的效果,缺点是在保存时需求记录每一个像素的方位和色彩值,占用较大的存储空间。常用的位图处理软件有 Photoshop、Painter 和 Windows 体系自带的画图东西等。
分辨率是位图不可逾越的壁垒,在对位图进行缩放、旋转等操作时,无法出产新的像素,因此会扩展原有的像T 1 ! & 0 W h素填补空白,这样会让图片显得不清楚。
(图片来L N H % p历:https://zh.wikipedia.oD p Arg/wiki/%E4? 9 N 9 5 L%BD%8D%E5%9B%e & ` W oBE)
图中的小方块被称为像素,这些小方块都有一个清晰的方位和被分/ W Y d J J !配的色彩数值,小e Z : D _ f @方格颜4 ? R | $ j , T色和方7 N 5 B 8 i o位就决议该图像所出现出来的姿势。
可以将像素视J G = t ? N为整个图像中不可分割的单位或许是元素。+ $ W / i「不可分5 X W : 7 E m割的意思Z R V J o是它不| e L V ; A I / 6可以再切割成更小单位抑或是元素,它是以一个单一色彩的小格存在。」 每一个点阵图像包含了必定量的像素,这些像素决议图像在屏幕上所出现的大小。
1.2 矢量图
所谓矢量图,便是运用直线和曲线来描绘的图形,构成这些图形的元素是一些点、线、矩形、多边形、圆和弧线等,「y 2 1它们都是通过数学公式核算获得的,具有修正后不失真的特色。」 例如一幅画的矢量图形实践上是由线段形b + v S x 9 7 C h成外框概括,由外框的色彩以及外框所封闭的色彩决议画闪现出的色彩。
「矢量图以几何图形居多,图形e 5 l { { I Q I可以c P Y ( c无限扩展,不变色、不迷糊。」 常用于图像、标志、VI、文字等规划。常用软件有:CorelDraw、Illustrator、Freehand、XAQ a V B xRA、CAD 等。
这| ~ ~ %儿我们以 Web 开发者比较了解的 SVG(. [ 1 , |「Scalable Vector Graphics —— 可缩放矢量图形」)为例,来了解m E G / l @ M &一下 SVG 的结构:
可缩放矢量图形(英语:Scalable Vector Graphics,SVG)是一种依据可扩展符号言语(XML),用于描绘二维矢量图形的图形格式。SVG 由( | P Z 2 ( x W3C 拟定,是一个开放规范。
SVG 首要支撑R ] P k B 3 r以下几种闪现政策n * % | ^ C 6:
-
矢量闪现政策,底子– D c D d g矢量闪现政策包含矩形、圆、椭圆、多边形、直线、恣意曲线等; -
嵌入式外部图像,包含 PNG、JPEG、SVG 等; -
文字政策。
了解完位图与矢量图的差异,下面我们来介绍一下位图的数学| ^ ` 6 , Z标明。
1.3 位图的数学标明
位图的像素都分配有特定的方位和色彩值。每个像素的色彩信息由 RGB 组合或许灰度& A s @ I D C R值标明。
依据位深度,可将位图分为1、4、8、16、24 及 32 位图像等。每个像素运用的信息位数越多,可用的色彩就越多,色彩表现就越传神,相应的数据量越大。
「1.3c , ^.1 二值图像」
位深度为 1 的像素位图只需两个或许的值(黑色和白色),所以又称为二值图像。二值图像的像素点只需是非两种情况,因此每个像素点可以由 0 和 1 来标明。
比如一张U n u m ` 4 * 4 二值图像:
1 1 0 1
1 1 0 1
1 0 0 0
1 0 1 0
「1.3.2 RGB 图像」
RGB 图像由三个色彩通道组K ! {成) ~ y % * a,其间 RGB 代表红、绿、蓝三个通道的色彩。8 位/通道的 RGB 图像中的每个通道有 256 个或许的值,这意味着该图像有 1600 万个以上或许的色彩值。有时将带有 8 位/通道(bpc)的 RGB 图像称作 24 位图像(8 位 x0 A & ; [ g D 3 通道 = 24 位数据/像素)。一般将运用 24 位 RGB 组合数据位标明的的位图称为真五颜六色位图。
RGB 五颜六色图像可由三种矩阵标明:一种代表像素中赤色D # X S B H的强度,一种代表绿色,另一种代表蓝色。
(图h C j b ] [ Y片来历:https://freecontent.manv / n 6 ~ p N K 9ning.com/the-computer-vision-pipeline-part-2-input-images/)
「图像处理的实质实践上便是对这些像素矩阵进行核算。」 其实位图中的图像类型,除了二值图像和 RGB 图像之外,还有灰度图像、索引图像和– # 2 YUV 图像。这儿我们不做过多介绍,感兴趣的小伙伴,可以自行查阅相关资料。
二、图片处理库
2.1V l I B H 8 K P AlloyImage
❝
依据 HTML 5 的专业级图像处理开源引擎。
https://github.c$ g W I % @ M Iom/AlloyTeam/AlloyImage
❞
AlloyImage 依据 HTML5 技术的专业图像处理库,来自腾讯 A6 , u A 3lloyTeam 团队。它具有以下功用特性:
-
依据多图层操作 —— 一个图层的处理不影响其他图v ^ A层; -
与 PS 对应的 17 种图层混合方式 —— 便于 PS 处理教程的无P : 2缝搬迁; -
多种底子滤镜处理效果 —— 底子滤镜不断丰富、可扩展; -
底子的图像调度功用 —— 色相、饱和度、对比度、亮度、曲线等; -
简略便当的 API —— 链式处理、API 简洁易用、传参活络; -
多种组合效果封装 —— 一句代码轻松完结一种风格; -
接口一致的单、多线程支撑 —— 单、多线程切换无需更改一行p k u x ~代码,多线9 Z x s程保持便当 API 特性。
关于该库 AlloyTed N Y r l 5am 团队主张的运用场景, D { c M B j G如下:
-
桌面软件客户端内嵌网页运转方法 >>> 打包 Webkit 内核:用户较大头像上传风格处理、用户相册风格处理(处理时间均匀 < 1s); -
Win8 Metro 运用 >>> 用户上传头像,比较小的图片风格处理后上传(Win8 下 IE 10 支撑多线程); -
Mobile APPP k Y >>> Andriod 渠道、iOS 渠道小图风格 Web 处理的需求,如 PhoneGap 运用,在线头像上传时的风格处理、Mobile Web 端同享图片时风格处理等。
「运用示例」
// $AIu , } l a -或AlloyImage初始化一个AlloyImaQ n H k q 3 E = 0ge政策
var ps = $AI(img, 600).save(+ @ a /'jpg', 0.6);
// save将组成图片保存成base64格式字符串
var string =m n ] } @ AlloyImage(img).save('jpo 1 ` 6 s v 8 8g', 0.8);
// saveFile将组成图片下载到本地
img0 / n.onclick = function(){
AlloyImage(this).saveFile('处理后图像.jpg', 0.8);
}
「在线示例」
❝
http://allor Z ` g = Wyteam.githubC E !.io/AlloyImage/
❞
(图片来历j P y ! ~ M e E f:http://alloyteam.giR I 4 ~ a O L Pthub.io/AlloyIM k R ( mmage/)
2.2 blurify
❝
blurify.js is a; F l ( z f tiny(~2kb) library to blure ( y P E p ~ j qred pictures, support graceful downgrb w # * H lade from
css
mode tocanvas
mode.https://github.com/JustClear/blurify
❞
blurify.js 是一个用于图片迷糊,很小的 JavaScript 库(约 2 kb),并支撑从& B . 3 K Q CSS 方式到 Canvas 方式8 o v的典雅降级。该插件支撑三种方式:
-
css 方式:运用 filter
特色,默许方式; -
canvas 方式:运用 ca) 2 Z 8 -n+ l ? M . 9 ) 4vas
导出 base64; -
auto 方式:优先运用 css 方式,不然o n ^ 0 b a z # E自动切换到 canvas 方式。
「运用示例」
import blurify from 'bluG . =rify';
new blurify({
images% v @ & k 8 &: document.querySelectorT * k q / pAll('.blurify'),
blur: 6,
mode: 'css',
});
// or in shorthand
blurify(6, document.querySelectorAll('.blurify'));
「在线示例」
❝
https:/9 f G F o + E/justclear.githur i B 2 Sb.io/blurify/
❞
(图片来历:https://H s { N – a l 3justclear.github.iW f c U x b 4o/blura N , % Rify/)
看到这儿是不是有d g k ? l R E些小伙伴觉得仅仅迷糊处理算了,觉得不过瘾,能不能来点更酷的。嘿嘿,有求必应!阿宝哥立马来个 「“酷炫叼”」 的库 —— midori,该库用于为背景图创建动画,运用 tK o $ 2 & s 4hree.js 编写并运用 WebGL。C E L 0 2 W h本来是想给个演示动图,无奈单个 Gif 太大,只能放个领会地址,感兴趣的小伙伴自行领会一下。
❝
midori 示例地址:https://aeroheim.github.io/midori/
❞
2.3 cr% N 9 x ^ @opperjs
❝
JavaScript image cropper.
https://github.com/fengyuanchen/cropperjs
❞
Cropper.js 是一款十分健壮却又简略的图片裁剪东西,它可以进H } ^行十分活络的配置,支撑手机端运用,支撑包含 IE9 以上# 2 & v a G . s w的现代浏览器。它可以用于满意比如裁剪头像上传、产品图片修正之n f C y U ; 5类的需求。
Cropper.js 支撑以下特性:
-
支撑 39 个配置选项; -
支撑 27 个方法; -
支撑 6 种工作P o e A j; -
支撑 touch(移动端); -
支撑缩放、旋转和翻转; -
支撑在画布上裁剪; -
支撑在浏览器端通过画布裁剪图像; -
支撑处理e { B = y H Exif 方向信息; -
跨浏览器支撑。
❝
可交换图像文件格式(英语:Exchangeable image file format,官方简称 Exif),是专门为数码相机的照片设定的文件格式,可以k = q P _ 0记录数码照片的特色信息和拍照数据。Exif 可以附加于 JPEG、TIFF、RIFF 等文件之中,为其增加有关数码相机拍照信息的内容和索引图或图像处理软件的版别} c 4 c _信息。
Exif 信息以 0xFFE1 作为开头符号,后两个字节标明 Exif 信息的长度。所以 Exif 信息最大为 64 kB,而内部选用 TIFF 格式。
❞
「运用示例」
/] A = , = 3 A/ import 'cropperjs/dist/cropper.c; 4 F n 1 ]ss';; u T v y : ] 1
import Cropper from 'cropperjs';
const image =X ? u B document.getElementById('ims N lage');
const cropper = new Cropper(image, {
aspectRatio: 16 / 9,
crop(event( n U m + & } C) {
console.log(event.dX V q H Xetail.x);
console.Z w { 4 - [log(event.detail.y);
console.log(event.detail.width);
console.log(event.detail.height);
console.log(event.detail.rotate);
console.log(eve^ _ _ v I ] & : tnt.detl V 1ail.scaleX);
console.log(event.detail.scaleY);
},O X . , b f ,
});+ f 5 z z 3 # 7 z
「在线示例」
❝
https://fengyuanchen.github.io/cropperjs/
❞
2.4 compressorjsS ( ? J V
❝
JavaScript image compressor.
https://github.com/fengyuanchen/compressorjs
❞
cm qompressorjs 是 JavaScript 图像紧缩器。运用浏览器原生的 canvas.toBlob
API 进行紧缩作业,这意味着它是有损紧缩。一般的运用场景是,在浏览器端图片上传o e = Y S $ a K I之前对其进行预紧缩。
在浏览器端要完结图片紧缩,除了运用 canvas.toBlob
API 之外,还能| m l c ( w ?够运用 Canvas 供应的另一个 API,即 toDataURL
API,它接收 type
和 encoderOptions
两个可选参数。
其间 type
标明图片格式,默许为 image/png
。而 encoderOptions
用于标明图片的质量,在指定图片格式为 image/jpeg
或 imB a AageJ d t U/webp7 ? o y d s ]
的情况下,可以从 0 到 1 的区间内挑选图片的质量。假设超出取值T 6 R 9 b i Q K ?规划,将会运用默许值 0.l v $ 492
,其他参数会被忽略。
相比 canvas.toDataURL
API 来说,canvas.toBlob
API 是异步的,因此多了个 c& @ 1 P : h d 4 /allback
参数,这个 callback
回调方法默许的第一个参数便是转化好的 blob
文件信息。canvas.toBlob
的签名如下:
canvas.to# A * q { iBlob(callback, mimeType, qualityArgument)
「运用示例」
import axios frok T ) n ? 2 Vm 'axios';
import Compressor from 'compressorjs';
// <input type="file" id="file" accept="iO F o u :mage/*">
document.getElementById('file').addEventK 1 DListener('change', (e) => {
const file = e.target.files[0];
if (!file) {
return;
}
new CompressoH } D N r = K 1r(file, {
quality: 0.6,
success(result) {
const formData = new FormData();
// The third parameter is required for server
formData.append('file', result, result.name);
// Send the compressed image file t` $ b go server with XMLHttpRequest% A }.
axios.post('/path/to/upload', formData).then(() => {
console.log('Upload successE $ ! 2 D 4 6 k');
});
},
error(err) {
coF : `nsole.log(err.message);
},
});
});
「在= ~ M . O h B ~ h线示例」
❝
http} | J @ =s://fengyuanchen.github.io/compressorjs/
❞
2.5 fabric.js
❝S 9 i s |
Javascript Canvas Library, SVG-to-Canvas (& canvas-to-SVG) Parser.
https://github.com/fabricjs/fabric.js
❞
Fabric.js 是一个结构,可让你轻松运用 HTML5 Canvas 元素。它是一个位于 Canvas 元素之上的交互式政策模型,一起也是一个 「SVG-to-canvas」 的解析器。
运用 Fabric.js,你可以在画布上创建和填充政策。所谓的政策w L S .,可以是简略的几何形状,比如矩形,圆形,椭圆形,多边形,或更复杂的形状,包含数百或数千个简略途径。然后,你可以运用鼠b H h = ? 0 v &标缩放,移动和旋转这些政策。并修正它们的特色 —— 色彩,透明度,z-index 等。此外你还可以一起操作这些政策,即通过简略的鼠标挑选将它们分组。
Fabric# F B v y.js 支撑全部干流的浏览器,具体的兼容情况如下:
-
Firefox 2+ -
Safari 3+ -
Opera 9.64+ -
Chrome(全部版别) -
IE10,IE11,Edge
「运用示例」
<!Di n QOCTYPE html>
&{ i & 4 o Y r lt;html>
<head></head>
&T } m ] . j ~ *lt;bodN B . ] }y>
&* E z | Q k Olt;canvas id=Q G s O 7 S - p c"canvas" width="300" hei? ; v B - Z , Gght= % F d ; H m"300"&B 3 o . t + M u .gt;</canvas>
<script src=4 T j S M "lib/fabric.js"></script>
<script>
var canvas = new fabj 2 Q ,ric.Canvas('canvas');
var rect = new fabric.Rect({
top : 100,
left : 100,
width : 60,
height : 70,
fill : 'red'
});
canvas.add(rect);
</script>
</bodU 9 D ^ $ e M b qy>
</html>
「在线示例」
❝
http://fal ~ F * jbricjs.com/kitchensink
❞
(图片来历:https:/g E u ] a F %/github.com/fabricjs/fabric.js)
2.6 Resemble.js
❝
ImA 8 S T ~ G ! ~age analysis and comparison
https://githI { – 1 8 i { tub.com/rsmbl/Resemble.js
❞
Resemble.js 运用 HTML Canvas 和 JavaScript 来完结图片的分析和比较。兼容大于n 6 i T B K L 8.0 的 Nod= ^ I t 7 A # de.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 },
getImy g j Z 7ageDataUrl: fun, $ 6 f { & Iction(){}
}
*/
});
「在线示例」
❝
http://rsmbl.github.i= 8 u O _o/Resemble.js/
❞
2.7 Pica
❝
Resize image in browseC R K 0 2 7 nr with high quality and high speed
https://github.com/nodeca/pica
❞
Pica 可用于在浏览器中调整图像大小,没有像素化而且相当快。它会自动挑选最佳的可 # f U O 8 v 0用技术:we( : . | k ~ M 3 Wbworkers,webassembly,create! [ @ PImageBitmap,纯 JS。
借助[ J + Pica,你可以完结以下功用:
-
减小大图像的上传大小,节约上传时间; -
在图像处理上节约服务器资源; -
在浏览器中生成缩略图。
「运用示例」
const pica = requirw : m Oe('pica')();
// 调整画布/图片的大小
pica.resize(from, to, {
unsL 9 a m GharpAmount: 80,
unsharp+ m ] P 8 $ l / DRadius: 0.6,
unsharpThreshold: 2
})
.then(result => console.log('resize done!'));: r S % H j U i
// 调整大小并转化为Blob
pica.resize(from, to)
.thN x Oen(result => pica.toBlob(result, 'image/jpeg', 0.90))
.then(blob => console.log('resized to@ ` x ! r canvas &V , b ~ L z t b; created blob!'));
「在线示例」
❝
http://nod, 7 W , c Peca.github.io/pica/demo/
❞K 2 R
2.8 tui.image-editor
❝
Full-featured photo image e: , V aditor using canvas. It is really easy, and it comes wi5 ; @th great filters.
httpo y x V X ys://git o U [ v b 3 S Jhub.com/nhn/tui.imagL m h N u g he-editor
❞
tui.image-editor 是运用 HTML5 Canvas 的全功用图像修正器~ , e。它易于运用,并供应健壮的过滤器。一9 t q v P } ;同它支撑对图像进行裁剪、翻转、旋转、4 / o E 4 F绘图、形状、文本、遮罩和图片过滤等操作。
tui.image-editX / H H cor 的浏览器兼容情况如下:
-
Chrome -
Edge -
Safari -
Firefox -
IE 10+
「运用示例」
// Image editor
var imageEditor = new tui.ImageEditor("#tui-image-editor-conta ^ 3 O N G y -iner", {
includeUI: {
loadImao x - U 5 Vge: {
path: M J 4 B L _ 5 e R"img/sampleImage2.png",
naY @ p $ O 4 Rme: "SampleImage"I H x,
},
theme: blackTheme, // or whiteThemeI c E Z @ C _
initMenu: "filter",
menuBarPosition: "bottom",
},
cssMaxWidth: 700,
cssMaxHeight:* Q i 8 P 5z p q | t c D00,
usagr m ( b { s NeStatistics: false,
});
window.onresize = function () {
imageEditor.ui.rU 3 wesizeEditor();
};
在线示例
❝
https://ui.toast.coM e 7 /m/5 ? % ; _ M n L [tui-image-editor/
❞
2.9 gif.js
❝
JavaScript GIF encoding library
https:Q c t P A – R//github.com/jnordberg/gif.js
❞
gif.js 是运转在浏览器端的 JavaScript GIF 编d 1 W ) { Q :码器。它运用类型化数组和 Web Worker 在后台烘托每一帧,速度真的很快。该库可作业在O m J支撑:Web Wor= X m { f _ n {kers,File API 和 Typed Arrays 的浏览器中。
gif.js 的浏览器兼容情况如下:
-
Google Chrome -
Firefox 17 -
Safari 6 -
Internet Explorer 10 -
M O 3 T s T m xobile Safari iOS 6
「运用示例」
var gif = new GIF({
workers: 2,
quality: 10
});
// add an image+ J V 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)i 5 q 5 G {
window.open(URL.createObjectURL(blob));
});
gif.render();
「在线示例」; W F 5 V
❝
http://jnordberg.github.io/gif.js/
❞
2.10 Sharp
❝
High performance Node.9 M w Cjs image processing, the fD : & B / m 4 .astestr { – ( module to resize JPEG, PNG, Weba X J t E } ] : 8P and TIFF imaG b Zges. Uses the libvips library.
https://github.com/lovell/sharp
❞
Sharp 的典型运用场景是将常见格式的W l G大图像转化为规范较小,对网络友爱的 JPEG,PNG 和/ o [ s WebP 格式的图像。因为其内部运用 libvips ,使得调i v [ ! e K整图像大小一般比% – ( m g s运用 ImageMagick 和 GraphicsMagick 设置快 4-5 倍 。除了支撑调整图像大小之外,Sharp 还支撑旋转、提取、组成和伽马校对等功用。
Sharp 支撑读取 JPEG,PNG,B 1 p z 5WebP,TIFF,GIF 和 SVG 图像。输出图像可以是 Jq y m o DPEG,PNG,WebP 和 TIFF 格式K _ {,也可以是未紧缩的原始像素数据。
「运用示例」
// 改动图像规范
sharp(inputBuffer)
.resize(32, { ( _ C0, 240)
.toFile('output.webp', (err, info) => { ... });
// 旋转输入图像并2 2 F v ` 5 C ( l改动图片规范
sharp('input.jpg')
.rotate()
.resize(200)
.toBuffer()) { x V 0 &
.then( data => { ... })
.catch( err => { ... });
「在线示例」
❝
https://segmentfault.com/a/1190000012903787
❞
该示例是来自阿宝哥 18 年写的 “Sharp 牛刀I 8 G j b v _小试之生成专属同享图片” 这篇文章,首要是运用 Sharp 供应的图片组成功用为每个用户生成专属的同享海报,感兴趣的小伙伴可以阅读一下原文哟。
const sharp = require("sharp")Y N J : / j R;
cp _ Y R lonst TextToSVG = rev S j r K } z Equire("text-to-svg");
const path = require("path");
// 加载字体文件
const textToSVG = TextT& m 1 I XoSVG.loadSync(path.join(__dirname, - y | j f 2 D q"./simT 7 T Mhei.tt* ; / Gf"k ! . /));
// 创建圆形SVG,用于完结头像裁剪
const rounded4 y _ g N { s +Corners = new Buffer(
'<svg><cI ! Y r b * ] Pircle r="90" cx="90" cy="90"/></svg>'
)q l 6 R w;
// 设置SVG文本元素相关参数
const attributes = { fill: "white" };
const svgOptionsM @ . U @ = {= f + 0 W F
x: 0,
y: 0,
fontSize: 32,
anc+ ] v f $ Hhor: "top",
attributes: attributes
};
/**
*3 p 2 l 运用文本生成SVG
* @param {*} text
* @par& b H 6 g (am {*} options
*/
functio# ; [ k a H h xn textToSVGFn(text, options = sh ` FvgOptions) {
return textTo! E QSVG.getSVG(text, o/ a X j _ gptions);
}
/**
* 图层叠加生成同享图片
* @param {*} options
*
*/
async function genShareImage(options) {
const { backgro$ N ^udPath, avatarPath, qrcodePath,
uset ( E !rName, woa N H H { # ards, likes, outFilePath
} = options;
// 背景图片
const backgroudB0 R 4uffer = sharp(path.join(__dig G Xrname, backgroudPath)).toBuffer({
resolveWithObj; ) HectR c -: true
});
const backgr| p 9 x m o _ 3oundImageInfo = await backgroudBuffer;
// 头像图片
const avatarBuffer = awa} 6 & h N = )it genCircleAvatar(path.join(__dirname, avatar_ U 2 k kPatp K , N ph));
// 二维码图片
const qrCodeBuffer = await sharp(path.join(_* a h i /_dirname, qu 2 k . ( % | TrcodePath))
.resize(180)
.toBuffer({
resolveWithObject: true
});
// 用S + m K f s户名
const userNameSVG = textToSVGFn(userName);
// 用户数据t ? S g l k [
const userDataSVG = textToSVGFn(`写了${words}个字 收成${likes}个赞`);
const userNameBuffer = await sharp(new Buffer(userNameSVG)).toBuffer({
resolveWithObject: true
});
c4 - _onst userDataBuffer = await sharp(new Buffer(userDataSVG)).toBuffer({
resolveWithO: m U a [ V abject: true
});
const bu. R J l qffers = [avatarBuffer, qrCodeBuffer, userNameBuffer, userDataBuffef } ] G - hr];
// 图层叠加参数列表
const overlayOptions = [
{ top: 150, leftZ Y E 4: 230 },
{ top: 861, left: 227 },
{
top: 365,
left: (backgr9 y t ; T HoundImageInfo.info.w( p G a 0 qidth - userNameBuffer.inf9 X [ do.width) / 2
},
{
top: 435,
left: (backgroundImageInfo.info.width - userDataBuffS b q per.info.width) / 2
}
];
// 组合多个图层:图片+文字图层
retur; N & ) s 5 n ? [n buffers
.p p O r : 8 J | 3reduce((input, overlay, index) => {
return input.then(result => {
console.dir(overlay.info);
return sharp(result.data)
.oveB H W c i Y = L ArlayWi= A Z fth(overlay.) C /dataP $ 2 j v i c, overlayOptio? B V nns[index])
.toBuffer({ resolveWithObject: true });
});
}, backgroudBuffer)
.then((data) => {
ret= 2 2urn sharp(data.data).toFile(outFilePath);
}).catch(error => {
throw new Error('Generate Share Image Failed.');
});
}
/**
* 生成圆形的头像
* @param {*} avatarPath 头像途径
*- o ^ f E V p E/
function genCircleAvatar(avatarPath) {
return sharp(avatarPath)
.R 3 * Gresize(180, 180)
.overlayWith(roundedCorners, { cutout: true })
.png()
.toBuffer({
resolvr 8 D p CeWC | U 7 . U ZithObject: true
});
}
module.expM y w * T F M v 0orts = {
genShareImage
};
三、阿宝哥有话说
3.1 怎样差异图片的类型
「核算机并不是通过图R a 0 R %片的后缀名来k P } u n p v Z M差异不同的图片类型,而是通过 “魔数”C a 8 ? N g # c(Magic Number)来差异。」 关于某一些类型的文件,起始的几个字节内容都是固定的,跟据这几个字节的内容就可以Z 8 { o * i判别文件的类型。
常见图片类型对应的魔数如下表所示:
文件类型 | 文件后缀 | 魔数 |
---|---|---|
JPEG | jpg/jpeg | 0xFFD8FF |
PNG | png | 0x89504E47 |
GIF | gif | 0x47494638(GIF8) |
BMP | bmp | 0x424D |
这儿我们以阿宝哥的头像(abao.png)为例,验证一下该图片的类型是否正[ W t + f 5 , 4 I确:
在日常开发进程G L 0 * ? ! _ d w中,假设遇到检测图片类型的– Y X _ A B l H f场景,我们可以直接运用一些现成的第三1 q L方库。比如,– W { | ` I 8 + y你想要判别一张图片是否为 PNG 类型,这时你可以运用 is-png 这个库,它一起支撑浏览器和 Node.js,运用示例如下:
「Node.js」
// npm install read-chunk
const readChunk = require('read-chunk');
const isPng = require(b V n L i ] :'is-png');
const buffer = readChunk.sync('unicorn.png', 0, 8);
isPng(buffer);
//=> true
「Browser」
(async () => {
const response = await fetch(u g 8 V f { G P'unicorn.png');
const) l ^ ` buffer = await response.arrayBuffer();
isPng(new Uint8Array(buffer));
//=> truz , ( , / % G Ue
})();
3.2 怎样获取图片的规范. N { e V x G # `
图片的尺W ) d 7度、位深度、色彩类型和紧缩算法都会存储在文件的二进制数据中,我们继续以阿宝哥的头像(K p 2 7 i q .abao.png)为例,来了解一下实践( F J Y u b的情况:
❝
528(十进制) => 0x0210(十六进制)
560(十进制)=> 0xK 0 , – R0230(十六进制)
❞
因此假设想要获取图片的规范,我们就需求依据不同的图片格式– / U h & g对图片二进制数据进行解析。走运的是,我们不需求自己= z [ . m – u ]做这件事,image-sY 2 eize 这t N w v m个 Node.js 库现已帮我们完结了获取干流图片类型文件规范的功用:
「同步方法」
var sizeOfS M 6 X u = require('imau M sge-[ ; d n 6 r Nsize');
var dimensions = sizeOf('images/abao.png');
console.log(dy | y ) ^ `imensions.width, dimensions.height);
「异步方法」
var sizeOf = require('image-siz6 I P Xe');
siz* 6 t 0 oeOf('image, w w +s/abao.png', function (err, dimenso c ? h - , ] H fions) {
console.log(dimen& p U [ $sions.width, dimen5 - R g , g t _sions.M D K b q f Q =height);
});
image-si; ] _ 8 i q Ize 这个库功用仍是蛮健壮的,除了支撑 PNG 格式之外,还支撑 BMP、GIF、ICO、JPEG、SVG 和 WebP2 M ; F * L 等格式。
3.3 怎样预览本地图片
运用 HTML FileReade~ v 1 S (r API,我们也可以便当的完结图片本地预览功用,具体代码如下:
<input type="file" accept="image/*" onchange] 3 i H ) H="loadFile(event)">
<img id="outputm q T 8 D ! G"/>
<script>
const loadFile = function(event) {
const reader = new FileReader();
reader.onl6 B D + boad = fN ~ 2 : ^unction(){
const output = docum n $ 7 ] } 3 k :ent.querySelector('output');
output.src = reader.result;
};
reader.readAsDataURL(event.target.files[0])s } $ =;
};
</script>
在完2 0 k [ O本钱地图片预览之后,可以直接把图片对应的 Data URLs 数据提交到6 v r服务器。针对这种景象,服务端需求I + j做一些相关处理,才能正常保存上传的图片,这儿以 Express 为例,具体处理代码如下:
const app = require('exph y h & 4ress')();
app.post('/upload'& P i, function(req, res){
let imgData = req.body.imgDv L pata; // 获取POST恳求中的base64图片数据
lx } % w Z 3 p aet base64Data = imgData.re@ T ( x uplace(/^data:i} ; xmage/w+;base64,/, "N w M ` j F");
let dataBuffer = Buffer.from(base64Data, 'base64')G 6 j m 6 - 8 ] :;
fs.writeFile("image+ T 0 r = c r ; /.png", dataBZ : 7 (uffer, function(err) {
if(err){
res.send(err);
}else{
res.send("图片上传成功!");
}
});
});
3.4 怎样完结图片紧缩
在一些场合中,我们希望在上传J D L * ) b 8本地图片时,先对图片进行必定的紧缩,然后再提交到服务器,然后削减传输的数据量。在前端要完结图片紧缩,我们可以运用 Canvas 政策供应的 toDataURL()
方法,该方法接收 type
和 encoderOptions
两个可选参数。
其间 type
标明图片格式,默许为 io - }mage/png
。而 encoderOptions
用于标明图片的质量,在指定图片格式为 image/jpeg
或 ig l F p % !mage/webp
的情况下,可以从 0 到 1 的区间内挑选图片的质量。假设超出取值规划O Y Q i / 5 g t @,将会运用默许值 0.92
,其他参数会被忽略。
下面我们来看一下具体怎样完结图片紧缩:
function compress(base64, quality, mimeType) {
let canvasO , f 5 D - = document.createElement("canvas");
let img = document.createElement("img");
img.crossOrigin = "anonymous";
return new Promise(6 8 /(. Z M V t 2resolve, reject) => {
imgx 7 J 5 7 z G E.src = base64;
img.o _ u ) s Xnload = () => {
let targetWidth, targetHeight;
if (img.width > MAX_WIDTH) {
targetWidth = MAX_WIDTH;
targetHeight = (img.height * MAX_WIDTH) / img.w* D L ; % n Y / Didth;
} else {
targc d | s 8 6 OetWidth = img.width;
targetHeight = img.height;
}
canvas.width = targetWidth;R / H |
canvac j J R }s.height = targetHeight;
let ctx = canvas.u 9 ` N ) 7 )getContext("2d");
ctx.c| P n L x b a OlearRect(0, 0, targetWidth, targetHeight); // 清除画布
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
let imageData = cag ` t i g Pnvas.toDataURL(mim] - @ d P A )eType, quality / 100);
resolve(imageData);
};6 - l @ p
});
}
关于回来的 Data URL 格式的图片数据,为了进一步削减r { S f 9 ( v F传输的数据量,我们可以把它转化为 Blob 政策:
f V W nunc: W Y f m 5 }tion dataUrlToBlob(base64, mimeType) {
let bytes = window.atob(baseb 5 # | w c H C 564.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 XMLHttpReques2 : h ^t();
formDak B Tta.append("images { S ^ t [ G z ["[ I ! * b `, blob);
request.open("P` = +OST", url, true);
request.send(formData)y s k;
}
3.5 怎样操作位图像素数据
假设想要操作图片像素数据,] ~ Z 9 9 ! 1 W o我们可以运用 CanvasRenderingContext2D 供应的 getImageData
来获取图片像素数据,其间 getImageData() 回来一个 ImageData 政策,用来描绘 canvas 区域隐含的像素数据,这个区域通过矩形标明,起始点为(sx, sy)、宽为 sw、高为 sh。其间 getImageData
方法的语法如下:
ctx.? 6 4 = p X 6 ` Age8 s ZtImageData(sx, sy, sw,k ? = y . } Q sh);
相应的参数说明如下:
-
sx:将要被提取的图像Q T y b $数据矩形区域的左上角 x 坐标。 -
sy:将要被提取的图像数据矩形区域的左上角 y 坐标。 -
sw:将要被提取的图像数据矩形区域的宽度。 -
sh:将D U 8 I V : i要被提取的图像数据矩形区域的高度。
在获取到图片的像素数据之后,我们就可以对获取的像素数据进行处理,比如进行灰度化或反色* f T 4 x k处理。当完结处理后,若要在页面上闪现处理效果,则我们需求运用 CanvasRenderingContext2D 供应o m n的另一个 API —— putImageData
。
该 API 是 Canvas 2D API 将数据从已有的 ImageData 政策制造到位图的方法( ^ L c Q l I X。 假设供应了一个制造过的矩形,则只制造该矩形的像素。此方法不受画布转化矩阵的影响。putImageData 方法的语法如下:
void c9 m . g ; L ^ Vtx.putImageData(imagedata, dx, dy);
void ctxb I ; # @ d.putImageData(imag? M N V K ] fedata, dx, du d Wy, dirtyX, dirtyY, dirtyW5 - ` w 3 D 5idth, dirt7 Z y T t M h # $yHeight);
相应的参数说明如下:
-
imageData: ImageData
,包含像素值的数组政策。 -
dx:源图像数据在政策画布中的方位偏移量(x 轴方向的偏2 _ ( m K E J移量)。 -
dy:源图像数据在方k f [ _ r M针画布k i ^ v 5 [ p L中的方位偏移量(y 轴方向的偏移量)。 -
dirtyX(可选):在源图像数h T + _ i N据中,矩形区域左上角的方位。默许是整个图像数据的左上角(x 坐标)。 -
diA b v JrtyY(可选):在源图像数据中,矩形区域左上角的方位。默许是整个图像数据的左上角(y 坐标)。 -
dirtyWidth(可选):在源图像数据中,矩形区域的宽度。默许是p K J图像数据的宽度。 -
dirtyHeight(可选):在源图像数据中,矩形区域的高度。默许是图像数据的高度。
介绍完相关的 API,下面我们来举一个实践比如:D j u w 0 / W
&lr ` Q [ 1 b 5t;q x W x E 8 l!DOCTYPE html>
<html lang=i Z c J P X"zh-CN">
<head>
<1 @ O X { 9 F e;meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<f b X 3 d &;title>图片反色和灰度化处理</e 8 N V A ] d n title>
</head>
<body onload=T e 2"loadImage()">
<+ W i K 6;div>
<button id="invertbtn">反色</button>
<button id="grayscalebtn">灰E G | H度化</butN N V ;ton>
</div>
<canvas id="canvas" width="800" height="600"></canvas>
<script>
function loadImage()= & ? {
var img = new Image();
img.c( | M KrossOrigin = "";
img.onl8 J oad = function () {
draw(thiT 8 _ +s);
};
// 这是阿宝哥的头像哟
img.src = "https://avatars3.githubu] * Q O ~ ksercontent.com/u/4220799";
}
function draw(img) {
var canvas = document.getElE H [ementBR 0 O F SyId("canvas");
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
img.style.display = "none"E T - ];
var imageData = ctx.getImageDatp z # z g ma(0, 0, canvas.width, canvas.height);
var data = imageData.data;
var invert = function () {
for (var i = 0;* / , 0 b & ; i < data.9 + W Zleng^ ? D : 3 tth; i += 4y A ) 6 W A) {
data[i] = 255 - data[i]; // red
data[i + 1] = 255 - data[J P h r : Ki + 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; //u 1 W _ d 1 S l F red
data[i2 K 8 M . ( + 1] = avg; // green
di @ 4 *ata[i + 2] = avg; // blue
}
ctx.putImageData(imageData, 0, 0);
};s = l
var invertbtn = document.getElementById("invertbtn");
invertbtn.addEvent~ H ; 9 : w R | sListener("click", invert);
var grayscalebtn = document.getElemenf * s c X d K DtById(u . ; k , W X"grayscalebtn");
grayscalebtn.add. + s g !EventListener("clJ W * Z B w x A Cick", grayscale);
}
</script>
</body>
</html>
需求留心的在I / v [ 2 v K P V调用 getImageData
方法获取图片像素数据时,你或许会遇到跨D X R [ Q域问题,比如:
Uncaught DOMException: Fai} 4 w z ! m mled to execute 'getImageData' on 'Canvas@ R u LRenderingContextQ T E U B ? )2D': The canvas has been tainted by cross-origin data.
关于这个问题,你可以阅读 「张鑫旭」 大神 “解决canvas图片getImageData,toDataq u ? V P s c B ;URL跨域问题” 这一篇文章。
3.6 怎样完结图片隐写
「隐写术是一门p # $关于信息躲藏的技巧与科学,所谓信息躲藏指的是不让除预期的接收者之外的任何人知晓信息的传递工作或许信息的内容。」 隐写术的英文叫做 Steganography,来历于特里特米乌斯的一本叙说密码学与隐 I }写术的著作 Steganographia,该书书名源于希腊语,意为 “隐秘书写”。
下图是阿宝哥选用在线的图片隐写东西,将 「“全栈修仙之路”」 这 6 个字躲藏到原始, I T U n的图片中,然后运用对应的解密东西,解密出躲藏信息的效果:
(在线图片隐写领会地址:https://c.p2hp.com/yinxietu/)
现在有多种方案可以完结图片隐写,以下是几种常见的方案:R f 6
-
附加式的图片隐写; -
依据文件结构的图片隐写; -
依据 LSB 原理的图片隐写; -
依据 DCT 域的 JPG 图片隐写; -
数字水印的隐写; -
图片容差的隐写。
篇幅有限,这儿我们就不继续翻开,别离介绍每种计[ A q划,感兴趣的小伙伴可以阅读 “隐S z 0写术之图片隐写(一)” 这篇文章。
四、参阅资源
-
Baike – 矢量图 -
Wiki – 可缩放矢量图形 -
隐写术之图片隐写(一) -
不能说的秘密——前端也能玩的图片隐写术 -
又拍图片管家亿级图像之搜图体系的两代演进及底层原理 -
image-manipulation-libraries-for-javascript
创建了一个 “全栈修仙之路沟通群” 的微信群,想加群的小伙伴,加我微信 “semlinker”,补白 2。阿里、京东、腾讯的大佬都在群里等你哟。
semlinker/awesomeB ! A-typescript 1.6K
本文运用 mdnice 排版