How to swap images on HTML without libraries (like z-index) - html

I have a question !
In a html5 canvas, I create an "x" number of images. The last image created is always above the other. Is it possible to swap the depths of the images without the need for libraries? (I do not intend to reverse the order of the variables)
I want somethin like this (without kinetic):
http://www.html5canvastutorials.com/kineticjs/html5-canvas-shape-layering-with-kineticjs/
so, here is the code I'm using:
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var imageObj = new Image();
imageObj.onload = function() {
context.drawImage(imageObj, 69, 50);
};
imageObj.src = "image1.png";
var imageObj2 = new Image();
imageObj2.onload = function() {
context.drawImage(imageObj2, 40, 30);
};
imageObj2.src = "image2.png";
/* I want to put "Image1.png" over "Image2.png" dynamically, for example, pressing a button, all this after render on the canvas */

Draw the furtherest images first. It might help to put the images in an array so you can just loop through them.
Otherwise, in the case of your example, this will fix it as you are drawing context.drawImage(imageObj, 69, 50); last. Think of it as if you would draw it on paper; what you draw last is above what you have already drawn.
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var imageObj2 = new Image();
imageObj2.onload = function() {
context.drawImage(imageObj2, 40, 30);
};
imageObj2.src = "image2.png";
var imageObj = new Image();
imageObj.onload = function() {
context.drawImage(imageObj, 69, 50);
};
imageObj.src = "image1.png";

The order you draw the images matters. You will need to draw the images in the back first, then draw the closer ones.

Why don't you create a canvas object for every image and either show / hide them or adjust the z-index?

Related

HTML canvas dataURL output is blank when using multiple images

I am trying to add multiple images to a HTML canvas and then get a file that I can save using toDataURL()
I have this so far
const canvas = document.querySelector("canvas");
const context = canvas.getContext("2d");
myImage = new Image();
myImage.src = "https://via.placeholder.com/150";
myImage2 = new Image();
myImage2.src = "https://via.placeholder.com/250";
myImage3 = new Image();
myImage3.src = "https://via.placeholder.com/350";
myImage.addEventListener("load", ()=>{
context.drawImage(myImage, 0, 0, 100, 100);
});
myImage2.addEventListener("load", ()=>{
context.drawImage(myImage2, 150, 0, 100, 100);
});
myImage3.addEventListener("load", ()=>{
context.drawImage(myImage3, 300, 0, 100, 100);
});
var final = canvas.toDataURL();
console.log(final);
<canvas width="800"></canvas>
The image is generated in the canvas correctly and I can see it looks ok, but the dataurl output is blank, where am I going wrong?
I know there are solutions when using one image like at HTML5 Canvas toDataURL returns blank but as I am using multiple images, I am unsure how to apply this solution in my case

HTML5 Canvas image resize on Chrome & easeljs

