I am loading an image from the client side using createObjectURL. Sometimes, the image is big, so I wanted to put a "loading" animated gif in there.
I have a container div (fileDisplay) and an img (imgDisplay).
fileDisplay has the background set to the animated gif. So, as imgDisplay is loading a big file, you see the loading gif. When imgDisplay is done loading, it covers the gif. So, its still there, but you dont see. I figured that should be fine. All is well--as long as the img is a square, which distorts the img proportions.
But, if the image loaded into imgDisplay is resized with correct proportions and not square (fileDisplay dimensions are 45x45 pixles) you still see the gif in the background.
So what I need is to be able to turn off the animated gif (background url=none on fileDisplay), or cover it with another div, or something. But only when the image has fully loaded.
I can see by now, after trying everything I can think of, that there is no way to change backgrounds, turn divs on/off, etc and do this. It seems everything is processed and then the final results are all painted at once as opposed to incrementally as if it were, say, a VB app.
Can anyone help me with working out how to do this--I'm assuming it CAN be done. And I think the solution is "promise." I have looked for samples, read what I can find, but just can't seem to grasp it.
The "uploadButton on change" event fires this code:
imgSrc = window.URL.createObjectURL(this.files[0]);
//document.getElementById("imgDisplay" + justNumber).src = imgSrc;
getImgSize(imgSrc, useImgSize);
Where the commented line just displays the image as a 45x45. But the getImgSize function calculates the size and displays the image resized--which is when the problem occurs. Just try to load an image that is not square and you will see the issue.
A complete fiddle is here: https://jsfiddle.net/msith718/xfuv79b3/334/
So to do this you want to remove the gif/img element from the parent div when you load the new image into the div. Which you would do with the element.removeChild() function.
//Put this code as soon as you want to remove the loading gif
//This gets the node or the element you want to remove
var node = document.getElementById("gif");
//This removes the node from the parent div
document.getElementById("parentDiv").removeChild(node);
Here is one way of solving this problem https://jsfiddle.net/xfuv79b3/353/. Because you are wanting to change something after the image has loaded we need to add the code somewhere after the onload event has been called. I chose to pass in an id to my imgLoader that gets passed on to the callback that processes the image once loaded. Then finally use that id to modify the background of the div once the img element has loaded the new image. What you ne
getImgSize(imgSrc, useImgSize, justNumber);
//...
img.onload = function() {
fn({
width: img.width,
height: img.height
},id);
}
//...
document.getElementById("imgDisplay" + id).onload=function(){
document.getElementById('fileDisplay'+ id).style.background='none';
}
//Also to reset the background if upload is canceled
document.getElementById("fileDisplay" + justNumber).style.background = '';
Extra Note
From your reference to vb and painted I'm guessing your background is more geared towards a main loop that handles all of the interactions and holds all the data or state of your application. Javascript is very different from this in that user interactions happen through events which are asynchronous. This means data has to be explicitly passed around if need inside a certain callback.
Think about it like this. Your program starts in a resting state, but is listening for events that signal some change has happened. If an event happens (such as a user clicks a button) your program calls an event handler. This happens asynchronously meaning two things. One the only data available to it is the data passed into the handler and the data available from its parent scope. And two it means any subsequent actions or data processing have to be called from that event handler. So each event handler must perform all the actions needed and any state change updates to the program before returning to the resting state. Then your program waits for the next event.
This is very simplified (e.g. multiple events can be fired at the same time) and hopefully helpful, but once you get the hang of it javascript makes a whole lot more sense. I'd suggest reading up on event driven programming with javascript since I'm sure there are much better tutorials and explanations out there, but hopefully this gets you headed in the right direction.
Related
I have a difficult question to explain and I'm way out of my comfort zone as far as expertise in Javascript, TrueType, Angular and MxGraph are concerned... Hope to be able to explain.
I have an Angular component displaying and MxGraph. I was able to integrate MxGraph with Angular following this link (How to integrate mxGraph with Angular 4?). Even if I use Angular 7, the solution still works...
The graph is displayed correctly on the page and everything works fine, including my override of the function graphHandlerMouseUp, which I do with this code:
// Save the position of the mouse when releasing the button: used for
// detecting the target in a drag and drop
mx.graphHandlerMouseUp = mx.mxGraphHandler.prototype.mouseUp;
mx.mxGraphHandler.prototype.mouseUp = function( graph, evt ) {
currentdropX = evt.graphX;
currentdropY = evt.graphY;
mx.graphHandlerMouseUp.apply(this, arguments);
}
When I run this page for the first time, no problem happens.
Then through a button I call a page with another component (through routing). If from this page I go back to the first component (again through a routerlink) the page and the component with the MxGraph loads correctly, BUT when I use this function (i.e., release the mouse button).
It seems to me a recoursive problem, as when I put a console output like this:
// Save the position of the mouse when releasing the button: used for
// detecting the target in a drag and drop
mx.graphHandlerMouseUp = mx.mxGraphHandler.prototype.mouseUp;
mx.mxGraphHandler.prototype.mouseUp = function( graph, evt ) {
currentdropX = evt.graphX;
currentdropY = evt.graphY;
// INIFINTE LOOP HERE
console.log("Test");
mx.graphHandlerMouseUp.apply(this, arguments);
}
The "Test" is written a number of times which is continuously growing. Yet, if I understood, this was the correct way of overriding the function. Of course on the first load of the page, "Test" is displayed once. Passing to another component and then back on this it is displayed an "infinite" number of times (until I reach the: "ERROR RangeError: Maximum call stack size exceeded")...
I tried also to remove that override, and besides the obvious lack of the functionality, the very same problem happened to the function "mxDragSource", which is overridden with the same approach.
As I said: I'm not expert enough in javascript, truetype, MxGraph or Angular, so any hint, even if obvious, is very welcome!
Thanks!
The first time you run your code, you store mx library's mouseUp event in a variable and you assign a new function to mouseUp event in which you call the old mouseUpEvent.
The second time you run your code, you store your current mouseUp event (that you have modified) and you assign a function in which you call the old mouseUpEvent, which is the same you stored previously. There goes your recursion !
What you need to do is override the third-party function properly, so you don't execute your code twice.
How to do it ?
You can create a mixin and use this mixin in your componentB/ If you have no idea what is it and how to do it, please reproduce your problem in a stackblitz I'll be glad to help you to implement it.
I've got code aimed at visualizing some complex data. The user first selects a data file to load; after clicking the load button, the page generates a "plot" button with an onclick event handler with the function renderPlots().
When the user clicks the "plot" button, then all the plots are properly generated and I'm happy. However, in the case of a larger data set, the plotting process may take a few seconds so I'd like to change the button text to something like "rendering..." while the plots are being generated so that the user knows their plots are on the way.
As per the bootstrap documentation for dynamically changing button text, I've tried something like
function renderPlots() {
$("#render").button("loading");
rest of code that does all the D3 plot generating...;
$("#render").button("reset");
}
The problem with the above code is that it doesn't actually change the button text first, it's doing things asynchronously and I'm at a loss of how I can force the button text to change on screen before any plotting actually starts.
Any thoughts?
Actually, if I understood the issue, stuff is running synchronously — not asynchronously — which is why the browser doesn't get a chance to refresh the DOM with the new button text prior to starting the big processing task. If you were to console.log the button text somewhere between the two calls to .button() you'd see that the text is set to "loading"; it just doesn't appear yet.
If so, then using setTimeout to delay the start of processing until the next "frame" will fix the issue:
function renderPlots() {
$("#render").button("loading");
setTimeout(function() {
// rest of code that does all the D3 plot generating...;
$("#render").button("reset");
}, 100); // even 0 will do the trick
}
I've got an image and I want to fire a JS function - but it's dependant on the height of the image. Something like below:
if($(".displayArea").find("img").height() > 500){
//Important function comes in...
myFunction();
}
The problem is I have to use $(window).load() to get the height of the image and that takes a long time, I'd like to be able to fire myFunction() on $(document).ready() and not later.
Is there anyway to use JS/jQuery or PHP to figure out that the image will be more or less than 500px and fire the function accordingly?
In PHP you can get more information about an image file by using getimagesize. Be aware that performing this operation thousands of times can be costly, so you may wish to cache the results somehow.
I have a javascript function which creates an image dynamically, then shows that image either in a div or on a separate page.
Since the image creation takes a few seconds I'd like to display a simple 'please wait' message so that the user knows something is happening, and I am doing this by replacing the contents of an initially empty div with some text.
$('#chartImage').html('Please wait...');
This works fine on it's own - the div contents are displayed immediately when the triggering link is clicked. The problem is when I add the actual image creation code after it, the 'please wait' does not show up until after the image creation is complete, and only for a split second, so it's kinda pointless.
Is there something like a flush method to make sure all actions have taken effect before continuing?
Alternatively if there is a better way to do this I'm happy to hear it...
-------------EDIT-----------------
Here is a more complete description of what I'm doing, using open flash chart to make an image:
function createChartImage(chartid)
{
$('#chartImageDiv'+chartid).html('Please wait...');
ofc = findSWF("ofc_chart_"+chartid);
x = ofc.post_image( getUploadChartURL(chartid), 'doneChartUpload', false );
setTimeout("",1000); //allow some time for upload to complete
window.location.href = getViewChartImageURL(chartid);
}
The 'please wait' appears to take effect immediately before the final line. If I change the div's contents again at that point, the 'please wait' never shows at all.
(According to the docs for the charts I'm using, the function 'doneChartUpload' should be called when the upload is complete which would remove the need for the setTimeout, but I spent a long time trying to get it working, even copying code verbatim from the page below, to no avail.)
http://teethgrinder.co.uk/open-flash-chart-2/adv-upload-image.php
From assessing your code i would definitely split it up:
function generateChartImageProcess(chartid){
displayLoader();
createChart(chartid);
}
function createChart(chartID){
ofc = findSWF("ofc_chart_"+chartid);
x = ofc.post_image( getUploadChartURL(chartid), 'doneChartUpload', false );
setTimeout("",1000); //these two lines here to be replaced by your chart generations complete process
window.location.href = getViewChartImageURL(chartid);
}
function displayLoader(){
$('#chartImageDiv'+chartid).html('Please wait...');
}
function chartGeneratedComplete(arguments){
//TODO
}
This happens because the image isn't loaded yet, and it takes some time for the browser to load it before it can be displayed.
You could try using the jquery.load() method to add a callback for the event of the image being loaded like this, (supposing the div containing the image has the id #imageContainer):
$('#imageContainer img').load(function() {
// Remove the Please wait
});
BlockUI is a good tool to use for these situations: http://jquery.malsup.com/block/. In both cases (using BlockUI or rolling your own), trigger the "waiting" code first, run your image generator, and then remove the waiting effect after the image is loaded.
I'm changing the image src of an image node.
I want to be able to make sure that it's changed before executing somecode. How would i do that?
right now i have
function changePic(imgNode, newPic, desc){
var descNode = $("#description");
$(imgnode).fadeTo(500, 0, function(){
$(imgnode).attr("src", newPic);
$(imgnode).attr("alt", desc)
descNode.text(desc);
$(imgnode).fadeTo(500, 1);
});
}
Works great if the server's fast/ a local server. Works terribly if the server's slow, where the image will fade back in before changing...
any idea?
Edit: I'm loading the image when changePic is called. Any better ways to do it?
More: Also why is it not a good idea to put the last line,
$(imgnode).fadeTo(500, 1);
, outside of the callback function?
Preload the image, but to be sure it's completely loaded, use the .load() event.
Quote:
The load event is sent to an element
when it and all sub-elements have been
completely loaded. This event can be
sent to any element associated with a
URL: images, scripts, frames, iframes,
and the window object.
And don't miss this line:
It is possible that the load event
will not be triggered if the image is
loaded from the browser cache. To
account for this possibility, we can
use a special load event that fires
immediately if the image is ready.
event.special.load is currently
available as a plugin.
I put together an example of how I think you want it to work. I switch between three images I found through Google Images. I bind the load event before I change the src of the image to be sure it's triggered.
http://jsfiddle.net/xdjjR/1/
I guess, you can preload image in hidden elements, so that it's loaded with other html. When the source changed such image should be shown immediately.
Use the callback param
doc
ex from doc:
$('#clickme').click(function() {
$('#book').fadeOut('slow', function() {
// Animation complete.
});
});