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));
});
Related
This question already has answers here:
Callback/scope understanding
(3 answers)
Closed 1 year ago.
I am having trouble drawing an image with CSS filters onto a canvas. I want to apply the CSS filters with sliders/range inputs that I have. The image that the filters will be applied to is an image that you upload from your files. This image is put into a variable in a function I have that will upload the image. The function for the slider is separate, and therefore I can't apply the filters and draw the image again in the function that is activated when the sliders are being dragged.
I need it to be drawn instead of just putting the filters on the canvas itself so that I can download the image with filters.
Here is the javascript code:
var imageLoader = document.getElementById('imageLoader');
imageLoader.addEventListener('change', handleImage, false);
var canvas = document.getElementById('imageCanvas');
var ctx = canvas.getContext('2d');
function handleImage(e) {
var reader = new FileReader();
reader.onload = function(event) {
var img = new Image();
img.onload = function() {
var ratio = this.height / this.width;
canvas.height = canvas.width * ratio;
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
}
img.src = event.target.result;
}
reader.readAsDataURL(e.target.files[0]);
}
var container = document.getElementById("container")
var download = document.getElementById("download")
var adjustments = document.getElementById("adjustments")
download.addEventListener("click", function() {
var link = document.createElement('a');
link.download = 'filename.png';
link.href = canvas.toDataURL();
link.click();
});
var prop1 = document.getElementById("prop1");
var open = true;
adjustments.addEventListener("click", function() {
if (open === true) {
prop1.style = "margin-left: -240px;";
adjustments.classList.remove("line");
open = false;
} else if (open === false) {
prop1.style = "margin-left: 50px;";
adjustments.classList.add("line");
open = true;
}
});
var contrast = document.getElementById("contrast");
var brightness = document.getElementById("brightness");
var slider1 = contrast.value;
var slider2 = brightness.value;
function effect() {
slider1 = contrast.value;
slider2 = brightness.value;
ctx.filter = "contrast(" + slider1 + ") brightness(" + slider2 + ")";
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
}
contrast.oninput = function() {
effect()
};
brightness.oninput = function() {
effect()
};
You could try clearing the canvas with ctx.clearRect() then redraw everything on the canvas. I'd also recommend creating a function to redraw the items just for easier reading.
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;
}
}
I am currently building an application, in which a user can upload several images and work with them simultaneously. Since some processes performed poorly due to large image files, I want to resize them, before the user gets them.
In my resizer() function I try to resize using canvas. It works, but since the 'canvas.toDataURL()' is inside the img.onload function, I don't know how to return the value and parse it to the handleFiles() function.
Also...I tried some cases in which I got some code from the handleFiles() - like:
var preview = document.getElementById("img"+count);
var surface = document.getElementById("cubface"+count);
var count = counterImg();
preview.src = resizer(e.target.result, count);
surface.src = resizer(e.target.result, count);
And put them in the end of the img.onload function like
var preview = document.getElementById("img"+number);
var surface = document.getElementById("cubface"+number);
preview.src = canvas.toDataURL;
surface.src = canvas.toDataURL;
I got the resize, but I got only the last image from the loop to be processed.
So the questions are:
In the resizer() function, how to return the canvas.toDataURL value which is in img.onload?
Why does the loop cover only the last instance and not every image and how to solve that?
Full code:
JavaScript:
function resizer(base64, number){
// Max size for thumbnail
if(typeof(maxWidth) === 'undefined') maxWidth = 1200;
if(typeof(maxHeight) === 'undefined') maxHeight = 1200;
var img = new Image();
img.src = base64;
// Create and initialize two canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
var canvasCopy = document.createElement("canvas");
var copyContext = canvasCopy.getContext("2d");
img.onload = function() {
// Determine new ratio based on max size
var ratio = 1;
if (img.width > maxWidth)
ratio = maxWidth / img.width;
else if (img.height > maxHeight)
ratio = maxHeight / img.height;
// Draw original image in second canvas
canvasCopy.width = img.width;
canvasCopy.height = img.height;
copyContext.drawImage(img, 0, 0);
// Copy and resize second canvas to first canvas
canvas.width = img.width * ratio;
canvas.height = img.height * ratio;
ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);
return canvas.toDataURL();
};
return img.onload;
}
function handleFiles(files) {
for (var i = 0; i < files.length; i++) {
var file = files[i];
var count = counterImg();
var preview = document.getElementById("img"+count);
var surface = document.getElementById("cubface"+count);
var reader = new FileReader();
reader.onload = (function (preview, surface) {
return function (e) {
var newimage = resizer(e.target.result, count);
preview.src = newimage;
surface.src = newimage;
$('#image-cropper'+count).cropit('imageSrc', e.target.result);
}
})(preview, surface);
reader.readAsDataURL(file);
}
}
The variable count is not scoped properly.
There's a delay between the time you declare the function and the time you use it, therefore, each execution ends up with the same value. So the jquery selector will always return the same element. Which explains why only the last image is modified.
Here's a jsfiddle that demonstrate the execution.
https://jsfiddle.net/Lc6bngv5/1/
To fix this, simply pass count to the function that encapsulate your preview and surface :
reader.onload = (function (preview, surface, count) {
return function (e) {
var newimage = resizer(e.target.result, count);
preview.src = newimage;
surface.src = newimage;
$('#image-cropper'+count).cropit('imageSrc', e.target.result);
}
})(preview, surface, count);
For the second question :
The resize function returns a function. It does not return the result of that function. To get the url properly, I would use a callback function :
reader.onload = (function (preview, surface, count) {
return function (e) {
var newimage = resizer(e.target.result, count, function(url){
preview.src = url;
surface.src = url;
$('#image-cropper'+count).cropit('imageSrc', e.target.result);
});
}
})(preview, surface, count);
And you would have to do the following changes in resize :
function resizer(base64, number, cb){
...
img.onload = function() {
...
// return canvas.toDataURL();
cb(canvas.toDataURL());
};
}
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>
I'm trying to create a simple Sprite class for use in a canvas game, but the image I specify when I create a new instance of the class never calls onload or onerror.
What's happening, and how do I get it to load and draw the image?
Here's the script:
var cvs = document.getElementById("c");
var ctx = cvs.getContext("2d");
var imagesToLoad = 1 ;
var imagesLoaded = 0;
var gameState = 0;
//START OF IMAGE STUFF//
function imageOnload() {
imagesLoaded+=1;
console.log("Images: loaded "+imagesLoaded+" overall.");
}
function loadLoop() {
if (imagesLoaded == imagesToLoad) {
gameState = 1;
}
}
function loop1() {
ctx.drawImage(circle.image);
}
function gameLoop() {
if(gameState==0){
loadLoop();
}
if(gameState==1){
loop1();
}
}
function Sprite(positionsArray){
this.x = positionsArray[0];
this.y = positionsArray[1];
this.dx = positionsArray[2];
this.dy = positionsArray[3];
this.angle = positionsArray[4];
this.rotating = positionsArray[5];
};
circle = new Sprite(new Array(0, 0, 0, 0, 0, 0));
circle.image = new Image().onload="imageOnload()".src="circle.png".onerror="console.log(\"Nope.\")";
console.log("circle?");
setInterval(function(){gameLoop()}, 10);
//END OF IMAGE STUFF//
Corrections:
No quotes on image.onload function definition
No parens on image.onload function definition
image.onerror requires a function definition or anonymous function
Put image.onerror before image.src
Here's some example code:
circle.image=new Image();
circle.image.onload=imageOnLoad;
circle.image.onerror=function(){console.log("Image failed to load")};
circle.image.src="koolaidman.png";
function imageOnLoad(){
ctx.drawImage(circle.image,0,0); // or do other stuff
}