How to build a Preview image by stacking transparent images based on Form selections using JavaScript?

Using HTML, CSS, and JavaScript I need to build a Form with Selection input fields and Radio selection fields. When each one of these fields is selected, I need to build a Preview image based on the selection they made.

Each field will have an Image associated with it and then it will also have that same image replicated for each color.

So if there was 20 Form fields that a user could pick it would have 20 images x 10 different color options and would result in there being 200 possible images to build there preview image!

Here is an image that shows an example of what I am trying to accomplish…the Vans website uses this technique for a custom shoe generator with preview…
http://i.stack.imgur.com/O61hQ.png

When you pick a section and then the color for that section, it loads a transparent image and stacks them together.

For testing, I have made a set of transparent images for a demo form with only 4 Fields and 3 color choices so 12 images in total. (All shown below)

When all the fields have been selected it should build an image preview to the user that is similar to this…
http://i.imgur.com/JYJdBnN.png

or the Alternative has a Top/Roof more like an octagon type shape

enter image description here


bottom_blue
http://i.imgur.com/fsG49zS.png


bottom_green
http://i.imgur.com/TdjBCXk.png


bottom_red
http://i.imgur.com/UxCyzij.png


entrance_blue
http://i.imgur.com/yLPtqYF.png


entrance_green
http://i.imgur.com/YoxBjxL.png


entrance_red
http://i.imgur.com/LQ07PgU.png


top_blue
http://i.imgur.com/2ZmjBYe.png


top_green
http://i.imgur.com/kImYjxF.png


top_red
http://i.imgur.com/wgtFL6h.png


top_red_octo
enter image description here


top_blue_octo
enter image description here


top_green_octo
enter image description here


Here is a basic form that could use the images…

http://jsfiddle.net/Pha4V/

<form id="form-ChannelType">
  <p>
        Select a bottom (just 1 in the demo)<br>
        <select name="bottom" class="form-control" id="bottom" size="1">
          <option value="bottom1">Bottom 1</option>
        </select>
  </p>
  <p>
        Select a Front (just 1 in the demo)<br>
        <select name="front" class="form-control" id="front-1" size="1">
          <option value="front1">Front 1</option>
        </select>
  </p> 

  <p>
        Select a Top<br>
        <select name="bottom" class="form-control" id="bottom" size="2">
          <option value="bottom_triangle">Triangle Shaped Top</option>
          <option value="bottom_octo">Octo. Shaped Top</option>
        </select>
  </p>    

  <p>
        Select a Color<br>
        <select name="color" class="form-control" id="color" size="3">
          <option value="blue">Blue</option>
          <option value="green">Green</option>
          <option value="red">Red</option>
        </select>
  </p>
</form>

Can anyone help me in the logic of how to make such a form work? I need to make it work in a way that can easily scale to hundreds of options and images.

With the basic form above, it would need to build a preview image by stacking appropriate images based on the form selections. I don’t expect anyone to do all the work for this unless they are simply up for the challenge but I would appreciate any help or ideas on how to best make this work?


NOTE:
My final project that will be based off of this will be much different. It will be used for a custom sign generator. I cannot use ImageMagick or GD libraries to do on the fly images, so each image has to be built and each layer changed. Instead of abuilding a simple house structure I will have options such as…

  • Sign Type which will determine a lot of the setting that are shown or hidden after it based on the type of sign selected.
  • Font the base image will have one for each font and in each font will be in each color as well. So 10 fonts x 15 colors = could easily be over 150 possible images to show just as the BASE image
  • Colors There will be from 2-4 different color options that will change the color for different parts of the sign. So all the image combinations listed above x the different color areas = lots of images

So you can see this will be a pretty big beast when it’s all done and I could use any help in getting a good start on it. I don’t think a HUGE if/else or switch statement is the best way to go with something like this that can have hundreds of image combinations but I could be wrong?

Read More:   How to import a js library without definition file in typescript file

