Perniferous
Perniferous

Reputation: 611

Merging Images Using Javascript/React

I am creating a website in which each user has an "avatar". An avatar has different accessories like hats, facial expressions, etc. I have made this previously on a php website but I am using react to create this new website. I am loading in each users avatar and its item links from firestore. I do not want to use absolute positioning or css, I want the avatar to be one image.

Example of what I am trying to achieve: enter image description here

I found this library: https://github.com/lukechilds/merge-images which seems to be exactly what I need but I cannot load in external images or I get this error:

error img

Any solutions to this error or suggestions to an alternative would be greatly appreciated.

My code:

render() {

mergeImages([
  'http://example.com/images/Avatar.png',
  'http://example.com/images/Hat.png',
])
.then((b64) => {
  document.querySelector('img.abc').src = b64;
})
.catch(error => console.log(error))
return (
  ...
      <img class="abc" src='' width={100} height={200} alt="avatar"/>
  ...
); }

Upvotes: 5

Views: 9704

Answers (2)

Matt Carlotta
Matt Carlotta

Reputation: 19762

The merge-images package has some quirks. One of those quirks is that it expects individual images to either be served from your local server (example: http://localhost:3000/images/head.png, http://localhost:3000/images/eyes.png, and http://localhost:3000/images/mouth.png) or that those individual images be imported into a single file.

Working example: https://github.com/mattcarlotta/merge-images-example (this example includes the first three options explained below with the fourth option utilizing the end result of using a third party CDN)

To run the example, clone the repo:

git clone https://github.com/mattcarlotta/merge-images-example

Change directory:

cd merge-images-example

Then install dependencies:

yarn install

Then run the development server:

yarn dev

Option 1: The simplest implementation would be to import them into a AvatarFromFiles component. However, as written, it isn't reusable and isn't suitable for dynamically selected avatars.

Option 2: You may want to serve them from the local server like the AvatarFromLocalServer component with a Webpack Dev Config. Then you would retrieve stored strings from an API and pass them down into from state into the component. Once again, this still requires the images to be present in the images folder, but more importantly, it isn't ideal for a production environment because the images folder must be placed outside of the src folder to be served. This could also lead to security issues. Therefore, I don't recommend this option at all.

Option 3: Same as Option 1, but lazy loaded like the AvatarFromLazyFiles component and therefore flexible. You can load images by their name; however, it still requires that all of the images be present upon runtime and during production compilation. In other words, what you have on hand is what you get.

Option 4: So... the ideal option would be to build an image microservice or use a CDN that handles all things images (uploading, manipulating/merging, and serving images). The client would only be able to select/upload new images to this microservice/CDN, while the microservice/CDN handles everything else. This may require a bit more work but offers the most flexibility, super easy to implement, and best performance -- as it offloads all the work from the client to the dedicated service.

In conclusion, unless you plan on having a set amount of images, use option 3, otherwise option 4.

enter image description here

Upvotes: 4

Kevin Chavez
Kevin Chavez

Reputation: 1031

Problem

This is a CORS issue. The images are coming from a different origin that's not your server.

If you look at the source of the library, you'll notice it's using a <canvas> under the hood to merge the images, and then getting the resulting data. Canvas cannot work with images loaded from another domain. There's good reasoning behind this. In essence, loading an image into a canvas is a way to fetch data, and since you can retrieve the data from the canvas as base64, a malicious one could steal information by first loading it into a <canvas> and then pulling it out.

You can read about it directly from the spec for the <canvas> element.

Solution

You need to serve the images either from the same origin (essentially, the same domain) or include Access-Control-Allow-Origin: ... on the HTTP headers that serve the images. There's ways to do this in firebase storage, or other server solutions you might use.

Upvotes: 1

Related Questions