From b52d112d58c13b8c05d359a8ecc639b6b37857b6 Mon Sep 17 00:00:00 2001 From: Hanterdro Date: Wed, 14 Feb 2018 05:31:17 +0100 Subject: [PATCH] Add opacity option (#37) --- README.md | 16 ++++++++++++++++ src/index.js | 5 ++++- test/fixtures/face-opacity.png | Bin 0 -> 8189 bytes test/unit.js | 17 +++++++++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/face-opacity.png diff --git a/README.md b/README.md index a3f0a45..a60317e 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,22 @@ Using the same source images as above would output this: +### Opacity + +The opacity can also be tweaked on each image. + +```js +mergeImages([ + { src: 'body.png' }, + { src: 'eyes.png', opacity: 0.7 }, + { src: 'mouth.png', opacity: 0.3 } +]) + .then(b64 => ...); + // data:image/png;base64,iVBORw0KGgoAA... +``` + + + ### Dimensions By default the new image dimensions will be set to the width of the widest source image and the height of the tallest source image. You can manually specify your own dimensions in the options object: diff --git a/src/index.js b/src/index.js index b6cade7..1d1a724 100644 --- a/src/index.js +++ b/src/index.js @@ -44,7 +44,10 @@ const mergeImages = (sources = [], options = {}) => new Promise(resolve => { canvas.height = getSize('height'); // Draw images to canvas - images.forEach(image => ctx.drawImage(image.img, image.x || 0, image.y || 0)); + images.forEach(image => { + ctx.globalAlpha = image.opacity ? image.opacity : 1; + return ctx.drawImage(image.img, image.x || 0, image.y || 0); + }); if (options.Canvas && options.format === 'image/jpeg') { // Resolve data URI for node-canvas jpeg async diff --git a/test/fixtures/face-opacity.png b/test/fixtures/face-opacity.png new file mode 100644 index 0000000000000000000000000000000000000000..25a3ff6264b42438f8f8c93258b42bcb09ee65f9 GIT binary patch literal 8189 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 literal 0 HcmV?d00001 diff --git a/test/unit.js b/test/unit.js index 3de1977..abbfada 100644 --- a/test/unit.js +++ b/test/unit.js @@ -87,3 +87,20 @@ test('mergeImages uses custom jpeg quality', async t => { t.true(b64 === expectedB64); }); + +test('mergeImages uses opacity', async t => { + t.plan(1); + const images = await Promise.all([ + { src: 'body.png' }, + { src: 'eyes.png', opacity: 0.7 }, + { src: 'mouth.png', opacity: 0.3 } + ].map(image => fixtures.getImage(image.src).then(src => { + image.src = src; + return image; + }))); + const b64 = await mergeImages(images, { Canvas }); + + const expectedB64 = await fixtures.getDataURI('face-opacity.png'); + + t.true(b64 === expectedB64); +});