How to preload an image and examine its properties before display - javascript

This is for a carousel side show. I'd like to place the fwd arrow at a fixed location, which would be the location of the widest picture plus an increment. So I'd like to find the width of the widest picture in the array before displaying any of them.
This does not work. What should I do instead? I'm not using JQuery or Angular. Barebones.
I see a previous answer to a similar question, but that will not work for me because it assumes that the number of images is fixed. In my case, they will vary from site to site.
To be included in js.config for every web site:
var pictures = ["CodeTalker.jpg", "challenger.jpg", "Hotline.jpg", "EarliestDigraphic.jpg", "Bombe.jpg", "SIGABA.jpg", "SIGCALYSign.jpg", "Cray.jpg"];
var maxWidth = 0;
var image = new Image()
for (picture in pictures) {
image.src = picture;
image.loading = "eager";
if (image.naturalWidth > maxWidth) {
maxWidth = image.naturalWidth;
}
}
var currentPic = 0;
When executed, the natural length always comes back zero.

Hi #Rin,
The issue with your code is the image.naturalWidth property is always returning 0. As Mr.#Dave Newton described in the above comment, The image object is not properly being loaded before its properties are being accessed.
for (var i = 0; i < pictures.length; i++) {
var image = new Image();
image.src = pictures[i];
image.onload = function() {
if (this.naturalWidth > maxWidth) {
maxWidth = this.naturalWidth;
}
}
}
onload event handler function when image is fully loaded. To ensure that the image is fully loaded before its properties are accessed.

Related

Restart a for loop after the end of an array

I working in JavaScript and am trying to add some images to a site.
I have a list of blog posts on a page. And I have a bunch of small stand-alone images (think small icon images) that I want to place on either side of the blog post feed. They are kind of just like random background images. As the feed gets longer, more images will automatically be placed.
Here is some example code of what I have so far. Right now I am just appending text to the page to get this working.
I need help figuring out how to restart this loop once the end of the array is reached. For example, after 6url.jpg is printed, I want to print 1url.jpg and so on if my imageCount is more than 6. I played around with continue and while loops but not sure how to implement that.
var blogIcons = ["1url.jpg", "2url.jpg", "3url.jpg", "4url.jpg", "5url.jpg", "6url.jpg"];
var blogFeedHeight = $(".blog-feed").height();
var imageDistance = 400;
// Determining how many images to place based on the blog feed height;
var imageCount = Math.ceil(blogFeedHeight/imageDistance);
// the number of images that should be inserted.
for(var i = 0; i < imageCount; i++){
$('blog-feed').append('<div class="blog-icon">' + blogIcons[i] +'</div>')
}
What you are looking for is called modulo, the rest of the euclidian division of 2 numbers.
var blogIcons = ["1url.jpg", "2url.jpg", "3url.jpg", "4url.jpg", "5url.jpg", "6url.jpg"];
var imageCount = 10;
// the number of images that should be inserted.
for (var i = 0; i < imageCount; i++) {
console.log(blogIcons[i % blogIcons.length])
}
I simplified your problem so it can run on StackOverflow. but you'll get the proper index by using "%"
So your script should look like that:
var blogIcons = ["1url.jpg", "2url.jpg", "3url.jpg", "4url.jpg", "5url.jpg", "6url.jpg"];
var blogFeedHeight = $(".blog-feed").height();
var imageDistance = 400;
// Determining how many images to place based on the blog feed height;
var imageCount = Math.ceil(blogFeedHeight/imageDistance);
// the number of images that should be inserted.
for(var i = 0; i < imageCount; i++){
$('blog-feed').append('<div class="blog-icon">' + blogIcons[i % blogIcons.length] +'</div>')
}
You don't need to restart a loop. Instead you can use modulo division to fetch an item within bounds from the array
var blogIcons = ["1url.jpg", "2url.jpg", "3url.jpg", "4url.jpg", "5url.jpg", "6url.jpg"];
for (var i = 0; i < 14; i++) {
console.log(blogIcons[i % blogIcons.length]);
}
When doing i % blogIcons.length you will get a number between 0 and blogIcons.length

javascript loaded img still has 0 height

