So I was making a rather large canvas that was filled with 1x1 pixels, and I wanted to make a loading screen for it. The problem after the loop filling the canvas finishes, it alerts that it has finished loading, but the canvas has not actually changed. I made a jsfiddle here to demonstrate. How do I actually find out when the canvas has actually loaded using javascript or Jquery, and what is causing this behavior?
var ctx=document.getElementById('canvas').getContext('2d');
for(var x=1;x<600;x++){
for(var y=1;y<600;y++){
var color= '#' + Math.floor (Math.random() * 16777215).toString(16);
ctx.fillStyle=color;
ctx.fillRect(x,y,1,1);
}
}
alert('done!');
Since you said jquery was ok, just fire a custom event when the loops are done looping.
Any code that needs the canvas fully loaded can go in the event handler.
// Listen for custom CanvasOnLoad event
$(document).on( "CanvasOnLoad", canvasOnLoadHandler );
var ctx=document.getElementById('canvas').getContext('2d');
for(var x=1;x<600;x++){
for(var y=1;y<600;y++){
var color= '#' + Math.floor (Math.random() * 16777215).toString(16);
ctx.fillStyle=color;
ctx.fillRect(x,y,1,1);
}
// fire CanvasOnLoad when the looping is done
if(x>=599){
$.event.trigger({
type: "CanvasOnLoad"
});
}
}
console.log("...follows the for loops.");
// handle CanvasOnLoad knowing your canvas has fully drawn
function canvasOnLoadHandler(){
console.log("canvas is loaded");
}
It is just like a load-progress animation. Updating/advancing that animation from a running function usually doesn't work right away (screen updates when that function finishes).
Your canvas-code is (automatically) wrapped in a onload function: window.onload=function(){ /*your code here*/ };.
The last line of that function is alert('done!');, so naturally you'll get the alertbox before the screen updates and you see the noize.
One solution is to first one set and display's a loading-image, then using a setTimeOut (say 30ms) one renders the canvas, ending that canvas-function with another setTimeOut to remove the loading-image again.
Note: as you probably know, your code will generate (a lot of) hex-colors like #3df5 and #5d8a6, both of which aren't valid colors! Also you used 16777215, but Math.random() needs to be multiplied by 16777216. To fix this, you might want to try:
color='#'+((Math.random()+1)*16777216|0).toString(16).substr(1);
Here is some good read about random colors.
See this JSFiddle result as an example.
Hope this helps.
Related
This is a question related to Basic Javascript loading message while js processing completes
My main problem is that cursor not is changed before my two functions drawlegend() and display() are called, but changes after everthing has finnished.
With the code as below where the restore of the cursor temporary commented out, I get the hourglass, but after everything has finnished.
How to get my cursor to change to an hourglass before my slow functions are called?
examplefunc()
{
mini.append("text")
.text(series[i].name)
.attr("x",30)
.attr("y",(15+(15*i)))
.attr("stroke",(d3.rgb(192,192,192)))
.attr("fill",(d3.rgb(192,192,192)))
.attr("stroke-width",0)
.style("font-size","12px")
.attr("text-anchor","start")
.attr("id","legend")
.on('mouseup', legendclick);
}
//===== legend clicked
function legendclick()
{
//--- get mouse pos
var origin = d3.mouse(this);
//--- get channel
var ch=Math.floor((origin[1]-4)/15);
//--- toggle active state
if (series[ch].active==true)
series[ch].active=false;
else
series[ch].active=true;
setTimeout(setcursor("wait"),5);
drawlegend();
display();
//setTimeout(setcursor("default"),5); // temp removed to see any result at all
}
//===== set cursor
function setcursor(cursor)
{
d3.select("body").style("cursor", cursor);
}
It is known that executing things in javascript, hangs your application. This means that only the eventual output is displayed on your screen. Thus, when you change the cursor to "wait" and after execution to "cursor", the javascript hasn't changed it, because the ui thread was busy calculating the things in the functions "drawlegend" and "display". However, I think when you execute the "drawlegend" and "display" asynchronous like
setTimeout(function () {
drawLegend();
display();
setcursor("default");
}, 0);
then things should go like you want to.
Let me know if this works for you.
Extra info: on this slideshare (especially slide 5) is explained what your problem is.
I am writing an internet site, using javascript, HTML for IE9.
I found solution to loading dynamically the image by:
document.getElementById("id_image").filters.item("DXImageTransform.Microsoft.AlphaImageLoader").src = document.getElementById("id_filepic").value
id_image related to the <IMG> and id_filepic related to <input id="id_filepic" type="file">
After the line:
document.getElementById("id_image").filters.item("DXImageTransform.Microsoft.AlphaImageLoader").src = document.getElementById("id_filepic").value
what is the exact event that occurs just after the image is shown on the html page (hence, the image has width + height), and how can I capture that event?
It is important to me knowing the solution for IE9.
It doesn't have to be after that line, since it will be asynchronous anyway, but here it is:
document.getElementById("id_image").filters.item("DXImageTransform.Microsoft.AlphaImageLoader").src = document.getElementById("id_filepic").value;
document.getElementById("id_image").addEventListener('load', (function(i) {
return function() {
console.log(i, 'loaded');
}, false);
})(i));
Source: Javascript Image onload event binding
I want to participate my solution, I have found.
Well, the above is not compilable, and also 'onload' is not the correct event (it is not fired after using the "filter" command, as far as I investigated).
What I did is a little delay, with timeout command (about one second) like this :
setTimeout(function () {
alert("w: " + $("#id_image").width());
alert("h: " + $("#id_image").height());
}, 1000);
(even 100 milliseconds is enough, but I check that out for very large images. 1 second is quite big not to fall by code on large images).
After the delay, I could retrieve the image width and height with no problem.
That's complete this issue.
The title of the question expresses what I think is the ultimate question behind my particular case.
My case:
Inside a click handler, I want to make an image visible (a 'loading' animation) right before a busy function starts. Then I want to make it invisible again after the function has completed.
Instead of what I expected I realize that the image never becomes visible. I guess that this is due to the browser waiting for the handler to end, before it can do any redrawing (I am sure there are good performance reasons for that).
The code (also in this fiddle: http://jsfiddle.net/JLmh4/2/)
html:
<img id="kitty" src="http://placekitten.com/50/50" style="display:none">
<div>click to see the cat </div>
js:
$(document).ready(function(){
$('#enlace').click(function(){
var kitty = $('#kitty');
kitty.css('display','block');
// see: http://unixpapa.com/js/sleep.html
function sleepStupidly(usec)
{
var endtime= new Date().getTime() + usec;
while (new Date().getTime() < endtime)
;
}
// simulates bussy proccess, calling some function...
sleepStupidly(4000);
// when this triggers the img style do refresh!
// but not before
alert('now you do see it');
kitty.css('display','none');
});
});
I have added the alert call right after the sleepStupidly function to show that in that moment of rest, the browser does redraw, but not before. I innocently expected it to redraw right after setting the 'display' to 'block';
For the record, I have also tried appending html tags, or swapping css classes, instead of the image showing and hiding in this code. Same result.
After all my research I think that what I would need is the ability to force the browser to redraw and stop every other thing until then.
Is it possible? Is it possible in a crossbrowser way? Some plugin I wasn't able to find maybe...?
I thought that maybe something like 'jquery css callback' (as in this question: In JQuery, Is it possible to get callback function after setting new css rule?) would do the trick ... but that doesn't exist.
I have also tried to separte the showing, function call and hiding in different handlers for the same event ... but nothing. Also adding a setTimeout to delay the execution of the function (as recommended here: Force DOM refresh in JavaScript).
Thanks and I hope it also helps others.
javier
EDIT (after setting my preferred answer):
Just to further explain why I selected the window.setTimeout strategy.
In my real use case I have realized that in order to give the browser time enough to redraw the page, I had to give it about 1000 milliseconds (much more than the 50 for the fiddle example). This I believe is due to a deeper DOM tree (in fact, unnecessarily deep).
The setTimeout let approach lets you do that.
Use JQuery show and hide callbacks (or other way to display something like fadeIn/fadeOut).
http://jsfiddle.net/JLmh4/3/
$(document).ready(function () {
$('#enlace').click(function () {
var kitty = $('#kitty');
// see: http://unixpapa.com/js/sleep.html
function sleepStupidly(usec) {
var endtime = new Date().getTime() + usec;
while (new Date().getTime() < endtime);
}
kitty.show(function () {
// simulates bussy proccess, calling some function...
sleepStupidly(4000);
// when this triggers the img style do refresh!
// but not before
alert('now you do see it');
kitty.hide();
});
});
});
Use window.setTimeout() with some short unnoticeable delay to run slow function:
$(document).ready(function() {
$('#enlace').click(function() {
showImage();
window.setTimeout(function() {
sleepStupidly(4000);
alert('now you do see it');
hideImage();
}, 50);
});
});
Live demo
To force redraw, you can use offsetHeight or getComputedStyle().
var foo = window.getComputedStyle(el, null);
or
var bar = el.offsetHeight;
"el" being a DOM element
I do not know if this works in your case (as I have not tested it), but when manipulating CSS with JavaScript/jQuery it is sometimes necessary to force redrawing of a specific element to make changes take effect.
This is done by simply requesting a CSS property.
In your case, I would try putting a kitty.position().left; before the function call prior to messing with setTimeout.
What worked for me is setting the following:
$(element).css('display','none');
After that you can do whatever you want, and eventually you want to do:
$(element).css('display','block');
I've a scenario that requires me to detect animation stop of a periodically animated element and trigger a function. I've no control over the element's animation. The animation can be dynamic so I can't use clever setTimeout.
Long Story
The simplified form of the problem is that I'm using a third party jQuery sliding banners plugin that uses some obfuscated JavaScript to slide banners in and out. I'm in need of figuring out a hook on slideComplete sort of event, but all I have is an element id. Take this jsfiddle as an example and imagine that the javascript has been obfuscated. I need to trigger a function when the red box reaches the extremes and stops.
I'm aware of the :animated pseudo selector but I think it will need me to constantly poll the required element. I've gone through this, this, and this, but no avail. I've checked jquery promise but I couldn't figure out to use that in this scenario. This SO question is closest to my requirements but it has no answers.
P.S. Some more information that might be helpful:
The element isn't created by JavaScript, it is present on page load.
I've control over when to apply the plugin (that makes it periodically sliding banner) on the element
Most of the slideshow plugins I have used use changing classes at the end of the animation... You could extend the "addClass" method of jQuery to allow you to capture the class change as long as the plugin you use is using that method like it should:
(function($){
$.each(["addClass","removeClass"],function(i,methodname){
var oldmethod = $.fn[methodname];
$.fn[methodname] = function(){
oldmethod.apply( this, arguments );
this.trigger(methodname+"change");
return this;
}
});
})(jQuery);
I threw together a fiddle here
Even with obfuscated code you should be able to use this method to check how they are sending in the arguments to animate (I use the "options" object when I send arguments to animate usually) and wrap their callback function in an anonymous function that triggers an event...
like this fiddle
Here is the relevant block of script:
(function($){
$.each(["animate"],function(i,methodname){
var oldmethod = $.fn[methodname];
$.fn[methodname] = function(){
var args=arguments;
that=this;
var oldcall=args[2];
args[2]=function(){
oldcall();
console.log("slideFinish");
}
oldmethod.apply( this, args );
return this;
}
});
})(jQuery);
Well since you didn't give any indication as to what kind of animation is being done, I'm going to assume that its a horizontal/vertical translation, although I think this could be applied to other effects as well. Because I don't know how the animation is being accomplished, a setInterval evaluation would be the only way I can guess at how to do this.
var prevPos = 0;
var isAnimating = setInterval(function(){
if($(YOUROBJECT).css('top') == prevPos){
//logic here
}
else{
prevPos = $(YOUROBJECT).css('top');
}
},500);
That will evaluate the vertical position of the object every .5 seconds, and if the current vertical position is equal to the one taken .5 seconds ago, it will assume that animation has stopped and you can execute some code.
edit --
just noticed your jsfiddle had a horizontal translation, so the code for your jsfiddle is here http://jsfiddle.net/wZbNA/3/
I want that when mouse is over an image, an event should be triggered ONCE, and it should be triggered again only after mouse is out of that image and back again, and also at least 2 seconds passed.
If I leave my mouse over the image,it gets called like every milisecond,and by the logic of my function once you hover on the variable 'canhover' becomes 0 until you move mouse out
This code seems to have a bug and I cant see it. I need a new pair of eyes, but the algorithm is kinda logical
Working code :
<script type="text/javascript">
var timeok = 1;
function redotimeok() {
timeok = 1;
}
//
function onmenter()
{
if (timeok == 1)
{
enter();
timeok = 0;
}
}
//
function onmleave()
{
setTimeout(redotimeok, 2000);
leave();
}
//
$('#cashrefresh').hover(onmenter,onmleave);
function enter(){
$("#showname").load('./includes/do_name.inc.php');
$("#cashrefresh").attr("src","images/reficonani.gif");
}
function leave(){
$("#cashrefresh").attr("src","images/reficon.png");
}
</script>
I don't know if this will solve your entire problem (since we don't have a detailed description of what it is), but instead of:
$('#cashrefresh').hover(onmenter(),onmleave());
try:
$('#cashrefresh').hover(onmenter,onmleave);
And the same thing here:
setTimeout(redotimeok, 2000); // just the function name
Also, I don't see where you ever set timeok to zero. Do you mean to set timeok = 0 in onmenter()?
There are two methods in jquery for your problem:
.mouseenter() and .mouseleave()
Check out the demos there.
EDIT:
I thought hover was for mouseover and mouseout, sorry for confusion.
I checked your code again. And it seems that you're changing the image when mouse gets over the image, which forces browser to load the new image and the old image disappears for a very little while till the new one appears and i think this must be triggering both handlers continuosly and you're getting this behaviour.
Try not to change the source of the image, comment out that line and instead console.log("some message") there and see if the message is repeated as much as .load() was fired before.
Hope this helps.
Try changing onmleave function as follows:
function onmleave()
{
setTimeout(redotimeok, 2000);
leave();
}