This question already has an answer here:
Why is the image drawn in the canvas when debugging but not when running?
(1 answer)
Closed 5 years ago.
I have a canvas :
<canvas id="mandarin" width="235" height="300" style="border:1px solid #000000;"></canvas>
And I want to insert an image into the canvas when the window loads, however this code doesn't render anything:
window.onload = function() {
var context = document.getElementById("mandarin").getContext("2d");
var ourImage = document.createElement("IMG");
ourImage.src = "mandarin.jpg";
ourImage.setAttribute("width", 235);
ourImage.setAttribute("height", 300);
context.drawImage(ourImage, 10, 10);
};
But when I include this line , irrelevant of the code above and outside the body it works.
<img id="mandarinImg" src="mandarin.jpg" alt="The Scream" width="250" height="350">
But the problem is I have the canvas and the image side by side. All I need to display is the canvas with the image inside.
How would I insert this DOM image into the canvas ?
When you set the source of the image (ourImage.src = "mandarin.jpg") it is not loaded yet. This means if you draw the image afterwards it will not do it. Because your image isnt loaded. You should wait for the image to load and then draw it:
ourImage.onload = function(){
context.drawImage(ourImage, 10, 10);
}
ourImage.src = "mandarin.jpg"
(Note that it's important to hook the event before setting src. There may be only one main JavaScript UI thread, but the browser is not single-threaded. It's perfectly valid for the browser to respond to your setting src by immediately triggering a load event, seeing the element doesn't have any handlers for it, and therefore not queuing a callback. And at least one browser circa ~2008 did, if the image was in cache and usable.)
Related
I'm building a photo application using electron that loads user photos from the file system. These photos can easily be 7MB or more in size. The application allows the user to switch between photo's using the keyboard arrows, at which point I want the new photo to display extremely fast.
For a 7MB image, just changing the src of an existing image tag in the DOM can take ~200-300ms, webkit must load the file, decode the file, and render the file on the page. The loading and decoding take 100-150ms each. (actually the profiler just says 2 x decoding, but the next step removes one of those decodes, so I presume it's related to the file read).
Preloading an img tag...
var img = new Image();
img.src = "/path/to/photo.jpg"
...means that webkit preloads the file, and this strips the file load time, but there is still a 100-150ms delay in appending like this...
domElement.appendChild(img);
...because the read data must still be decoded for the item to be appended to the DOM.
Is there a way to pre-decode the image, so that appending to the DOM does not have a 100-150ms delay, and only the fast rendering is required?
No you cannot "pre-decode". However, you can pre-append the img in an effectively invisible way by applying the style width: 1px; height: 1px; opacity: 0.01, and webkit won't redo the work if you append again.
You can even remove the image in the mean time, provided it has had time to fully decode, and webkit will hold on to the decoded data (although I'm not sure for how long).
If you want to be absolutely certain it will load fast, you could do one of two things. Reveal the img tag by removing the styles above, or by loading the same img tag in a different part of the DOM, while leaving the 'pre-appended' one in place. This will take between 3ms and 20ms in my experience.
BE CAREFUL regarding cleanup if you are using a lot of user defined photo contents.
In my experience, simply removing numerous img elements from the DOM will cause memory leaks (and electron windows to crash). I would advise that you either set the img.src to null after removing the image from the DOM, or set the entire img to null if you no longer need the Image instance.
You could play with the following code (use images of your own) using the chrome devtools timeline to measure the render speeds of photos in different scenarios.
<style>
/*#preload img {
width: 1px;
height: 1px;
opacity: 0.01;
}*/
</style>
<button>Toggle Image</button>
<div id="container"></div>
<div id="preload"></div>
<script>
"use strict"
const button = document.getElementsByTagName('button')[0]
, container = document.getElementById('container')
, preload = document.getElementById('preload')
, img1 = new Image()
, img2 = new Image()
var current = img2
img1.src = './img1.JPG'
img2.src = './img2.JPG'
preload.appendChild(img2)
setTimeout(function(){
preload.removeChild(preload.childNodes[0])
}, 200)
button.addEventListener('click', function(e){
toggleImg()
})
function toggleImg(){
if (current === img1) {
setImg(img2);
} else {
setImg(img1)
}
}
function setImg(img){
if (container.hasChildNodes()) container.removeChild(container.childNodes[0])
container.appendChild(img)
current = img
}
</script>
I'd suggest experimenting with using the HTML5 Canvas to render your images, that way you can load the next image ahead of time and have more direct control over the caching strategy.
This question already has answers here:
jQuery event for images loaded
(14 answers)
Closed 8 years ago.
I want to create a picture preview plugin and for this i want to create a dynamic bar under the preview image with a title and so on
The Images have no consistent width so I need to adjust the bar everytime I select a new Picture.
The preview Image source is also created dynamicly with the source path of the clicked image but another directory.
To adjust the bar i ask for the preview image's width: var width = $("#maximized_image").width(); and I call this directly after I set the source of the preview image.
But my problem is that sometimes apparently the image is loaded after the width is defined so my width is 0 and my bar is not visible. Is it possible to wait for the image is loaded or force jquery to wait for it?
Or does anyone have another solution?
EDIT:
Here is what I have till now:
var img = $(this).attr("src");
var img_split = img.split('.');
var img_path = img_split[0];
var rest = img_path.substring(0, img_path.lastIndexOf("/") + 1);
var last = img_path.substring(img_path.lastIndexOf("/") + 1, img_path.length);
var max_image = rest + "/normal/" + last + "." + img_split[1];
$("#image_maximizer").css("height", win_h).css("width", "100%").css("display", "block").css("background", "rgba(0, 0, 0, 0.6)");
$("#render_img").attr("src", max_image);
var width = $("#maximized_image").width();
alert(width);
this sets the image but i don't get the image's width
you can load the image but render it off screen, get the width then load it where you need it (you wont be loading it twice as its already loaded/cached)
I'd suggest though that you load the image THEN load in the bar with a nice fade effect or something, I'm sure a slight 300ms etc delay wont make too much difference?
Using something like onLoad for the image will say when the image is loaded but it will still have to be rendered first
Run a function when the page is fully loaded including graphics:
$( window ).load(function() {
// Run code
});
jquery load event
I have an image and I want to draw on it. To do that, I use jQuery to hide the image:
$("img").hide();
And then I create a canvas and put it in the same div with id drawing in the html. I then set the background of the canvas to be the same as the img src for the image I hid. This makes it look like an image but now it is actually a canvas with the image as it's background. I do this by:
$('#drawing > canvas').css('background-image','url('+$(".image img").attr('src')+')');
context.canvas.width = $("img").width();
context.canvas.height = $("img").height();
The issue I am having is that sometimes, the image isn't displayed in the canvas and the canvas is not the size of the image. I think it's probably because of some loading issue. How can I wait for the canvas to have the image displayed in the background for sure? Thank you
Edit: Note that in the DOM, the canvas always has the right src. It just doesn't display it
Edit 2: Here's the JSfiddle. Here, everything seems fine but I have a lot more going on in my code including fetching stuff from the server so it's slower there. Hope this helps you guys to understand the problem: http://jsfiddle.net/wL3ezLke/2/
Thanks again
You need to use:
$(function(){
// Code executed once the DOM is loaded.
});
Official documentation:
https://api.jquery.com/ready/
If I understand correctly your problem is knowing when the image loaded (from what you describe it could be a lot of other problems though).
To test if an image has loaded it's pretty simple.
var $img = $('#hiddenImg');
if ($img[0].complete) doCanvasStuff();
else {
$img.on('load', function(e) {
var $canvas = $('#drawCanvas');
$canvas.css({width: $img.width(), height: $img.height()});
//you can go ahead with the background image, but this is preferable
var ctx = $canvas[0].getContext("2d");
ctx.drawImage(img,0,0);
}
}
This should make sure you canvas loads an image only after it was loaded, or do canvas stuff right away if image was loaded, fiddle
Change
$('#drawing > canvas').css('background-image','url('+$(".image img").attr('src')+')');
context.canvas.width = $("img").width();
context.canvas.height = $("img").height();
to
context.canvas.width = $("img").width();
context.canvas.height = $("img").height();
$('#drawing > canvas').css('background-image','url('+$(".image img").attr('src')+')');
so the height and width are set before the image goes into the background.
I've a problem with image flickering with large images.
In my body i have 5 images:
<img id="img1" src="img1.png" width="250">
<img id="img2" src="img2.png" width="250">
<img id="img3" src="img3.png" width="250">
<img id="img4" src="img4.png" width="250">
<img id="img5" src="img5.png" width="250">
and one I'm dragging one of them with jQuery UI, all are changing their src and on dragend as well:
function dragStart() {
$('#img2').attr('src','newimg2.png');
$('#img3').attr('src','newimg3.png');
$('#img4').attr('src','newimg4.png');
$('#img5').attr('src','newimg5.png'); }
so fine so good. But I need to use large images (2000 x 2000px) because all images can be clicked and then they will animate to the full size of the viewport that they dont pixelate.
$this.animate(
{ width: 1800, top: -650, left: -250 },
{
duration: 4000,
easing: 'easeOutElastic'
})
I think because of the size of every image, they are flickering. Does anyone of you have an idea how to prevent this flickering on images, if all src change at the same time ?
Thanks for your effort
The problem you described does not sound like a pre-loading issue to me.
For preloading would happen, when you load ANOTHER image from the server once you start to move it around. But like I have read your Question you are moving the DOM-object containing your image in SRC around.
Thats most likely a Browser issue, because he has to scale your images down from 2k x 2k to lets say 100 x 100. That is some expensive interpolation stuff to do there.
So your main problem could be, like you mentioned, the size of the image.
Even preloading would not be of use, because you would have the same issues then.
In my eyes you should have two versions of your image: One small one (the size you want to drag around) and a big one, the one you want to display.
The big one can either be loaded automatically in background or on demand, when a user clicks on an image.
In the web it is quite common, to show scale the small image to screen size with smooth animations and start to preload in the background and when the preload finished, replace the fullscreen image to remove the pixel effect.
I hope I made myself clear.
The key to what you are trying to do is called preloading. However, you'll need to think carefully about how you want to do this.
Preloading involves loading the image in an img tag off-screen, but still in the DOM. This caches the image locally, which means that the next time you attempt to use the same source, it'll pull from cache instead of querying the server for the image (and, thus, flicker).
Preloading an image is a simple matter:
(new Image()).src="mysource.png";
What you want to decide is when you want to load the images. IF you load them all at first, you'll potentially use up a lot of bandwidth. If you load them on-click, you'll get buffering.
You can check if an image is loaded using the onload event present on img tags and wrapped within jQuery if needed, as follows:
var i = new Image();
i.onload = function() {
console.log("Loaded");
}
i.src = "mysource.png";
Credits to Robin Leboeuf for the concise Image() form.
You can use a function like this to preload your images:
function imagesPreload(){
var imgArray = new Array("path/to/img1.jpg", "path/to/img2.jpg", "path/to/img3.jpg");
for (var i=0; i<imgArray.length; i++) {
(new Image()).src = imgArray[i];
}
}
See the comments. You should ensure that the images are loaded before you show them. This is called pre-loading and can e.g. be achieved by having hidden images (not using display:none but placing them offscreen) that have the SRC that you want.
Edit: see the more elaborate answer by #Sebástien !
I'm making a canvas webpage for animation. Strange thing is that every time I launch the webpage from double clicking the html file the canvas will almost always size incorrectly. I have post a similar question before: Canvas sizing incorrectly on loading but have no desired answer.
After extensive debugging I find out that it's the window.innerWidth that's giving me such trouble. The window.innerWidth returns 0 every time the page is launched from double clicking the html file and results in a incorrect canvas size (interestingly the canvas size is not exactly 0, but a very small one that has object to be rendered stack on top of one another), but after reloading the page (Ctrl+R) the problem no longer happens. I'm using jQuery to load the page, here is my code:
html:
<body>
<canvas id="main_canvas"></canvas>
</body>
js:
$(document).ready(function() {
var SCREEN_WIDTH = window.innerWidth-10,
SCREEN_HEIGHT = window.innerHeight-10;
if (window.innerWidth === 0) { alert("wtf? width = 0?");}
var canvas = $('canvas')[0],
context;
init(); // use $(window).load(init) does not fix the problem
function init() {
if (canvas.getContext) {
context = canvas.getContext('2d');
animate(); // draw stuff
}
else {
alert('Your browser does not support html5 canvas');
}
}
});
As you can see here, I'm already using $(docuemnt).ready(function()) to make sure things gets loaded, however from a link http://4loc.wordpress.com/2009/04/28/documentready-vs-windowload/ the author suggest that the document ready event executes already when the HTML-Document is loaded and the DOM is ready, even if all the graphics haven’t loaded yet.
On the other hand the $(window).load(function()) executes a bit later when the complete page is fully loaded, including all frames, objects and images. Therefore functions which concern images or other page contents should be placed in the load event for the window or the content tag itself. But even I use this the problem is not solved.
Maybe the js file is asking for window's width before it's loaded, but I can't find a viable solution. Can anyone help with this?
Use an event listener that triggers on a window resize. That way you don't have to do any nonsense with trying to find when the window fully initializes. It will also make you less dependent on state which is a nice bonus.
window.addEventListener( 'resize', onWindowResize, false );
function onWindowResize() {
windowX = window.innerWidth;
windowY = window.innerHeight;
}
I had a similar problem with window.outerWidth which is returning 0 on some browsers when called from the ready() method, and I solved it by using screen.width instead.
I didn't test it but I think that screen.availWidth would solve your issue.