I created an Angular app that does this, in a more flexible way.

FIDDLE: http://jsfiddle.net/Pha4V/3/

You can easily add shapes, types and colors just by creating new elements in an array. Also, by having the structure defined this way, the code can be easily integrated with a database.

Example:

var tops =
[
    new Shape("Triangle", "http://i.stack.imgur.com/wkRaA.png", "Blue"),
    new Shape("Triangle", "http://i.stack.imgur.com/lHk5X.png", "Green"),
    new Shape("Triangle", "http://i.stack.imgur.com/LT6Nn.png", "Red"),
    new Shape("Octagonal", "http://i.stack.imgur.com/FjF7S.png", "Red"),
    new Shape("Octagonal", "http://i.stack.imgur.com/0FWGv.png", "Blue"),
    new Shape("Octagonal", "http://i.stack.imgur.com/92kyY.png", "Green")
];

For the future, fonts can be added by adding a fourth parameter and new colors can be added just by adding an element with that color.

New color example:

...
    new Shape("Octagonal", "http://...", "Cyan")
...

New shape type example:

...
    new Shape("Square", "http://...", "Blue")
...

The current form of the ‘house’:

It could be easily saved, as there are variables that define all the selections:

$scope.topColor, $scope.topType
$scope.bottomColor, $scope.bottomType
...

Different methods of input:

The select method can be changed easily, as the code does not depend on it’s type.

Future fonts:

You can easily add new shapes as fonts, and copy the logic from ‘tops’ and you won’t have to generate that many images. You’ll have just to generate images with different fonts:

var fontBottom =
[
    new Shape("Font", "http://...", "Arial"),
    new Shape("Font", "http://...", "Arial Black")
];

//Then add the methods and the input.

Just Because One Can – A Pure CSS3 Solution

Okay, for your purposes, a pure css solution will probably not be ideal (and even your question title stated javascript). However, just to show that it can be done purely with CSS in browsers recognizing CSS3, here is the proof. Much credit should be given as the fiddle, html, and css all has some initial sourcing from Stefan Moraru’s demo fiddle that did use code to work. Tested in Firefox 25, Chrome 31, IE10.

Fiddle Demo

HTML

<form>
    <h4>Choose the top type:</h4>

    <input type="radio" id="TopTriangle" name="TopShape" value="Triangle" checked/>Triangle 
    <input type="radio" id="TopOctogon" name="TopShape" value="Octogon"/>Octogon

    <h4>Choose the top color:</h4>

    <input type="radio" id="TopBlue" name="TopColor" value="Blue" checked/>Blue
    <input type="radio" id="TopGreen" name="TopColor" value="Green"/>Green
    <input type="radio" id="TopRed" name="TopColor" value="Red"/>Red

    <h4>Choose the bottom type:</h4>

    <input type="radio" id="BotRect" name="BotShape" value="Rectangle" checked />Rectangle

    <h4>Choose the bottom color:</h4>

    <input type="radio" id="BotBlue" name="BotColor" value="Blue"/>Blue
    <input type="radio" id="BotGreen" name="BotColor" value="Green" checked/>Green
    <input type="radio" id="BotRed" name="BotColor" value="Red"/>Red

    <h4>Choose the entrance color:</h4>

    <input type="radio" id="EntBlue" name="EntColor" value="Blue"/>Blue
    <input type="radio" id="EntGreen" name="EntColor" value="Green"/>Green
    <input type="radio" id="EntRed" name="EntColor" value="Red" checked/>Red

    <div class="house">
        <div class="top">
            <img class="blue triangle" src="http://i.stack.imgur.com/wkRaA.png" />
            <img class="green triangle" src="http://i.stack.imgur.com/lHk5X.png" />
            <img class="red triangle" src="http://i.stack.imgur.com/LT6Nn.png" />
            <img class="blue octogon" src="http://i.stack.imgur.com/0FWGv.png" />
            <img class="green octogon" src="http://i.stack.imgur.com/92kyY.png" />
            <img class="red octogon" src="http://i.stack.imgur.com/FjF7S.png" />
        </div>

        <div class="entrance">
            <img class="blue" src="http://i.stack.imgur.com/jehkB.png" />
            <img class="green" src="http://i.stack.imgur.com/CgYnh.png" />
            <img class="red" src="http://i.stack.imgur.com/lHbKP.png" />
        </div>

        <div class="bottom">
            <img class="blue rect" src="http://i.stack.imgur.com/7P8Ua.png" />
            <img class="green rect" src="http://i.stack.imgur.com/h2Bpc.png" />
            <img class="red rect" src="http://i.stack.imgur.com/jAX1y.png" />
        </div>
    </div>