I'm struggling to make smooth image resized in canvas in Chrome. In firefox it works well, but in Chrome I'm stuck on making it smooth.
Here is the jsfiddle
http://jsfiddle.net/flashmandv/oxtrypmy/
var AVATAR_SIZE = 100;
var WHITE_BORDER_SIZE = 3;
var stage = new createjs.Stage("canvas");
var avCont = new createjs.Container();
stage.addChild(avCont);
avCont.x = avCont.y = 20;
//add white circle
var whiteBorderCircle = new createjs.Shape();
var radius = (AVATAR_SIZE+WHITE_BORDER_SIZE*2)/2;
whiteBorderCircle.graphics.beginFill("white").drawCircle(radius, radius, radius);
avCont.addChild(whiteBorderCircle);
//add avatar image mask
var avatarMask = new createjs.Shape();
avatarMask.graphics.beginFill("red").drawCircle(AVATAR_SIZE/2+WHITE_BORDER_SIZE, AVATAR_SIZE/2+WHITE_BORDER_SIZE, AVATAR_SIZE/2);
//add avatar image
var image = new Image();
image.onload = function(){
var bitmap = new createjs.Bitmap(image);
bitmap.mask = avatarMask;
var bounds = bitmap.getBounds();
bitmap.scaleX = (AVATAR_SIZE+WHITE_BORDER_SIZE*2) / bounds.width;
bitmap.scaleY = (AVATAR_SIZE+WHITE_BORDER_SIZE*2) / bounds.height;
avCont.addChild(bitmap);
stage.update();
};
image.src = 'http://files.sharenator.com/sunflowers-s800x800-423444.jpg';
Notice the jagged image
Please help
It is due to how clipping works in Chrome. Clip masks are pretty brutal in Chrome while in Firefox you get anti-aliasing along the non-straight edges.
Here is a proof-of-concept for this (run this in Chrome and in FF to see the difference):
http://jsfiddle.net/r65fcqoy/
The only way to get around this is to use composite modes instead, which basically means you need to rewrite your code unless the library you're using support this in some way.
One use of a composite mode is to use it to fill anything inside an existing drawing on the canvas.
We'll first create the filled circle we want the image to appear inside
Change comp mode to source-in and draw image
Then we go back to normal comp mode and draw the outer border
Here is an approach using vanilla JavaScript where you can control how you plug things together - this is maybe not what you're after but there is really not much option if the library as said doesn't support comp mode instead of clipping:
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
img = new Image,
x = 70, y =70;
var AVATAR_SIZE = 100;
var WHITE_BORDER_SIZE = 3;
var radius = (AVATAR_SIZE+WHITE_BORDER_SIZE*2)/2;
img.onload = function() {
// first draw the circle for the inner image:
ctx.arc(x, y, AVATAR_SIZE*0.5, 0 , 2*Math.PI);
ctx.fill();
// now, change composite mode:
ctx.globalCompositeOperation = 'source-in';
// draw image in top
ctx.drawImage(img, x-AVATAR_SIZE*0.5, y-AVATAR_SIZE*0.5,
AVATAR_SIZE, AVATAR_SIZE);
// change back composite mode to default:
ctx.globalCompositeOperation = 'source-over';
// now draw border
ctx.beginPath();
ctx.arc(x, y, radius + 5, 0, 2*Math.PI);
ctx.closePath();
ctx.lineWidth = 10;
ctx.strokeStyle = '#ffa94e';
ctx.stroke();
};
img.src = 'http://i.stack.imgur.com/PB8lN.jpg';
<canvas id=canvas width=500 height=180></canvas>
Another solution to this would be in onload function to add another shape above the masked image to simply cover the jagged edges of the clipping mask

Canvas, Rect: Images for patterns are displayed wrong

I'm developing a little map-editor just for fun to get to know HTML5 Canvas and stuff a little bit better.
What I want to do
I'm trying to load 3 items:
2 rocks
1 goblin
I wrote a function "drawItem(item)", which should draw me an item to the canvas:
drawItem = function(item) {
var imageObj = new Image();
imageObj.onload = function() {
var pattern = context.createPattern(imageObj, 'repeat');
context.rect(gridSize*item.position[0], gridSize*item.position[1], gridSize, gridSize);
context.fillStyle = pattern;
context.fill();
};
imageObj.src = item.img;
};
What an item-object looks like:
itemOne = {
img : 'https://lh3.googleusercontent.com/ZX4Zl7JT1gkgOVA9FbMFnMAw7TC9bBCVMSGWKFTmOW88vDTgcCOb7tBBo60nxoSdHQ=s190',
position : [0, 0] //these get multiplied with "gridSize" in the drawItem-function
};
Now here's the problem:
If I invoke this function with an item-object, the object gets drawn correctly.
If I invoke this function 3 times with 3 different item-objects (see JS-Fiddle), the 2 rock-items seem to have a goblin on top of it. That's wrong.
JS-Fiddle
http://jsfiddle.net/rSVkb/1/
"Question"
Does anyone know this issue? I've been googling for hours now, but since I'm not really sure what to search for, it's kind of hard to find out.
Many thanks!
Boris
You could simplify the whole process by using drawImage
drawItem = function(item) {
var imageObj = new Image();
imageObj.onload = function() {
context.drawImage(imageObj, gridSize*item.position[0], gridSize*item.position[1])
};
imageObj.src = item.img;
};
http://jsfiddle.net/rSVkb/2/
You should probably be using drawImage as the other answerer said, but for the sake of completeness, let me tell you why your original code was wrong.
In this code:
var pattern = context.createPattern(imageObj, 'repeat');
context.rect(gridSize*item.position[0], gridSize*item.position[1], gridSize, gridSize);
context.fillStyle = pattern;
context.fill();
You are adding a rect to the current path, and then filling the current path.
when you call rect then fill, and then call rect with a different rect and then fill again, the second fill command is filling both of the rects you had defined.
This is because rect always adds an additional rect to the current path.
So one way to fix your code would be to add one line, a call to beginPath(), which would reset the path so that you do not keep adding rects each time you draw:
var pattern = context.createPattern(imageObj, 'repeat');
context.beginPath();
context.rect(gridSize*item.position[0], gridSize*item.position[1], gridSize, gridSize);
context.fillStyle = pattern;
context.fill();
So it should look like this:
http://jsfiddle.net/rSVkb/6/
If you do want to keep using patterns, you need to switch to using fillRect instead of creating a rectangle and using fill:
drawItem = function(item) {
var imageObj = new Image();
imageObj.onload = function() {
var pattern = context.createPattern(imageObj, 'repeat');
context.fillStyle = pattern;
context.fillRect(gridSize*item.position[0], gridSize*item.position[1], gridSize, gridSize);
};
imageObj.src = item.img;
};
See it in action
.fill was applying the current pattern on the entire context that had been filled already. Continuing to use pattern would allow you to draw multiple in a row doing something like:
itemOne = {
img : 'https://lh3.googleusercontent.com/ZX4Zl7JT1gkgOVA9FbMFnMAw7TC9bBCVMSGWKFTmOW88vDTgcCOb7tBBo60nxoSdHQ=s190',
position : [0, 0], //these get multiplied with "gridSize" in the drawItem-function
howManyX: 2,
howManyY: 1
};
// And then in drawImage
context.fillRect(gridSize*item.position[0], gridSize*item.position[1], gridSize*(item.howManyX || 1), gridSize*(item.howManyY || 1));
And using those as modifiers for the last 2 arguments in fillRect.
Or lets you do multiple positions on your items like this. You could do that with .drawImage as well, but the pattern only needs to be made once. And this jsperf shows that using a pattern can be much faster.