This problem is annoying me because it works with one set of images and not with another.
I have a this object:
function PreLoader(toLoad, parent, images) {
var errored = 0;
var loaded = 0;
var toLoad = toLoad;
function allLoaded() {
// reset the counters so it can be used again
loaded = 0;
errored = 0;
// determine which img is the tallest
var l = 0;
for (var i = 0; i < images.length; i++) {
l = (l > images[i].height()) ? l : images[i].height();
}
// set the height of the container to the tallest
// unless it's already bigger
// height() is from jQuery
if (parent.obj.height() < l)
parent.obj.css("height", l);
}
this.load = function() {
++loaded;
if (loaded + errored == toLoad)
allLoaded();
};
this.error = function() {
++errored;
if (loaded + errored == toLoad)
allLoaded();
};
}
I have been using it in a similar way to this:
var parent = {obj: $("#some-img-container")};
var slabLoader = new PreLoader(2, parent, [external.slab, internal.slab]);
external.slab.src = "2.png";
external.slab.onload = slabLoader.load;
external.slab.onerror = slabLoader.error;
internal.slab.src = "1.png";
internal.slab.onload = slabLoader.load;
internal.slab.onerror = slabLoader.error;
The problem is, sometimes it doesn't work. I have multiple sets of images that are absolute positioned because they are layered on top of each other, but they can be different heights. Obviously I can't hard code the heights because I don't know what they are before the page loads... and because absolute positioned elements don't affect their parents size I can't rely on things like height: 100%.
var l in the allLoaded() function sometimes returns 0 even though the images should be loaded by the time that is called.
Am I correct in this statement or am I doing something wrong and it only works sometimes because of luck?
The html looks like this:
<div class='wrapper-hover'>
<div class='wrapper-content'>
<a herf='#' id='some-img-container' class='viewport'>
<!-- this is where the image goes, added by the script -->
</a>
<div class='wrapper-caption'>
<label class='wrapper-caption-content'>Some Image</label>
</div>
</div>
</div>
Sorry if the question is a bit difficult to understand.
UPDATE:
If I use .naturalHeight instead of jQuery .height()I get the height of the actual image on disk and not the height that it would take up after it has been scaled by css (I'm using width: 100% and leaving the height undefined). However it still does nothing for the images that returned 0 before.
I can confirm that all the images claim that .complete is true before I try to access their heights.
The working version of the resize function is as follows:
function resize( parent, images ) {
// The largest height seen so far.
var l = 0;
// Loop through all the images to
// determine which is the tallest.
for ( var i = 0; i < images.length; ++i ) {
// Create a variable to hold the current value.
// This just means that we can save time on multiple
// array access calls.
var c = images[i];
// If the current image is actually
// visible to the user.
if ( c.offsetParent !== null )
// If the largest so far is the biggest then
// keep it, otherwise change it to the new
// biggest value.
l = ( l > c.height ) ? l : c.height;
}
// If the value of 'l' is 0 then it is likely
// that the all the images are hidden. In
// which case skip the resizing.
if ( l === 0 )
return;
// Set the height of the parent to 'l'
// this ensures that the parent will resize
// even if the window is made smaller.
// adding 2 pixels for good luck :P
parent.setAttribute( "style", "height:" + (l + 2) + "px" );
}

SharePoint 2013 App Part - Not using iFrame or flowing elements outside of iFrame

I am currently developing an App Part that is attached to an Associate Directory application.
As one of the methods of finding someone, I have implemented a custom implementation of the jQuery UI Autocomplete widget (http://jqueryui.com/autocomplete/).
My problem is that according to the design for our homepage, where this app part is going to be placed, this isn't very tall.
So my issue is that when the dropdown comes down, for the autocomplete, a portion of it gets cut off. (See image below)
I know I do have some options with the styling to make it smaller and show more, but either way I would like to see if there are any more options for me.
My first thought was that I could make this into a Web Part, but then I would have to duplicate a lot of code from the application.
Please let me know if there are any ideas!
Thank you, Eric
Set the height in the app part elements.xml to that of the dropdown. When its focused you run a script that resizes your app part window to the height of your content.
My app part does an async call that renders an array as rows in the app part. On the last row i run below code.
var senderId;
var params = document.URL.split("?")[1].split("&");
for (var i = 0; i < params.length; i = i + 1) {
var param = params[i].split("=");
if (param[0].toLowerCase() == "senderid")
senderId = decodeURIComponent(param[1]);
}
var step = 77,
newHeight = 0,
arrayLength = scope.items.length,
resizeMessage = '<message senderId={Sender_ID}>resize({Width}, {Height})</message>';
for (var x = 0; x < arrayLength; x = x + 1) {
newHeight = newHeight + step;
}
if (senderId) {
resizeMessage = resizeMessage.replace("{Sender_ID}", senderId);
resizeMessage = resizeMessage.replace("{Height}", newHeight);
resizeMessage = resizeMessage.replace("{Width}", "100%");
window.parent.postMessage(resizeMessage, "*");
}
A couple of other examples:
Example 1
Example 2

Loading images in js for loop loads nonsequentially

Currently I have 3 or 4 galleries loaded on one page. It's a photographers site, so each gallery has about 40 images. I originally pre-loaded the images, where I would just load the first two images so the page loads quickly, then use JS in the background to cycle through a loop and load the rest. It worked well, except that it didn't honor the order, and the order is important. I have seen some examples where you hide the images until they load, but since there are about 120 large images being loaded in the page it causes the page to load slowly. Is there a way I can use my code, and sort the images once they're loaded? Or, should I load them through an array, would it still ignore the load order? Here's my jsfiddle: http://jsfiddle.net/drrobotnik/YKPEu/
The piece of js i am looking at mostly is this part:
var stack = [];
for (var i = 3; i <= 59; i++) {
var img = new Image(782,521);
img.src = 'http://anikalondon.com/Images/weddings/weddings-3-' + i + '.jpg';
$(img).bind('load', function() {
stack.push(this);
var len = stack.length+2;
$(".numbers").text(($(".enggal img:visible").index()+1)+"/"+len);
if(i>=58){$(".numbers").css("visibility", "visible");}
});
}
again, in this example it's loading the images out of order (probably whichever ones load first, not sure).
well idk if it helps but here's a sorting algorithm that you could use to order them before inserting them into the DOM
stack.sort(function(a,b){
return a.src.substring(a.src.lastIndexOf("-")+1,a.src.lastIndexOf(".")) - b.src.substring(b.src.lastIndexOf("-")+1,b.src.lastIndexOf("."))
})
What if you replaced you simplify things and use this for loop
for (var i = 3; i <= 59; i++)
{
$(".enggal").append("<img src='http://anikalondon.com/Images/weddings/weddings-3-" + i + ".jpg' />");
}
This inserts all of your images in order. You may want to add in the width/height attributes.
Without knowing the deal with how stack is used, I'm not totally sure this would work, but why don't you just save the index information to a store right away, rather than on the callback when it's done loading?
And you should also set src after you bind load or it might miss.
[totally revised from original answer]
var img,i,stack = [];
var imageLoaded=function(e) {
var len = stack.length+2,
index = parseInt(this.src.split('-')[2])-3
$(".numbers").text(($(".enggal img:visible").index()+1)+"/"+len);
if(index>=58){$(".numbers").css("visibility", "visible");}
}
for (i = 3; i <= 59; i++) {
img = new Image(782,521);
$(img).bind('load', imageLoaded);
stack.push(img);
}
// Just to make sure that the first image doesn't finish loading before the loop isdone
// throwing off the count in the imageLoaded function
for (i = 3; i <= 59; i++) {
stack[i-3].src = 'http://anikalondon.com/Images/weddings/weddings-3-' + i + '.jpg';
}

Is it possible to use multiple images in an HTML5 canvas?

I'm trying to load 10 different images into a canvas. My plan is to eventually animate these images but right now they seem to be overwriting one another. Here is my code:
var DrawLetters = function()
{
for (i = 0; i < howManyLetters; i++)
{
thisWidth = letters[i][0];
thisHeight = letters[i][1];
imgSrc = letters[i][2];
letterImg = new Image();
letterImg.onload = function()
{
context.drawImage(letterImg,thisWidth,thisHeight);
}
letterImg.src = imgSrc;
}
};
letters is an array with 10 elements where each element contains a path to the image. Any help on this would be greatly appreciated.
I've tried your code and the onload method always use the LAST value of the vars, not the value when the array was iterated.
Try setting the X and the Y to properties of the image object:
// I assume you are storing the coordinates where the letters must be
letterImg.setAtX = letter[i][XPOS];
letterImg.setAtY = letter[i][YPOS];
and on the onload:
context.drawImage(this, this.setAtX, this.setAtY);
this is the image raising the onload event.
Edit I've changed the properties used to carry the coordinates. Now they're setAtX/Y. You cannot use x and y because they're reserved.
You're drawing them on the same point. drawImage doesn't care about the height or width with your given parameters; it just wants an image and a coordinate. See https://developer.mozilla.org/en/Canvas_tutorial/Using_images
So, you're gonna need to give your images more data; something like:
thisWidth = letters[i][0];
thisHeight = letters[i][1];
imgSrc = letters[i][2];
thisX = letters[i][3]; //<---
thisY = letters[i][4]; //<---
letterImg = new Image();
letterImg.onload = function()
{
context.drawImage(letterImg, thisX, thisY, thisWidth, thisHeight);
//or, just
context.drawImage(letterImg, thisX, thisY);
}
letterImg.src = imgSrc;
Edit: Just had a thought - you can do it dymagically:
context.drawImage(letterImg, letters[i-1][0]+thisWidth, letters[i-1]+thisHeight, thisWidth, thisHeight);
With this way you'll have to check for stuff, but I think you get the overall intention.
You have to reposition the draw start position every time so the images arent overwritten.
[context . drawImage(image, dx, dy, dw, dh)][1]
There's an image on the link explaning what every parameter means.

Categories

Resources