diff --git a/README.md b/README.md
index 35aca76..aad242b 100644
--- a/README.md
+++ b/README.md
@@ -22,10 +22,10 @@ const responsiveImage = require('responsive?sizes[]=100,sizes[]=200,sizes[]=300!
// responsiveImage.images => [{height: 50, path: '2fefae46cb857bc750fa5e5eed4a0cde-100.jpg', width: 100}, {height: 100, path: '2fefae46cb857bc750fa5e5eed4a0cde-200.jpg', width: 200}, {height: 150, path: '2fefae46cb857bc750fa5e5eed4a0cde-300.jpg', width: 300}]
// responsiveImage.src => '2fefae46cb857bc750fa5e5eed4a0cde-100.jpg'
// responsiveImage.toString() => '2fefae46cb857bc750fa5e5eed4a0cde-100.jpg'
-React.render(, el);
+ReactDOM.render(, el);
// Or you can just use it as props, `srcSet` and `src` will be set properly
-React.render(, el);
+ReactDOM.render(, el);
```
Or use it in CSS (only the first resized image will be used, if you use multiple `sizes`):
@@ -38,13 +38,32 @@ Or use it in CSS (only the first resized image will be used, if you use multiple
}
```
+```js
+// Outputs placeholder image as a data URI, and three images with 100, 200, and 300px widths
+const responsiveImage = require('responsive?placeholder=true&sizes[]=100,sizes[]=200,sizes[]=300!myImage.jpg');
+
+// responsiveImage.placeholder => 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAIBAQE…'
+ReactDOM.render(
+
+
+
, el);
+```
+
+
### Options
-- `sizes: array`: specify all widths you want to use; if a specified size exceeds the original image's width, the latter will be used (i.e. images won't be scaled up). You may also declare a default `sizes` array in `responsiveLoader` in your `webpack.config.js`.
-- `size: integer`: specify one width you want to use; if the specified size exceeds the original image's width, the latter will be used (i.e. images won't be scaled up)
-- `quality: integer`: JPEG compression quality; defaults to `95`
-- `ext: string`: either `png`, `jpg`, or `gif`; use to convert to another format; defaults to original file's extension
-- `background: hex`: Background fill when converting transparent to opaque images; defaults to `0xFFFFFFFF` (note: make sure this is a valid hex number)
+- `sizes: array` — specify all widths you want to use; if a specified size exceeds the original image's width, the latter will be used (i.e. images won't be scaled up). You may also declare a default `sizes` array in `responsiveLoader` in your `webpack.config.js`.
+- `size: integer` — specify one width you want to use; if the specified size exceeds the original image's width, the latter will be used (i.e. images won't be scaled up)
+- `quality: integer` — JPEG compression quality; defaults to `95`
+- `ext: string` — either `png`, `jpg`, or `gif`; use to convert to another format; defaults to original file's extension
+- `background: hex` — Background fill when converting transparent to opaque images; defaults to `0xFFFFFFFF` (note: make sure this is a valid hex number)
+- `placeholder: bool` — A true or false value to specify wether to output a placeholder image as a data URI. (Defaults to `false`)
+- `placeholderSize: integer` — A number value specifying the width of the placeholder image, if enabled with the option above. (Defaults to `40`)
### Examples
@@ -62,7 +81,9 @@ module.exports = {
]}
},
responsiveLoader: {
- sizes: [300, 600, 1200, 2000]
+ sizes: [300, 600, 1200, 2000],
+ placeholder: true,
+ placeholderSize: 50
}
}
```
diff --git a/index.js b/index.js
index 101dd98..9632939 100644
--- a/index.js
+++ b/index.js
@@ -17,6 +17,8 @@ module.exports = function loader(content) {
const sizes = query.sizes || query.size || options.sizes || [Number.MAX_SAFE_INTEGER];
const name = query.name || options.name || '[hash]-[width].';
const outputContext = query.context || options.context || '';
+ const outputPlaceholder = query.placeholder || query.placeholder !== false && options.placeholder || false;
+ const placeholderSize = query.placeholderSize || options.placeholderSize || 40;
// JPEG compression
const quality = parseInt(query.quality, 10) || options.quality || 95;
// Useful when converting from PNG to JPG
@@ -87,14 +89,46 @@ module.exports = function loader(content) {
}
});
+ if (outputPlaceholder) {
+ q.defer(function generatePlaceholder(queueCallback) {
+ img
+ .clone()
+ .resize(placeholderSize, jimp.AUTO)
+ .quality(quality)
+ .background(background)
+ .getBuffer(mime, function resizeCallback(queueErr, buf) {
+ if (err) {
+ return queueCallback(queueErr);
+ }
+
+ const placeholder = buf.toString('base64');
+ return queueCallback(null, JSON.stringify('data:' + (mime ? mime + ';' : '') + 'base64,' + placeholder));
+ });
+ });
+ }
+
return q.awaitAll((queueErr, files) => {
+ 'use strict'; // eslint-disable-line
+ let placeholder;
+ if (outputPlaceholder) {
+ placeholder = files.pop();
+ }
+
const srcset = files.map(f => f.src).join('+","+');
const images = files.map(f => '{path:' + f.path + ',width:' + f.width + ',height:' + f.height + '}').join(',');
- const firstImagePath = files[0].path;
-
- loaderCallback(null, 'module.exports = {srcSet:' + srcset + ',images:[' + images + '],src:' + firstImagePath + ',toString:function(){return ' + firstImagePath + '}};');
+ const firstImage = files[0];
+
+ loaderCallback(null, 'module.exports = {' +
+ 'srcSet:' + srcset + ',' +
+ 'images:[' + images + '],' +
+ 'src:' + firstImage.path + ',' +
+ 'toString:function(){return ' + firstImage.path + '},' +
+ 'placeholder: ' + placeholder + ',' +
+ 'width:' + firstImage.width + ',' +
+ 'height:' + firstImage.height +
+ '};');
});
});
};
diff --git a/test/index.js b/test/index.js
index 45b20a1..321d9b1 100644
--- a/test/index.js
+++ b/test/index.js
@@ -46,3 +46,16 @@ test('output should be relative to context', t => {
t.equal(multi.toString(), 'foobar/test/9a7bdeb1304946f1fcd4a691409f286a-500.jpg');
t.end();
});
+
+test('with placeholder image', t => {
+ const output = require('../index?placeholder=true!./cat-1000.jpg');
+ t.equal(output.placeholder, 'data:image/jpeg;base64,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');
+ t.end();
+});
+
+test('output first resized image height & width', t => {
+ const output = require('../index?size=500!./cat-1000.jpg');
+ t.equal(output.height, 450);
+ t.equal(output.width, 500);
+ t.end();
+});