HTML5 canvas how to draw one picture on top of another - javascript

I want to draw two images on the same canvas. The first image is background.jpg and the second is photo.jpg. I want the photo.jpg always on top of the other:
var ctx = document.getElementById("main").getContext("2d");
var background = new Image();
var photo = new Image();
background.onload = function() {
ctx.drawImage(background, 0, 0);
}
photo.onload = function() {
ctx.drawImage(photo, 0, 0);
}
background.src = "background.jpg";
photo.src = "photo.jpg"
My question is how can I make sure the photo is always on the top. Because the onload are callbacks, I cannot make any assumptions about the calling order. Thanks!

Store your images in an array instead. That will makes sure the order is kept no matter which image finish loading first:
var ctx = document.getElementById("main").getContext("2d");
var background = new Image();
var photo = new Image();
var images = [background, photo]; /// the key
var count = images.length;
background.onload = photo.onload = counter;
background.src = "background.jpg";
photo.src = "photo.jpg"
/// common loader keeping track if loads
function counter() {
count--;
if (count === 0) drawImages();
}
/// is called when all images are loaded
function drawImages() {
for(i = 0; i < images.length; i++)
ctx.drawImage(images[i], 0, 0);
}
(the draw method assumes all being drawn at position 0,0 - of course, change this to meet your criteria).

The foreground could be loaded in the callback for the background
var ctx = document.getElementById("main").getContext("2d");
var background = new Image();
var photo = new Image();
background.onload = function() {
ctx.drawImage(background, 0, 0);
photo.src = "photo.jpg" // after background is loaded, load foreground
}
photo.onload = function() {
ctx.drawImage(photo, 0, 0);
}
background.src = "background.jpg";

Related

Combine Two Images with JavaScript for Watermark

I would like to combine two images (one uploaded from the device and another from the website) and export them as one image. The image uploaded from the device would be the background image and the one from the website is a logo to make in to a watermark in the bottom corner.
Is this possible and if so where should I start?
Thank you
I have been able to add an image on top of another image using the Javascript code below:
function watermarkLogo(elemImage) {
var canvas = document.getElementById("canvas");
canvas.width = elemImage.naturalWidth;
canvas.height = elemImage.naturalHeight;
var myVar = canvas.getContext("2d");
var img1 = loadImage(elemImage.src, myFunction);
var img2 = loadImage('icon.png', myFunction);
var numberOfImages = 0;
function myFunction() {
numberOfImages += 1;
if(numberOfImages == 2) {
myVar.drawImage(img1, 0, 0);
myVar.globalAlpha = 0.5;
var widthOffset = (canvas.width/3)*2;
var heightOffset = (canvas.height/3)*2;
myVar.drawImage(img2, widthOffset-50, heightOffset-50, 100, 100);
}
}
function loadImage(src, onload) {
var img = new Image();
img.onload = onload;
img.src = src;
return img;
}
}

Merging multiple previous drawn canvases

In my current code I have different functions to draw different canvases, these work perfectly. The draw function will be called a number of times and store the result to be called later, once the image is finalised.
I cannot get the two images to merge together. I can redraw them individually in mergeCanvas(), by calling 'testbor1.url' but when I try to draw them onto a new canvas, the result is blank.
Can anyone tell me why I cannot draw these onto one canvas?
Following: How can I put multiple canvas elements in to one canvas element?
function draw()
{
var can3 = document.getElementById('RugCanvas3');
can3.offset = 0;
can3.width = namespacetest.getCanvasWidth();
can3.height = namespacetest.getCanvasHeight();
var ctx3 = can3.getContext('2d');
ctx3.drawImage(drawBorder(70,size,img, borderwidth), 0, 0);
ctx3.drawImage(drawOverlay(borderwidth, 70), 0, 0);
var img = new Image();
img.src = can3.toDataURL("image/png");
namespacetest.dataURLs['img1'] = img.src;
}
namespacetest.mergeCanvas = function()
{
var testbor1 = new Image();
var testbor2 = new Image();
testbor1.src = namespacetest.dataURLs['img1'];
testbor2.src = namespacetest.dataURLs['img2'];
var bor = document.getElementById('Canvas5');
var ctx = bor.getContext("2d");
ctx.drawImage(testbor1, 0, 0, 400, 400);
ctx.drawImage(testbor2, 0, 0, 400, 400);
var buffer_img = new Image();
buffer_img.src = bor.toDataURL("image/png");
var output = document.getElementById('RugCanvas6');
output.innerHTML = '<img src="' + buffer_img.src + '" alt="Canvas Image" />';
}