</form>

CSS

.house {
    width: 100px;
    height: 80px;    
    position: relative;
}

.house .top {
    position: absolute;
    top: 0px;    
    z-index: 2;
}

.house .entrance {
    position: absolute;
    bottom: 0px;        
    z-index: 3;
}

.house .bottom {
    position: absolute;
    bottom: 0px;   
    z-index: 1;
}

img {
    width: 80px;
    height: 80px;
}

.house img {
    display: none; /* default hide all */
}

#TopTriangle:checked ~ #TopBlue:checked ~ .house > .top > .blue.triangle { display: block;}
#TopTriangle:checked ~ #TopGreen:checked ~ .house > .top > .green.triangle { display: block;}
#TopTriangle:checked ~ #TopRed:checked ~ .house > .top > .red.triangle { display: block;}
#TopOctogon:checked ~ #TopBlue:checked ~ .house > .top > .blue.octogon { display: block;}
#TopOctogon:checked ~ #TopGreen:checked ~ .house > .top > .green.octogon { display: block;}
#TopOctogon:checked ~ #TopRed:checked ~ .house > .top > .red.octogon { display: block;}

#BotRect:checked ~ #BotBlue:checked ~ .house > .bottom > .blue.rect { display: block;}
#BotRect:checked ~ #BotGreen:checked ~ .house > .bottom > .green.rect { display: block;}
#BotRect:checked ~ #BotRed:checked ~ .house > .bottom > .red.rect { display: block;}

#EntBlue:checked ~ .house > .entrance > .blue { display: block;}
#EntGreen:checked ~ .house > .entrance > .green { display: block;}
#EntRed:checked ~ .house > .entrance > .red { display: block;}

I have an idea for another approach which requires more programming but saves time when there are a lot of items which need to be maintained in this way.

Read More:   Does it make sense to use Require.js with Angular.js? [closed]

With this method you do not need to create a new image for every color and every customizable part.

Example: 20 items with each 4 customizable sections and 20 colors:

Working with images requires: 20 x 4 x 20 = 1600 images

Working with masks from this solution: 20 x 4 = 80 images


We are going to create a mask for each section. Open the image in Photoshop and cutout every customizable section of the image. So every part you want to be able to customize in another color. Make this part black and everything around transparent. Basically if you stack all these “layers” you have a shoe with solid black on every part which you want to customize.

Save these layers in separate images. You now can now load each layer on a canvas and replace the black with the color or pattern of your choice. Use these layers to compose the shoe.

Finally put a shading layer which gives the shoe a 3d effect like.

Schematically it would look like the image below. The processed masks become a colored layer. Combined with a base image it becomes a colored item. Your designer only need to cut out the patches and you are done.

By far not the easiest to code but doesn’t require dozens of precutted images when using large amounts of colors and patterns.

enter image description here

I am not able to put a lot of time in this right now but I do have an answer that may prove useful to you…

This can be done purely in jQuery.. If you are stacking transparent png images then you can wrap each image in it’s own div and position each div to an absolute position of 0 and 0 within a wrapping parent container..

