1 | ;
|
2 | /**
|
3 | * 图片压缩处理
|
4 | * - 用于压缩png、svg、jpeg等图片
|
5 | *
|
6 | * @class Compile
|
7 | * {
|
8 | * src:'', <string> 源文件路径
|
9 | * dist:'', <string> 输出路径
|
10 | * }
|
11 | */
|
12 | class Compile{
|
13 | constructor(option){
|
14 | const _ts = this;
|
15 |
|
16 | option = option || {};
|
17 |
|
18 | let m = _ts.m = {
|
19 | fs:require('fs-extra'),
|
20 | path:require('path'),
|
21 | pathInfo:require('../lib/getPathInfo'),
|
22 | copy:require('../lib/copy'),
|
23 | Svgo:require('svgo'),
|
24 | imagemin:require('imagemin'),
|
25 | imagemin_pngquant:require('imagemin-pngquant'),
|
26 | imagemin_jpegrecompress:require('imagemin-jpeg-recompress'),
|
27 | imagemin_gifsicle:require('imagemin-gifsicle')
|
28 | },
|
29 | config = _ts.config = {};
|
30 |
|
31 | //配置写入到_ts.config
|
32 | for(let i in option){
|
33 | config[i] = option[i];
|
34 | };
|
35 | let src = config.src,
|
36 | dist = config.dist;
|
37 |
|
38 | return _ts.taskList();
|
39 | }
|
40 |
|
41 | taskList(){
|
42 | const _ts = this,
|
43 | m = _ts.m,
|
44 | config = _ts.config;
|
45 |
|
46 | return new Promise((resolve,reject)=>{
|
47 |
|
48 | //读取文件内容
|
49 | let distDir = m.path.dirname(config.dist),
|
50 | extension = m.pathInfo(config.src).extension,
|
51 | isImg = (()=>{
|
52 | let type = ['png','jpg','jpeg','svg','gif'];
|
53 | return type.some(item => {
|
54 | return extension === '.'+item;
|
55 | });
|
56 | })();
|
57 |
|
58 | if(isImg){
|
59 | //svg格式处理
|
60 | if(extension === '.svg'){
|
61 | //读取svg内容
|
62 | let svgContent = m.fs.readFileSync(config.src);
|
63 |
|
64 | //压缩svg
|
65 | new m.Svgo({}).optimize(svgContent,result => {
|
66 | svgContent = result.data;
|
67 | });
|
68 |
|
69 | //写入新的压缩文件
|
70 | m.fs.ensureDir(distDir,err => {
|
71 | if(err){
|
72 | reject({
|
73 | status:'error',
|
74 | msg:`${distDir} 创建失败`,
|
75 | infor:err
|
76 | });
|
77 | };
|
78 | try{
|
79 | m.fs.writeFileSync(config.dist,svgContent);
|
80 | resolve({
|
81 | status:'success',
|
82 | msg:`写入 ${config.dist}`,
|
83 | data:svgContent
|
84 | });
|
85 | }catch(err){
|
86 | reject({
|
87 | status:'error',
|
88 | msg:`写入 ${config.dist}`,
|
89 | info:err
|
90 | });
|
91 | };
|
92 | });
|
93 | }
|
94 |
|
95 | //其它格式(jpg、jpeg、png)处理
|
96 | else{
|
97 | //得到原始文件大小,好作比较
|
98 | let originalSize = m.fs.readFileSync(config.src).length,
|
99 | useList = undefined,
|
100 | imgmini = (optimization,callback) => {
|
101 | //gif压缩选项,如果不是gif则不需要
|
102 | if(extension === '.gif'){
|
103 | useList = [
|
104 | m.imagemin_gifsicle({
|
105 | optimizationLevel:3,
|
106 | interlaced:false,
|
107 | colors:200
|
108 | })
|
109 | ];
|
110 | };
|
111 | m.imagemin([config.src],'',{
|
112 | plugins:[
|
113 | m.imagemin_jpegrecompress({
|
114 | quality:'high',
|
115 | accurate:true,
|
116 | method:optimization,
|
117 | min:40,
|
118 | max:90,
|
119 | loops:6
|
120 | }),
|
121 | m.imagemin_pngquant({
|
122 | nofs:false,
|
123 | speed:1,
|
124 | quality:'0-100'
|
125 | })
|
126 | ],
|
127 | use:useList
|
128 | }).then(result => {
|
129 | if(typeof callback === 'function'){
|
130 | callback(result);
|
131 | };
|
132 | });
|
133 | };
|
134 |
|
135 | //尝试使用ssim方式先压缩,如果压缩结果没有变小则进行深层压缩
|
136 | imgmini('ssim',result => {
|
137 | let img = result[0].data,
|
138 | imgSize = img.length;
|
139 |
|
140 | //新图片如果小于原始结果则保存结果
|
141 | if(imgSize < originalSize){
|
142 | //写入新的压缩文件
|
143 | m.fs.ensureDir(distDir,err => {
|
144 | if(err){
|
145 | reject({
|
146 | status:'error',
|
147 | msg:`${distDir} 创建失败`,
|
148 | infor:err
|
149 | });
|
150 | };
|
151 | try{
|
152 | m.fs.writeFileSync(config.dist,img);
|
153 | resolve({
|
154 | status:'success',
|
155 | msg:`写入 ${config.dist}`,
|
156 | data:img
|
157 | });
|
158 | }catch(err){
|
159 | reject({
|
160 | status:'error',
|
161 | msg:`写入 ${config.dist}`,
|
162 | info:err
|
163 | });
|
164 | };
|
165 | });
|
166 | }else{
|
167 | imgmini('ms-ssim',result => {
|
168 | let img = result[0].data,
|
169 | imgSize = img.length;
|
170 |
|
171 | //写入新的压缩文件
|
172 | m.fs.ensureDir(distDir,err => {
|
173 | if(err){
|
174 | reject({
|
175 | status:'error',
|
176 | msg:`${distDir} 创建失败`,
|
177 | infor:err
|
178 | });
|
179 | };
|
180 |
|
181 | //进行深层压缩,如果依然不能优化则Copy
|
182 | if(imgSize < originalSize){
|
183 | try{
|
184 | m.fs.writeFileSync(config.dist,img);
|
185 | resolve({
|
186 | status:'success',
|
187 | msg:`写入 ${config.dist}`,
|
188 | data:img
|
189 | });
|
190 | }catch(err){
|
191 | reject({
|
192 | status:'error',
|
193 | msg:`写入 ${config.dist}`,
|
194 | info:err
|
195 | });
|
196 | };
|
197 | }else{
|
198 | new m.copy({
|
199 | src:config.src,
|
200 | dist:config.dist
|
201 | }).then(v => {
|
202 | resolve(v);
|
203 | }).catch(e => {
|
204 | reject(e);
|
205 | });
|
206 | };
|
207 | });
|
208 | });
|
209 | };
|
210 |
|
211 | })
|
212 |
|
213 |
|
214 | };
|
215 | }else{
|
216 | reject({
|
217 | status:'error',
|
218 | msg:`${config.src} 不是有效的 PNG|JPG|JPEG|SVG 文件`
|
219 | });
|
220 | };
|
221 | });
|
222 | }
|
223 | }
|
224 | module.exports = Compile;
|