Going back to the previous thumbnail overview results in flickering. Looking at it in slow motion the build up of the thumbnail page becomes visible. If the images are loaded fast, the problem disappears, but is very visible if the connection is slow. Is there a way keep the thumbnail page in cache or render the page after flexbox places all the elements? Or is there any other solution? Thanks!
Slow motion:
https://www.youtube.com/watch?v=x3vvCRIJdRo
All images are small sprites that are then drawn to canvases btw. I'm also using flexbox.
Website: http://eboydb.mod.bz/pool/all/1 (will remove this link – if fixed)
Edit:
Just tried to use waitOn with Iron Router – as suggested below. It doesn't seem to make a difference though.
Router.route('/pool/:slug/:page', {
name: 'pool',
template: 'pool',
after: function() {
document.title = siteName + ' /pool/' + this.params.slug + '/' + this.params.page;
},
waitOn: function() {
// set search slugs that will show everything
var allOptions = [
"all",
"everything",
"show all",
];
// when the slug is part of the array show everything
if (allOptions.indexOf(this.params.slug.toLowerCase()) > -1) {
var searchTerm = ".*";
// console.log('slug: all > ' + this.params.slug);
} else {
var searchTerm = this.params.slug;
}
Meteor.subscribe('PixQuery', searchTerm, this.params.page);
},
data: function() {
templateData = {
slug: this.params.slug,
}
return templateData;
}
});
Related
Been bashing my head over this for a while. The project I'm working on requires that I display two randomly selected videos from a list of four possible, unique animations, and on the next page, display the correct corresponding textual description for the video selected. Right now, the approach I'm taking is creating a separate list.js file apart from my code, with each video grouped with its corresponding sentence, but as my code is right now, I cannot use the randomization function to randomize all objects at once.
the list.js file.
//two randomly selected animations are shown here as samples
var stims = jsPsych.randomization.factorial(list.js)
for (const i of stims) {
var video = "video/" + i[video] + ".mp4"
var description = {
type: "html-keyboard-response",
stimulus: [
i["sentence"] +
"<p> Press spacebar to proceed to the next sample animation </p>"
],
choices: [' ']
}
var experience = {
timeline:
[
{
type: 'video',
width: 600,
data: {
subject: code,
},
sources: [video]
}
]
}
timeline.push(fixation);
timeline.push(experience);
timeline.push(description);
}
above is the pertinent code in my experiment.
I currently have a giant table of "auditpoints", some of those points are "automated". If they are automated they receive a gear icon in their row. The gear icon is not the only icon each row receives. Each row, no matter if it's automated or not receives two other icons, a pencil and a toggle button. When an automated point "runs" the gear icon rotates until it is finished "running". I've have implemented some code to ran all of these points at once but I have a small problem. When you click my button to run all these points all three of the icons I have mentioned rotate and this is not the result I am looking for. The line commented out in my code snippet (and it's matching bracket) will prevent the code from running all of the automated points. Commenting out the line is what causes all the icons to rotate. I know this line is required to get the automated points to run properly as it used in the single execution of automated points I just don't know what to change it to. It obviously shouldn't be click because you are no longer clicking the gear icon to get a point to run I just don't know what to change it to but the classes in that click function are related to the gear icon.
Hopefully this is a very easy question to solve and doesn't waste anyone's time. Thank you!
private updateAuto() {
var self = this;
$(".auditPointRow").each(function () {
//self.el.on("click", ".update, .edit", function () {
var row = $(this).closest(".auditPointRow");
var id = row.data("id");
var automated = (<string>row.data("automated")).toLowerCase() == "true";
var running = true;
if (automated && $(this).closest(".edit").length == 0) {
var gear = $(this).find(".fa");
var maxTurns = 120;
gear.css("transition", "transform linear " + maxTurns * 2 + "s");
gear.css("transform", "rotate(" + (maxTurns * 360) + "deg)");
var request = $.ajax(self.root + "api/sites/" + self.site.ID + "/auditpoints/" + id, {
"type": "PATCH", data: JSON.stringify([
{
Op: "Replace"
, Path: "/score"
, Value: "run"
}
])
});
request.done(function () {
gear.css("transition", "").css("transform", "rotate(0deg)");
row.prev().find("td").css("background-color", "");
if (row.prev().qtip("api")) {
row.prev().qtip("api").destroy(true);
}
});
}
//}
});
}
I think I found a solution to my problem. I used .each again to go through all of the "gears" and only rotate them.
private updateAuto() {
var self = this;
//$(".auditPointRow").each(function () {
$(".update, .edit").each(function () {
//Left out the rest of the code so this answer isn't too
//long, none of it changed if that matters.
});
//});
}
For some reason the result runs very slowly (but it runs!) and I'm not sure why so if anyone has any better suggestion/optimizations please feel free to leave those here.
Edit: I realized I didn't to go through .each twice, that's what was slowing to down so I removed that first each that went over auditPoints and just did the ones with gears instead.
My goal is to call an API, which returns JSON data. On successfully retrieving the data, I would like to put the retrieved image into a gallery, organized with the Masonry library. This should work for initially loading data, and endless scrolling to load more data when the user scrolls down the page.
The code below works well for grabbing and organizing the first set of data. It loads nine images and uses Masonry to organize them. But, when I scroll down the page to load more images, all of the current images get pushed down the page. When I scroll down again the same thing happens. So I end up with a bunch of white space at the top of the page.
I was attempting to roughly follow the example that I found here;
http://codepen.io/desandro/pen/iHevA
Thanks for any help you can provide.
function loadThumbnailImages(thumbDate) {
// Format the date into YYYY-mm-dd format for the API
formattedDate = this.formatDate(thumbDate);
$.jsonp({
// The URL to parse the JSON from
url: 'dataURLGoesHere' + formattedDate, // any JSON endpoint
// if URL above supports CORS (optional)
corsSupport: false,
// if URL above supports JSONP (optional)
jsonpSupport: false,
// If the JSON from the URL was successfully parsed
success: function(data){
$(document).ready(function() {
if(data.media_type == "image")
{
var item = '<div class="masonry-item"><img src=' + data.url + ' class="thumbImage img-responsive"/></div>';
items += item;
if(doneLoading)
{
var $container = $('#masonry-list').masonry({
itemSelector: '.masonry-item',
columnWidth: 50
})
$container.append(items);
$container.imagesLoaded().progress( function ( imgLoad, image) {
var $item = $(image.img).parents('.masonry-item');
$container.masonry('appended', $item);
});
items = "";
}
}
})
}
I managed to figure it out. Adding the two lines $container.masonry('reloadItems') and $container.masonry('layout') manages to load the images and responsively place them in the correct place. Hopefully this can help someone in the future!
var $container = $('#masonry-list').masonry({
itemSelector: '.masonry-item',
columnWidth: 50
})
$container.append(items);
$container.imagesLoaded().progress( function ( imgLoad, image) {
var $item = $(image.img).parents('.masonry-item');
$container.masonry('appended', $item);
$container.masonry('reloadItems'); //<--- This line and
$container.masonry('layout'); //<-------- This line fixed the problem
});
I recently downloaded a nice mootools plugin to provide a rating system for search results on my site: MooStarRating.
It works quite well, but it is very slow to initialize. Here are the execution times I have logged (for pulling back 50 search results).
======== starrating ========
init: 0.06ms 50
img: 5.40ms 50
str: 0.54ms 50
each: 3.04ms 50
inject: 0.86ms 50
end: 1.52ms 50
subtotal: 11.42ms 50
-----------------
total: 571.00ms
Here is the initialize function these logs refer to (just for reference):
initialize: function (options) {
lstart("starrating");
// Setup options
this.setOptions(options);
// Fix image folder
if ((this.options.imageFolder.length != 0) && (this.options.imageFolder.substr(-1) != "/"))
this.options.imageFolder += "/";
// Hover image as full if none specified
if (this.options.imageHover == null) this.options.imageHover = this.options.imageFull;
lrec("init");
// Preload images
try {
Asset.images([
this.options.imageEmpty,
this.options.imageFull,
this.options.imageHover
]);
} catch (e) { };
lrec("img");
// Build radio selector
var formQuery = this.options.form;
this.options.form = $(formQuery);
if (!this.options.form) this.options.form = $$('form[name=' + formQuery + "]")[0];
if (this.options.form) {
var uniqueId = 'star_' + String.uniqueID();
this.options.form.addClass(uniqueId);
this.options.selector += 'form.' + uniqueId + ' ';
}
this.options.selector += 'input[type=radio][name=' + this.options.radios + "]";
// Loop elements
var i = 0;
var me = this;
var lastElement = null;
var count = $$(this.options.selector).length;
var width = this.options.width.toInt();
var widthOdd = width;
var height = this.options.height.toInt();
if (this.options.half) {
width = (width / 2).toInt();
widthOdd = widthOdd - width;
}
lrec("str");
$$(this.options.selector).each(function (item) {
// Add item to radio list
this.radios[i] = item;
if (item.get('checked')) this.currentIndex = i;
// If disabled, whole star rating control is disabled
if (item.get('disabled')) this.options.disabled = true;
// Hide and replace
item.setStyle('display', 'none');
this.stars[i] = new Element('a').addClass(this.options.linksClass);
this.stars[i].store('ratingIndex', i);
this.stars[i].setStyles({
'background-image': 'url("' + this.options.imageEmpty + '")',
'background-repeat': 'no-repeat',
'display': 'inline-block',
'width': ((this.options.half && (i % 2)) ? widthOdd : width),
'height': height
});
if (this.options.half)
this.stars[i].setStyle('background-position', ((i % 2) ? '-' + width + 'px 0' : '0 0'));
this.stars[i].addEvents({
'mouseenter': function () { me.starEnter(this.retrieve('ratingIndex')); },
'mouseleave': function () { me.starLeave(); }
});
// Tip
if (this.options.tip) {
var title = this.options.tip;
title = title.replace('[VALUE]', item.get('value'));
title = title.replace('[COUNT]', count);
if (this.options.tipTarget) this.stars[i].store('ratingTip', title);
else this.stars[i].setProperty('title', title);
}
// Click event
var that = this;
this.stars[i].addEvent('click', function () {
if (!that.options.disabled) {
me.setCurrentIndex(this.retrieve('ratingIndex'));
me.fireEvent('click', me.getValue());
}
});
// Go on
lastElement = item;
i++;
}, this);
lrec("each");
// Inject items
$$(this.stars).each(function (star, index) {
star.inject(lastElement, 'after');
lastElement = star;
}, this);
lrec("inject");
// Enable / disable
if (this.options.disabled) this.disable(); else this.enable();
// Fill stars
this.fillStars();
lrec("end");
return this;
},
So, the slowest part of the function is this:
// Preload images
try {
Asset.images([
this.options.imageEmpty,
this.options.imageFull,
this.options.imageHover
]);
} catch (e) { };
Which is strange. What does Asset.images do? Does the script block until these images have been loaded by the browser? Is there a way to preload images that runs faster?
How can I make the scripts on my page execute faster? It is a big problem for them to take 800ms to execute, but 200ms is still quite bad. At the moment, my search results all pop into existence at once. Is it possible to make it so that individual search results are created separately, so that they don't block the browser while being created? Similarly, is it possible to do this for the individual components of the search results, such as the MooStarRating plugin?
no. Asset.images is non-blocking as each of these gets loaded separately and a singular event is being dispatched when all done.
the speed for loading will be dependent on the browser but it be will multi-threaded to whatever capability it has for parallel downloading from the same host.
https://github.com/mootools/mootools-more/blob/master/Source/Utilities/Assets.js#L115-129
immediately, it returns an Element collection with the PROMISE of elements that may still be downloading. that's fine - you can use it to inject els, attach events, classes etc - you just cannot read image properties yet like width, height.
each individual image has it's own onload that fires an onProgress and when all done, an onComplete for the lot - i would advise you to enable that, remove the try/catch block and see which image is creating a delay. you certainly don't need to wait for anything from Asset.images to come back.
you also seem to be using it as a 'prime the cache' method, more than anything - as you are NOT really saving a reference into the class instance. your 'each' iteration can probably be optimised so it uses half the time if objects and functions are cached and so are references. probably more if you can use event delegation.
To answer your questions about not freezing the browser due to the single-threaded nature of javascript, you defer the code via setTimeout (or Function.delay in mootools) with a timer set to 0 or a 10ms due to browser interpretations. You also write the function to to exec a callback when done, in which you can pass the function result, if any (think ajax!).
I'm running a prototype script (lightbox) and I want to add an extra link below each image with the url to a page that changes with each image.
I have so far managed to add the a element to the right place, but later in the script I need to update the href of the link based on the image that is loaded.
There's a point in the script where the image is inserted into the lightbox after loading/navigating the lightbox:
showImage: function(){
this.loading.hide();
new Effect.Appear(this.lightboxImage, {
duration: this.resizeDuration,
queue: 'end',
afterFinish: (function(){ this.updateDetails(); }).bind(this)
});
this.preloadNeighborImages();
},
After it's finished it runs updateDetails, which includes the images' caption. This seemed to me the perfect place to also update the link:
updateDetails: function() {
// if caption is not null
if (this.imageArray[this.activeImage][1] != ""){
this.caption.update(this.imageArray[this.activeImage][1]).show();
}
// if image is part of set display 'Image x of x'
if (this.imageArray.length > 1){
this.numberDisplay.update( LightboxOptions.labelImage + ' ' + (this.activeImage + 1) + ' ' + LightboxOptions.labelOf + ' ' + this.imageArray.length).show();
}
/** slider effect cut from example code for the sake of brevity **/
}
I've tried many variations based on the codes used in these functions, but nothing seems to work. How do I update the href of a link with id=toFilePage?
I figured it'd be something like
this.toFilePage.href.update('http://example.com');
But that doesnt seem to work..
I believe the problem may lie within this section (starting at line 175 in lightbox.js)
(function() {
var ids = 'overlay lightbox outerImageContainer imageContainer lightboxImage hoverNav prevLink nextLink loading loadingLink ' + 'imageDataContainer imageData imageDetails caption numberDisplay bottomNav bottomNavClose';
$w(ids).each(function(id) {
th[id] = $(id);
});
}).defer();
This creates references to the objects created in the Builder section above it. Simply adding your id to that list should do the trick.
Otherwise you may also access it through $("myIdHere").