You can assign a z-index dynamically to each container.. and use this z-index to manipulate which div is in front of or behind a previous div. each time you select an element on the selector panel you would set a corresponding z-index of corresponding element to the highest number and reduce the z-index of the previous top positioned element by 1..

This would pretty function as “bring to front” functionality..

You can try building a dynamic version of this with 3 or 4 images first.

If you go this route you will also have to look into preloading images…

Do your logic is something like this…

Preload images..
Wrap them in dynamically positioned divs..
Create dynamically named classes on div elements in order to be able to generalize your code
Dynamically assign z-index values to divs..
On click of relevant panel element bring relevant div with an image to front and push the previous top element one z-index down.

Read More:   Draw simple timeline with D3

Hope this helps with the logic if your path is jQuery

It seems to me that a shoe of any color is simply an object that has shape and texture with some color on it.

  • Shape is provided by canvases.
  • Texture is provided by gradient transparency that a png can give. You can generate one from a greyscale image of your shoes.
  • and color is provided by rgb values and alpha codes.

You “cut” (design and load) the shapes and texture to fit your shoe image. That’s one images per section.
You have X (4) sections
You define what colors the factory can make. That’s Y (500?) colors.

That’s 2 * X * Y…maybe 4000 combinations? And here we’ve only loaded 4 images total.

The hardest part is creating the canvas items, but it can be done to pixel accuracy and you have only a handful for each item. Probably some tools to do it.

Here’s a fiddle showing swapping colors. Of course you would have a selector to click on and have better shoe shapes than I have.

Very simple fiddle

var alpha = 0.4

    function createCanvas(id, color, alpha) {
        var canvas = document.getElementById(id);
        var context;
        context = canvas.getContext('2d');
        context.clearRect(0, 0, canvas.width, canvas.height); // clear canvas first
        context.globalAlpha = alpha || 0.4; // transparency could be dynamic
        context.beginPath();

        switch (id) { // add a case for each piece
            case 'left':
                context.moveTo(0, 0);
                context.lineTo(0, 200);
                context.lineTo(400, 200);
                context.arc(200, 100, 75, 50, 50);
                break;
            case 'right':
                context.moveTo(0, 0);
                context.lineTo(200, 200);
                context.arc(200, 100, 75, 50, 50);
                break;
        }
        context.closePath();
        context.lineWidth = 0;
        context.fillStyle = color || '#8ED6FF'; // dynamic color from provided list
        context.fill();

    };

createCanvas('left', alpha);
createCanvas('right', alpha);
// add a couple more peices here


var counter = 0;

// have some kind of color selector
$('body').on('click', 'canvas', function () {
    console.log(this, counter);
    var newShapeColor = $('div.colors:eq(' + counter + ')').css('background-color');
    createCanvas($(this).attr('id'), newShapeColor, alpha);
    counter = (counter < 3) ? counter + 1 : 0;
});

Considering your plan is to build a sign generator you might need only shapes and plain color so SVG images would be perfect for you. It’s already well supported.

Even if someday you’ll fancy to use some gradients it’s till something you can achieved with svg. Indeed you can draw pretty complex things in SVG (see http://raphaeljs.com/tiger.html).

You can generate theme with a using online generator like svg-edit or simply Adobe Illustrator and even by code then I recomand you raphael.js.

This way you can store easily light weight shapes definitions without any color consideration (or just default color if you’d like so). In my example I used place holders I replaced with selected colors.

http://codepen.io/svassr/pen/myepF

In the future you’ll also be able to insert text as well and use various number of colors for each shape.

To draw SVG in a canvas, and then be able to save as an image using canvas.toDataURL("image/png"), you’ll have to use canvg.

canvg(document.getElementById('canvas'), svg);

That said if your project would have been similar to Vans’s custom shoe generator I would have recommand @gillyspy’s solution wichs seems to me the best but perhaps by using CSS masking.


The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 .

Similar Posts