Canvas Image Animation (Crossfade)

Using HTML5 canvas I'm trying to load images and create a 'crossfade' effect where the first image fades into view then, after a short delay, the second image fades in over top of first image, etc.
With the help of answers to similar questions on this forum I've got 2 separate bits of code working ... one which loads an array of images and a second which animates a 'fade in' effect. My problem is that I don't know how to combine these 2 scripts to load an array of images AND ALSO have each image in array fade in as it loads.
Here are the 2 separate scripts I've got working:
LOAD IMAGE ARRAY INTO CANVAS:
HTML
<canvas id="canvas" width=600 height=350></canvas>
JS
window.onload = function() {
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var delay=2000;
var nextTime=0;
var nextImage=0;
var imageURLs=[];
imageURLs.push("img/sunflower0.jpg");
imageURLs.push("img/sunflower1.jpg");
imageURLs.push("img/sunflower2.jpg");
var imgs=[];
var imagesOK=0;
loadAllImages(start);
function loadAllImages(callback){
for (var i=0; i<imageURLs.length; i++) {
var img = new Image();
imgs.push(img);
img.onload = function(){
imagesOK++;
if (imagesOK >= imageURLs.length ) {
callback();
}
};
img.src = imageURLs[i];
}
}
function start(){
requestAnimationFrame(animate);
}
function animate(currentTime){
requestAnimationFrame(animate);
if(currentTime<nextTime){return;}
ctx.clearRect(0,0,cw,ch);
ctx.drawImage(imgs[nextImage],0,0);
nextTime=currentTime+delay;
nextImage++;
if(nextImage>imgs.length-1){nextImage=0;}
}
} // close window.onload
FADE IN IMAGES AS THEY LOAD INTO CANVAS:
I managed to get a separate bit of code working that does this using Canvas and Greensock TweenMax:
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.18.0/TweenMax.min.js"> </script>
<script>
var ctx, img;
function init() {
ctx = document.getElementById("canvas").getContext("2d");
img = new Image();
img.src = "img/sunflower0.jpg";
img.xpos = 0;
img.ypos = 0;
img.globalAlpha = 0;
img.onload = function() {
TweenMax.ticker.addEventListener("tick",loop);
}
TweenMax.to(img, 5 ,{globalAlpha:1});
}
function loop(){
ctx.clearRect(0,0,500,336);
ctx.globalAlpha = img.globalAlpha;
ctx.drawImage(img, img.xpos, img.ypos);
}
init();
Can anyone show me how to combine these two scripts to get a crossfade effect?
Many thanks!
I would use three canvases for this :
var imgs = [];
var rand = Math.random;
var common = "http://lorempixel.com/500/300?";
var imageURLs = [common + rand(), common + rand(), common + rand(), common + rand(), common + rand()];
var imagesOK = 0;
function loadAllImages(callback) {
for (var i = 0; i < imageURLs.length; i++) {
var img = new Image();
imgs.push(img);
img.onload = function() {
imagesOK++;
if (imagesOK >= imageURLs.length) {
callback();
}
};
img.src = imageURLs[i];
}
}
var ctx = main.getContext('2d');
var last = main.cloneNode(true).getContext('2d');
var next = main.cloneNode(true).getContext('2d');
var current = 0;
var op = 1;
function nextImage() {
if (current++ >= imgs.length - 1) current = 0;
op = 1;
fade();
}
function fade() {
op -= .01;
last.clearRect(0, 0, main.width, main.height);
last.globalAlpha = op;
last.drawImage(imgs[current], 0, 0);
next.clearRect(0, 0, main.width, main.height);
next.globalAlpha = 1 - op;
next.drawImage(imgs[(current + 1) % (imgs.length)], 0, 0);
ctx.clearRect(0, 0, main.width, main.height);
ctx.drawImage(last.canvas, 0, 0);
ctx.drawImage(next.canvas, 0, 0);
if (op <= 0) setTimeout(nextImage, 1500);
else requestAnimationFrame(fade);
}
loadAllImages(fade);
<canvas id="main" width="500" height="300"></canvas>

