maulik13
maulik13

Reputation: 3760

Merge two dataURIs to create a single image

I want to generate images consisting of a label and an icon. The label part is going to vary a lot (50-100) while there are about 10 icons. I would like to make the final images in a modular way by splitting the final image in two parts, a label image and an icon image. I will build a service that returns dataURI for the labels while the icon dataURIs will be embedded in the page. Then I would like to combine these two different dataURIs to create a single dataURI representing a combined image.

How can I do this on the client side?

Upvotes: 7

Views: 10114

Answers (2)

TennisVisuals
TennisVisuals

Reputation: 427

Example using an array of image objects containing URI src and x, y offsets:

var images = [
   { src: 'data:image/gif;base64,R0lGODdhAgACALMAAAAAAP///wAAAAAAAP8AAAAAAAAAAAAAAAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAgACAAAEAxBJFAA7', x: 0, y: 0 },
   { src: 'data:image/gif;base64,R0lGODdhAgACALMAAAAAAP///wAAAAAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAgACAAAEA5BIEgA7', x: 20, y: 20 },
];

var canvas = document.createElement('canvas');
var destination = document.getElementById('canvas');

Promise.all(images.map(imageObj => add2canvas(canvas, imageObj)))
    .then(() => destination.append(canvas));

function add2canvas(canvas, imageObj) {
   return new Promise( (resolve, reject) => {
      if (!imageObj || typeof imageObj != 'object') return reject();
      var image = new Image();
      image.onload = function () {
          canvas.getContext('2d')
                .drawImage(this, imageObj.x || 0, imageObj.y || 0);
          resolve();
      };

      image.src = imageObj.src;
   });
}

Some more bells/whistles... function to generate a composite png:

  function mergeImageURIs(images) {
      return new Promise( (resolve, reject) => {
          var canvas = document.createElement('canvas');
          canvas.width = 1000; // desired width of merged images
          canvas.height = 1000; // desired height of merged images
          Promise.all(images.map(imageObj => add2canvas(canvas, imageObj))).then(() => resolve(canvas.toDataURL('image/png'), reject));
      });
  }

  function add2canvas(canvas, imageObj) {
    return new Promise( (resolve, reject) => {
       if (!imageObj || typeof imageObj != 'object') return reject();
       var x = imageObj.x && canvas.width ? (imageObj.x >=0 ? imageObj.x : canvas.width + imageObj.x) : 0;
       var y = imageObj.y && canvas.height ? (imageObj.y >=0 ? imageObj.y : canvas.height + imageObj.y) : 0;
       var image = new Image();
       image.onload = function () {
           canvas.getContext('2d').drawImage(this, x, y);
           resolve();
       };

       image.src = imageObj.src;
    });
  }

Upvotes: 1

mrmcgreg
mrmcgreg

Reputation: 2816

You can create images using your data uris and then draw a new image that includes them using canvas. Here's a simple example:

var nloaded = 0;
function checkload(event) {
  nloaded++;
  if (nloaded < 2) {
    return;
  }
  
  var canvas = document.querySelector('canvas');
  var context = canvas.getContext('2d');
  context.drawImage(image1, 0, 0, 50, 50);
  context.drawImage(image2, 50, 50, 100, 100);

  var combined = new Image;
  combined.src = canvas.toDataURL('data/gif');
  
  document.body.appendChild(combined);
}

var image1 = new Image;
image1.onload = checkload;
image1.src = 'data:image/gif;base64,R0lGODdhAgACALMAAAAAAP///wAAAAAAAP8AAAAAAAAAAAAAAAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAgACAAAEAxBJFAA7';


var image2 = new Image;
image2.onload = checkload;
image2.src = 'data:image/gif;base64,R0lGODdhAgACALMAAAAAAP///wAAAAAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAgACAAAEA5BIEgA7';
canvas {
    display: none;
}
<canvas width=100 height=100></canvas>

.

Once you have the images loaded from the data URI and combined using the drawImage commands of the canvas context you can use the canvas to create a new image like:

var combined = new Image;
combined.src = canvas.toDataURL('data/gif');

Unfortunately this won't work in IE8.

Upvotes: 10

Related Questions