Use image for background of canvas

I am making the simple canvas application.
I want to use image for background of canvas.
I have found out that I should use drawImage(img,x,y).
This is my codes ,but drummap.img doesn't appear.
Where is wrong?
Test
<canvas id="leap-overlay"></canvas>
<script src="leap.js"></script>
<script>
var canvas = document.getElementById("leap-overlay");
// fullscreen
canvas.width = document.body.clientWidth;
canvas.height = document.body.clientHeight;
// create a rendering context
var ctx = canvas.getContext("2d");
ctx.translate(canvas.width/2,canvas.height);
var img = new Image();
img.src = "drummap.jpg";
ctx.drawImage(img,0,0,100,100);
Assuming that your image drummap.jpg is located in said directory, create the image, set the onload to use the new image, and then set the src (see):
var img = new Image();
img.onload = function () {
ctx.drawImage(img, 0, 0, 100, 100);
};
img.src = 'drummap.jpg';
I'm not sure how you're clearing your canvas or what your drawing, but remember your canvas is inherently transparent so feel free to give the canvas a css style background:
<canvas style = "background-image: url(pathtoyourimage.jpg);"></canvas>
Hope that helps.

Frame by frame animation in HTML5 with canvas

I have a flash animation I am trying to convert to HTML5. Now I have taken out all the images. For example in the hand animation, I have taken images of all hand images. I have made the canvas with the base drawing but I don't know how to replace those images frame by frame.
function draw(){
var canvas = document.getElementById('canvas');
if(canvas.getContext){
// canvas animation code here:
var ctx = canvas.getContext('2d');
var lhs = new Image();
lhs.src = "images/left_hnd_1.png";
lhs.onload = function(){
ctx.drawImage(lhs, 293, 137);
}
} else {
// canvas unsupported code here:
document.getElementById('girl').style.display = "block";
}
}
Now I have three more frame for this image. left_hnd_2.png, left_hnd_3.png & left_hnd_4.png. I would've used one image but the difference in frames is way too much for it to be done with one image. How can I animate this with the time differences I want.
Any ideas would be greatly appreciated. Thanks!
Try this:
var imgNumber = 1;
var lastImgNumber = 4;
var ctx = canvas.getContext('2d');
var img = new Image;
img.onload = function(){
ctx.clearRect( 0, 0, ctx.canvas.width, ctx.canvas.height );
ctx.drawImage( img, 0, 0 );
};
var timer = setInterval( function(){
if (imgNumber>lastImgNumber){
clearInterval( timer );
}else{
img.src = "images/left_hnd_"+( imgNumber++ )+".png";
}
}, 1000/15 ); //Draw at 15 frames per second
An alternative, if you only have 4 images, would be to create a single huge image with all four in a 'texture atlas', and then use setTimeout or setInterval to call drawImage() with different parameters to draw different subsets of the image to the canvas.
This worked for me as well! For some reason, it didn't work when I had used the OP's opening code: function draw(){
However when I used: window.onload = function draw() { the animation plays on the canvas. I'm also using about 150 PNG images with an Alpha channel so this is a great way to bring 'video' or create composites to the iPad/iPhone. I confirm that it does work on iPad iOS 4.3.