I am having issues with JavaScript and Canvas particularly in Firefox. I am trying to create an canvas image viewer that will run when canvas is enabled in a browser in place of standard img tag. Javascript changes the image from an array of src paths when an arrow is clicked. For some reason Firefox is always one img out of sync. When I test it in chrome the imgs are in sync but the first one is not displayed.
here is the image object
var image = new Image();
image.src = myImage.src[0];
here is my initial canvas intialization
if(canvas()){ //canvas stuff
var myc = document.createElement('canvas');
myc.id='mycanvasx';
mycanvas = document.getElementById('mycanvasx');
myc.width = image.width;
myc.height = image.height;
//viewer.replaceChild(myc, scroller);
viewer.appendChild(myc);
var context = myc.getContext("2d");
dw = image.width;
dh = image.height;
context.clearRect(0, 0, myc.width, myc.height);
context.drawImage(image, 0, 0, dw, dh);
}
and when the arrow is pressed a function like this is called
function update(){
if(canvas()){
context.clearRect(0,0,myc.width, myc.height) //maybe bloat
myc.width = image.width;
myc.height = image.height;
dw = image.width;
dh = image.height;
context.drawImage(image, 0, 0, dw, dh);
}
}
function left(){ //these move the image through the source array
if(myImage.num > 0){myImage.num--;}else{
myImage.num = (myImage.src.length-1)}
image.src = myImage.src[myImage.num];
scroller.src = image.src;
update();
x=0;
}
I know I must be missing something simple here. I'm using firefox 4.0.1 and chrome 11
You're setting the src of the image and then immediately painting it into the canvas. But image loads are async. So you're painting it before the new src you set has loaded; since you keep reusing the same image it's still showing the previous image at that point.
You want to run update() off an onload listener on image.
Related
Assume I have a an image and a canvas. I use the canvas api to modify the displayed image.
I now need to have 2 copies of the image, the original and the modified one.
the pseudo code to convey what I am trying to do would be:
var img = fetchImage();
context.drawLine(0, 0, 100, 100);
var copy = context.getImage();
I have tried searching it up but I don't find an answer to this.
Edit:
By "initialise", I mean creating a deep copy of an image. So, the original image has no line, the modified image in the canvas has a line, I now need 2 independent images, one with the line and one without.
You get an image out of a drawn canvas with the getImageData method.
Or perhaps even toDataURL might be of help.
If you're working with image data URL's, try this snippet
let imageDataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAFIklEQVR4nO2aT2wUVRzHv7+36+y2NYUCLoYDZrHLVhoVNOpBSWgk6KUxxLSEEJUjakI9STzISaIxRtSEhIPxvxeQYAKJsf6ploNRiyY2DdPZXTbpwdpVhPCnbKez8/XQNTRhp/vnzezWOJ9b+95832++773fe2/eAiEhISH/YyQIUZKRqampzmKx2K6UEsMwrp0/f/5KX1+fE0R7OvhqgGma65RSOwBsJ5kGsKpcdJHkpIh8aRjGcDKZ/MPPdnXwxYB8Ph+3bXsQwAsA7gYQ9ajqkPwNwJuxWOxEMpks+tG+DtoG5HK5FY7jHATwLIC2Wp4hOQvgSCQSeSWVSl3WjUEHLQPKPX8IwBCASJ2POyQPRyKRl1Op1JxOHDoonYdt295Fch/qf3lgYZo8XyqVBnRi0KXhEWCa5joROQ1gi2YMZ0ulUv+mTZumF68eABCPx2fXr19/WURKmm140rABlmXtJfkuGuv9xcwDOEDyT6XU4+XVo6tcdlFEJgF87brucE9Pz++abd1EQwaQjGQymQ9J7vEpjmsADAC3eJQ7AMYBvGUYxjE/V4+GcsDU1FRnuaf8ogPeLw8s5IstAI7atn0ol8ut8Kvhhgwoz9GuqhX9pw3AkOM4B/P5fNwPQa1VoEVESO6zbXuXH2INGRCPx2cBXPQjgEYQkXYAQ6ZprtPVqssAkurcuXMbi8XiXgBrdRvX5J7yuUMLrz37TeTz+dszmcxzSqmnANyBgE6SdRDBwqHrY519Qk0GWJa12bbtNwD0YRnlDZLp8fHxTmhMx6oGWJa1meR70N/xBUFXLBZrh4YBS/ZmOckcxvJ8eV/wHAEkJZPJ7CO5rZkB1YOIXHddd3U2m1WlUsmNRqNXN2zYcEVE3Jo1vApM00yLyBcAkr5EGwxzAAokKSLzJAsiMqaUOmHb9g+9vb12NQFPAyzL2k/ybV/DbR6XROQDpdSr3d3dhaUqVswBIyMjUQCPBBJac1hJcsh13SPZbDaxVMWKBiQSiU7Xde8MJramISSfdF33pbGxMc+DVkUDDMNoF5GVwcXWNATAMx0dHQ97VVg2m5qgINklIju9yisaYNv2LMlLwYXVdB7w+oZQ0YBCoXBZKZULNqbmISIJx3FurVRW0YDyFdaZQKNqLvQq8MwBrusOA8gHEk7zKSilrlQq8DQgnU5bIvIJlnDvv4KI/Ox1A+V5FhARmqZ5VETuA3AvyZr318sFEREAV0me8KxTTWRiYmJVPB6vmED+C8zPzzvT09OF5Xg1H7IcCO67Hin931mrAeDUto0XINJYMvVLx4PAtsL9o9kBAMMCfPXEqDXYah0vAhkB/d9meiHu5wC6F1rhL6A8dqov/VcrdJbC9xEwMDFhQLn78W/QZaIlt66h65dONXw34PpMdCuIxUO1RFc+Pbn9rgut0KmG7waIyBYAi74lyBkRfNQqnWoEYIB7FsBM+c8ZCF9vZM76pVONmq/GaiVWSI/OJSZ3k3iQiPzYVuhu6FTpl041fFkFBo4xcv227FZB6SER/BQrpEePD9Z/X+eXTj34MgKKCWuHEO8DspbEzFxicjeAkVbp1IN2DugfmVwDyou4cV2+llT3t0qnXrQNIPE0wK2L/nWJ5K+t0qkXrSnQPzK5BsI9oNz4qZzgWFvCqSth+aXTCH4vg1m46p3jNdzJNUmnKtqrQP/3mUGwdAAUEuq1032pz1qpUy/6yyApO78xVwHAyUd7/tY59vqiExISEhISEhISUhv/AESDVQMMfbjKAAAAAElFTkSuQmCC';
let image = document.querySelector('img');
image.src = imageDataUrl;
let canvas;
image.addEventListener('load', () => {
canvas = document.querySelector('canvas');
canvas.width = image.width;
canvas.height = image.height;
let context = canvas.getContext('2d');
context.drawImage(image, 0, 0);
let margin = 10;
context.strokeRect(margin, margin, canvas.width - margin * 2, canvas.height - margin * 2);
console.log('new data url:', canvas.toDataURL());
});
<div>
<img src="https://www.google.com/logos/doodles/2019/us-teacher-appreciation-week-2019-begins-4994791740801024-l.png">
</div>
<div>
<canvas></canvas>
</div>
with this code I try to update a canvas with a drawn image when imageUpdated is true.
I use a timetag so the image isnt loaded from the browser cache.
But when i use timetags the canvas stays empty.
It works without the timetags.
var imageUpdated = true;
if(imageUpdated)
{
imageUpdated = false;
var heightImg = new Image;
heightImg.src = '/path/to/image.png?' + new Date().getTime().toString();
this.canvas = $('<canvas />')[0];
this.canvas.width = this.width;
this.canvas.height = this.height;
this.canvas.getContext('2d').drawImage(heightImg, 0, 0, this.width, this.height);
}
/*rest of event happens here*/
Thanks!
Probably it's because you're drawing the image before it is loaded.
I guess that's also the reason it works without the timetag, it's already in cache so can instantly be drawn.
A solution would be to draw after the image is loaded. So, add an eventListener on the image:
image.addEventListener('load', callback);
Within the callback, draw the image on the canvas.
For example:
// add eventlistener
heightImg.addEventListener('load', function(e) {
var width = heightImg.width;
var height = heightImg.height;
var canvas = document.querySelector('canvas');
canvas.width = width;
canvas.height = height;
canvas.getContext('2d').drawImage(heightImg, 0, 0, width, height);
});
// trigger the loading
heightImg.src = url + '?time=' + new Date().getTime().toString();
And a Fiddle
So I am creating a cordova app where I take a photo from the iphone library, draw it to canvas and add another image to it in order to save it as one photo. So far the photo I draw from the iphone photo library gets drawn without problems to the canvas, however the second image doesn't.
When I load the second image, it first gets added to a div with absolute positioning in order to move it to wherever I want. After that I get the actual image it's source and it's positions and try to draw it to canvas. The drawing of the second image happens when I call a method that also performs the canvas2ImagePlugin it's functions. In the end only the first photo without the second image gets saved.
The draw image to canvas function:
function drawImage(image_source, dx, dy)
{
var canvas = document.getElementById('Photo');
var image = new Image();
image.src = image_source;
image.onload = function() {
c=canvas.getContext("2d");
c.canvas.width = window.innerWidth;
c.canvas.height = window.innerHeight;
c.drawImage(image,dx,dy, window.innerWidth, window.innerHeight);
}
}
The method for drawing the second image and saving it:
function saveImage()
{
var img = $('.ObjectImage').attr('src', $('img:first').attr('src'));
var imagePosition = $('.ObjectImage').find('img:first').position();
drawImage(img, imgPosition.left, imgPosition.top);
window.canvas2ImagePlugin.saveImageDataToLibrary(
function(msg){
console.log(msg);
},
function(err){
console.log(err);
},
document.getElementById('Photo')
);
alert("Image saved");
}
The window.innerWidth, window.innerHeight on the canvas is done to get the canvas in full screen of the parent div.
EDIT to the comment:
function drawImage(image_source, dx, dy)
{
var canvas = document.getElementById('Photo');
var image = new Image();
image.onload = function() {
c=canvas.getContext("2d");
c.canvas.width = window.innerWidth;
c.canvas.height = window.innerHeight;
c.drawImage(image,dx,dy, window.innerWidth, window.innerHeight);
}
image.src = image_source;
}
Still not working
The drawImage function works asynchronously, it starts loading an image and exits immediately. Then, when the image loads, the canvas is updated. If you run something like:
drawImage('test.jpg',0,0);
drawImage('test2.jpg',0,0);
you will get both images updating the canvas at approximately the same time and only one will appear.
Also, what wolfhammer said is correct. If you set the size of the canvas, you clear it, so drawing one image after the other, even if they are smaller sizes and should both appear, will only show the last one. Check this link on how to solve it: Preventing Canvas Clear when Resizing Window
Further more, you are drawing all images with the width and height of the window, which doesn't make sense. Probably you want to use the width and height of the image (so this.width instead of window.innerWidth)
When you set the width and height of the canvas the data on "Photo" is cleared. I've provide a resize function if resizing is really needed.
function drawImage(image_source, dx, dy)
{
var canvas = document.getElementById('Photo');
var image = new Image();
image.src = image_source;
image.onload = function() {
c=canvas.getContext("2d");
//c.canvas.width = window.innerWidth;
//c.canvas.height = window.innerHeight;
c.drawImage(image,dx,dy, window.innerWidth, window.innerHeight);
}
}
var can = document.getElementById('can');
var ctx = can.getContext('2d');
ctx.fillStyle = "red";
ctx.beginPath();
ctx.moveTo(20, 90);
ctx.lineTo(50, 10);
ctx.lineTo(80, 90);
ctx.lineTo(10, 40);
ctx.lineTo(90, 40);
ctx.lineTo(20, 90);
ctx.fill();
var btn = document.getElementById('btnResize');
btn.addEventListener('click', function() {
resize(can, can.width * 2, can.height * 2);
});
function resize(can, w, h) {
var ctx = can.getContext('2d');
// copy
var can2 = document.createElement('canvas');
var ctx2 = can2.getContext('2d');
can2.width = can.width;
can2.height = can.height;
ctx2.drawImage(can, 0, 0);
// resize
can.width = w;
can.height = h;
ctx.drawImage(can2, 0, 0, can2.width, can2.height, 0, 0, w, h);
}
#can {
border:1px solid red;
}
<button id='btnResize'>Size x 2</button><br/>
<canvas id="can" width="100" height="100"></canvas>
On load of my image I:
var img = new Image();
img.src = e.target.result;
var canvas = $('#test-canvas')[0];
$('#test-canvas').width(img.width);
$('#test-canvas').height(img.height);
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
But the image is drawn larger than it's original size? I've tried a few images, same problem.
What's the fix?
jQuery will resize the element through CSS, which won't actually change the canvas internal height and width. This will resize the actual canvas element.
var canvas = document.GetElementById('test-canvas');
canvas.width = img.width;
canvas.height = img.height;
jsFiddle (http://jsfiddle.net/L0drfwgL/) just show it drawing it to scale, and resizing the canvas item itself.
It looks like you are pulling the image from an image element on load. You can use the src from the image element rather than recreating an image object and then get the image width/height from the element to draw the image to canvas.
<script>
$(document).ready(function(){
$('#testImg').on('load',function(){
var image = document.getElementById('testImg');
var canvas = document.getElementById('test-canvas');
canvas.width = image.width;
canvas.height = image.height;
var ctx=canvas.getContext("2d");
ctx.drawImage(image,0,0);
})
});
</script>
/** with vue3 -(to fix drawing canvas image is larger than actual image size **/
const captureImage = () => {
let width = video.value.getBoundingClientRect().width;
let height = video.value.getBoundingClientRect().height;
let context = canvas.value.getContext('2d')
canvas.value.width = width
canvas.value.height = height
context.drawImage(video.value, 0, 0, width, height)
}
I'm trying to load an image from a URL into a HTML canvas at a 1:1 scale. I load the image, and set the canvas DOM element to the appropriate dimensions, but for some reason the image in the canvas is significantly upscaled and therefore only the top left hand corner is drawn.
This is demonstrated by the following JSFiddle: http://jsfiddle.net/KdrYr/1/
var img = new Image();
var cv = document.getElementById('thecanvas');
img.src = 'http://www.photographyblogger.net/wp-content/uploads/2009/12/Picture-in-a-picture4.jpg';
img.onload = function() {
var ctx = cv.getContext('2d');
cv.style.width = img.width + 'px';
cv.style.height = img.height + 'px';
ctx.drawImage(img, 0, 0);
};
For example, I'm trying to draw this (sorry about the big images :/)
But end up with this
What could be causing this?
You need to assign the canvas actual width and height, not via its style:
cv.width = img.width;
cv.height = img.height;
Live test case.
As for the why, well, it's explained already here.