getImageData() returning all zeros

I'm trying to output the pixel values from an image. The image is loading correctly; another answer on here suggested that the browswer is trying to read the pixels before the image is finished loading, but I can see that it's loaded by the time the alert() fires.
function initContext(canvasID, contextType)
{
var canvas = document.getElementById(canvasID);
var context = canvas.getContext(contextType);
return context;
}
function loadImage(imageSource, context)
{
var imageObj = new Image();
imageObj.onload = function()
{
context.drawImage(imageObj, 0, 0);
};
imageObj.src = imageSource;
return imageObj;
}
function readImage(imageData)
{
console.log();
console.log(imageData.data[0]);
}
var context = initContext('canvas','2d');
var imageObj = loadImage('images/color-test.png',context);
var imageData = context.getImageData(0,0,10,10);
alert();
readImage(imageData);
Image.onload() is called asynchronously when the image has been loaded. That can happen after your current code calls context.getImageData().
The following code should work:
function initContext(canvasID, contextType)
{
var canvas = document.getElementById(canvasID);
var context = canvas.getContext(contextType);
return context;
}
function loadImage(imageSource, context)
{
var imageObj = new Image();
imageObj.onload = function()
{
context.drawImage(imageObj, 0, 0);
var imageData = context.getImageData(0,0,10,10);
readImage(imageData);
};
imageObj.src = imageSource;
return imageObj;
}
function readImage(imageData)
{
console.log();
console.log(imageData.data[0]);
}
var context = initContext('canvas','2d');
var imageObj = loadImage('images/color-test.png',context);
If you want to access the imageData value outside of loadImage(), you have 2 basic choices:
Store the imageData in a variable that is declared outside the function. In this case, the value will be unset until your onload() is called, so the outside code would have to test it for reasonableness before calling readImage(imageData).
Have the outside code register a callback function, and have your onload() call that function with the imageData value.
It is also possible you are evaluating a portion of an image that is transparent. I had this issue while scanning a PNG.
Share a situation I encountered when I used a loop to request multiple images at the same time and get their pixels respectively. Then I encountered this problem.
//my code looks like this
for(let i = 0; i < urls.length; ++i){
let img = new Image() // it will be destoryed every loop begin, even if use keyword 'var' insteads of 'let'.
img.onload = ()=>{
let canvas = document.createElement('canvas')
let ctx = canvas.getContext('2d')
ctx.drawImage(img, 0, 0)
let imgData = ctx.getImageData(0, 0, 1024, 1024) // imgData will be all 0 or some other unexpected result.
}
img.src = urls[i]
}
// fixed bug
window.imgs = []
for(let i = 0; i < urls.length; ++i)
window.imgs[i] = new Image()
for(let i = 0; i < urls.length; ++i){
let img = window.imgs[i]
img.onload = ()=>{
let canvas = document.createElement('canvas')
let ctx = canvas.getContext('2d')
ctx.drawImage(img, 0, 0)
let imgData = ctx.getImageData(0, 0, 1024, 1024)
}
img.src = urls[i]
}
The reason may be object reference, garbage collection and other problems. I don't know. Because I'm a beginner of js.

