Skip to content

Commit f9b1678

Browse files
add screenshot optimization w/ imageming. 20.9MB => 4.6MB => 1.72MB
1 parent bbd62a1 commit f9b1678

File tree

4 files changed

+41
-31
lines changed

4 files changed

+41
-31
lines changed

build.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const componentsBuildScreenshots = require('./src/components-build-screenshots')
2121

2222
// See src/components-build-defaults for list of options that can be overriden
2323
const options = {
24-
// componentsGlobPattern: 'src/components/text/*.html',
24+
// componentsGlobPattern: 'src/components/buttons/*.html',
2525
};
2626

2727
// Note that componentsBuildIndex() generates the index *and* the JSON

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
"co": "^4.6.0",
3838
"express": "^4.14.0",
3939
"filesize": "^3.3.0",
40+
"imagemin": "^5.2.2",
41+
"imagemin-mozjpeg": "^6.0.0",
4042
"jimp": "^0.2.27",
4143
"nightmare": "^2.8.1",
4244
"pretty-hrtime": "^1.0.3",

src/components-build-defaults.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ module.exports = {
66
componentsBuildPages: true, // false to skip building pages
77
componentsBuildScreenshots: true, // false to skip building screenshots
88
// Screenshots
9-
screenshotName: 'screenshot.jpg', // name screenshot file in each component dir
9+
screenshotName: 'screenshot.jpg', // name JPEG screenshot in each component dir
1010
screenshotAspectRatio: '4x3', // Tachyon aspect ratio of screenshot in index
1111
screenshotViewportWidth: 1024, // viewport width used for capture
1212
screenshotViewportHeight: 768, // viewport height used for capture
13-
screenshotTargetMinWidth: 360, // min width of target, resized screenshot
14-
screenshotTargetMinHeight: 128, // min height of target, resized screenshot
13+
screenshotTargetMinWidth: 400, // min width of target, resized screenshot
14+
screenshotTargetMinHeight: 160, // min height of target, resized screenshot
15+
mozjpegQuality: 90, // mozjpeg optimizer quality (default 75)
1516
screenshotSelector: '[data-name="component-container"]', // DOM element to capture
16-
screenshotCompressionQuality: 98, // JPEG compression quality before optimization
1717
// Misc
1818
tachyonsCssPath: 'src/css/tachyons.css',
1919
serverPort: 3333,

src/components-build-screenshots.js

+34-26
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ const co = require('co');
44
const express = require('express');
55
const filesize = require('filesize');
66
const fs = require('fs');
7+
const imagemin = require('imagemin');
8+
const imageminMozjpeg = require('imagemin-mozjpeg');
79
const Jimp = require('jimp');
810
const Nightmare = require('nightmare');
911
const path = require('path');
@@ -102,7 +104,9 @@ module.exports = _options => new Promise((resolve, reject) => {
102104
console.log(chalk.red(' * FAILED to create screenshot:'), component.screenshot.name);
103105
continue; // eslint-disable-line
104106
}
105-
const tmpObj = tmp.fileSync({ dir: path.dirname(component.screenshot.path) });
107+
108+
const screenshotDir = path.dirname(component.screenshot.path);
109+
const tmpPngObj = tmp.fileSync({ dir: screenshotDir });
106110
componentRect.height = Math.min(componentRect.height, options.screenshotViewportHeight);
107111
yield nightmare
108112
// we can not use .screenshot() with componentRect, so constrain the viewport instead
@@ -111,13 +115,12 @@ module.exports = _options => new Promise((resolve, reject) => {
111115
componentRect.height || options.screenshotViewportHeight
112116
).scrollTo(componentRect.y, componentRect.x)
113117
// .wait(1000)
114-
// do *not* use componentRect in .screenshot() below or risk distortions
115-
.screenshot(tmpObj.name);
116-
const tmpFileSize = fs.statSync(tmpObj.name).size;
117-
tmpTotalFileSize += tmpFileSize;
118+
.screenshot(tmpPngObj.name); // do *not* use componentRect here or risk distortions
118119

119-
// Resize and convert
120-
const screenshot = yield Jimp.read(tmpObj.name);
120+
// Resize and convert to JPEG, and optimize
121+
const tmpJpegDirObj = tmp.dirSync({ dir: screenshotDir, unsafeCleanup: true });
122+
const tmpJpegPath = path.join(tmpJpegDirObj.name, path.basename(component.screenshot.path));
123+
const screenshot = yield Jimp.read(tmpPngObj.name);
121124
yield new Promise((write_resolve, write_reject) => {
122125
if (component.frontMatter.screenshot === undefined ||
123126
component.frontMatter.screenshot.autocrop !== false) {
@@ -131,18 +134,36 @@ module.exports = _options => new Promise((resolve, reject) => {
131134
const scale = Math.max(scaleHeight, scaleWidth);
132135
screenshot
133136
.scale(scale > 0 ? scale : 1.0)
134-
.quality(options.screenshotCompressionQuality)
135-
.write(component.screenshot.path, (err) => {
136-
if (err) {
137-
write_reject(err);
137+
// Do not use .quality() here, default max quality helps optimizers below
138+
.write(tmpJpegPath, (jimp_err) => {
139+
if (jimp_err) {
140+
write_reject(jimp_err);
138141
}
139-
write_resolve();
142+
// Optimize
143+
imagemin([tmpJpegPath], screenshotDir, {
144+
plugins: [
145+
imageminMozjpeg({ quality: options.mozjpegQuality }),
146+
// imageminJpegRecompress(), // this guy is useless
147+
// imageminJpegtran(), // this guy is useless
148+
],
149+
}).then(() => {
150+
write_resolve();
151+
}).catch((imagemin_err) => {
152+
write_reject(imagemin_err);
153+
});
140154
});
141155
});
142156

143-
tmpObj.removeCallback();
157+
const tmpFileSize = fs.statSync(tmpPngObj.name).size;
158+
tmpTotalFileSize += tmpFileSize;
144159
const screenshotFileSize = fs.statSync(component.screenshot.path).size;
145160
screenshotTotalFileSize += screenshotFileSize;
161+
162+
// Cleanup
163+
tmpPngObj.removeCallback();
164+
fs.unlinkSync(tmpJpegPath);
165+
tmpJpegDirObj.removeCallback();
166+
146167
console.log(
147168
' * Created screenshot:',
148169
component.screenshot.path,
@@ -163,17 +184,4 @@ module.exports = _options => new Promise((resolve, reject) => {
163184
});
164185

165186
resolve(renderPromise);
166-
167-
// TODO: optimize all PNG files, eventually resize to smaller width
168-
// const imagemin = require('imagemin');
169-
// const imageminPngquant = require('imagemin-pngquant');
170-
// imagemin(['components/*.{jpg,png}'], ??, {
171-
// plugins: [
172-
// imageminPngquant({quality: '65-80'})
173-
// ]
174-
// }).then(files => {
175-
// console.log(files);
176-
// //=> [{data: <Buffer 89 50 4e …>, path: 'build/images/foo.jpg'}, …]
177-
// });
178-
//
179187
}); // return promise

0 commit comments

Comments
 (0)