Src Canvas Flickering

I am trying to figure out how to switch images on a canvas without a small time of blankness in between.
To make my point here is an extreme example of this. In this program, when your spacebar is held down it redraws the image circle.png every milisecond. Because this is so fast the image dissappears.
I did try to preload my image but it didn't help.
Here is my full code:
// Access Canvas
var canvas = document.getElementById("gameBoard");
var ctx = canvas.getContext("2d");
// preload image
var circleReady = false;
var circleImage = new Image();
circleImage.onload = function () {
circleReady = true;
};
circleImage.src = "images/Circle.png";
// Game objects
var circle = {
};
// circle location
circle.x = canvas.width / 2;
circle.y = canvas.height / 2;
// Keyboard events
var keysDown = {};
addEventListener("keydown", function (e) {
keysDown[e.keyCode] = true;
}, false);
addEventListener("keyup", function (e) {
delete keysDown[e.keyCode];
}, false);
// Update Objects
var update = function () {
if (32 in keysDown) { // Player space pressed
circleImage.src = "images/Circle.png"; //re-draws image
}
};
// Draws Everything
var render = function () {
ctx.fillStyle = "#FFFFFF";
ctx.fillRect(0,0,600,609);
if (circleReady) {
ctx.drawImage(circleImage, circle.x, circle.y);
}
};
// The main loop
var main = function () {
update();
render();
};
// Starts Function
var then = Date.now();
setInterval(main, 1); // Execute as fast as possible
You are reloading the image in the update function which is causing your delay:
circleImage.src = "images/Circle.png"; //re-draws image
All you have to do is drawImage without reloading the image:
ctx.drawImage(circleImage, circle.x, circle.y);
Here is an example of an image loader that loads all your images before execution begins:
var imageURLs=[]; // put the paths to your images here
var imagesOK=0;
var imgs=[];
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/house1.jpg");
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/house2.jpg");
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/house3.jpg");
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/house4.jpg");
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/house5.jpg");
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/house6.jpg");
loadAllImages(start);
function loadAllImages(callback){
for (var i=0; i<imageURLs.length; i++) {
var img = new Image();
imgs.push(img);
img.onload = function(){
imagesOK++;
if (imagesOK>=imageURLs.length ) {
callback();
}
};
img.onerror=function(){alert("image load failed");}
img.crossOrigin="anonymous";
img.src = imageURLs[i];
}
}
function start(){
// all your images are fully loaded so begin your app
}
You can have two canvases, one for each image. When you want to switch. Remove or hide the one in the front, leaving (instantly) the one in the back.
Per your comment, here is an example of 26 letters of the alphabet. We only keep two canvases at a time. Each time the user clicks whe add a new only, and make the hidden one visible, with no delay.
function createCanvas(letter) {
var canvas = document.createElement('canvas');
canvas.id = letter;
canvas.style.display = 'none';
document.getElementById('container').appendChild(canvas);
var image = new Image();
image.src = 'http://icons.iconarchive.com/icons/iconicon/alpha-magnets/128/Letter-'+letter+'-icon.png';
image.onload = function() {
canvas.getContext('2d').drawImage(this, 0, 0);
};
return canvas;
}
createCanvas('a').style.display = '';
createCanvas('b');
var prevCharCode = 'a'.charCodeAt(0);
var charCode = 'b'.charCodeAt(0);
document.addEventListener('click', function() {
document.getElementById(String.fromCharCode(charCode)).style.display = '';
document.getElementById('container').removeChild(
document.getElementById(String.fromCharCode(prevCharCode))
);
prevCharCode = charCode;
if(++charCode > 'z'.charCodeAt(0)) {
charCode = 'a'.charCodeAt(0);
}
createCanvas(String.fromCharCode(charCode));
});

Categories

Resources