trigger function when done airport plugin text effect - javascript

I'm using a plugin which rotates letters similar to airport notice boards.
Here is the script written by http://unwrongest.com/projects/airport/
I would like to know how to trigger a function once the rotation of the letter has ended so that I can change the css background property of my background div before the next array starts rotating.
Something like so: background1 -> rotate "7 days" -> done rotation -> background2 -> rotate "14 days" -> done rotation -> background3 -> rotate "22 days" etc...
HTML
$('.dur').airport(['7 days','14 days','22 days','31 days','3 weeks','2 months']);
JS Script
(function($){
$.fn.extend({
airport: function(array) {
var self = $(this);
var chars = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g',' ','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','-','1','2','3','4','5','6','7','8','9','0'];
var longest = 0;
var items = items2 = array.length;
function pad(a,b) { return a + new Array(b - a.length + 1).join(' '); }
$(this).empty();
while(items--)
if(array[items].length > longest) longest = array[items].length;
while(items2--)
array[items2] = pad(array[items2],longest);
spans = longest;
while(spans--)
$(this).prepend("<span class='c" + spans + "'></span>");
function testChar(a,b,c,d){
if(c >= array.length)
setTimeout(function() { testChar(0,0,0,0); }, 0);
else if(d >= longest)
setTimeout(function() { testChar(0,0,c+1,0); }, 10000);
else {
$(self).find('.c'+a).html((chars[b]==" ")?" ":chars[b]);
setTimeout(function() {
if(b > chars.length){
testChar(a+1,0,c,d+1);
}
else if(chars[b] != array[c].substring(d,d+1)){
testChar(a,b+1,c,d);
}
else{
testChar(a+1,0,c,d+1);
}
}, 1);
}
}
testChar(0,0,0,0);
}
});
})(jQuery);

The following conditional
else if(d >= longest)
setTimeout(function() { testChar(0,0,c+1,0); }, 10000);
is where the plugin prepares the next spin after completing one - so you could apply a class/call a function there.
Add brackets and include the logic to apply a class, like this:
else if(d >= longest) {
$('.dur').attr('class', 'dur ' + (colors[c]));
setTimeout(function() { testChar(0,0,c+1,0); }, 1000);
}
Here's a codepen example: http://codepen.io/anon/pen/gbqqKX

Related

How to avoid "jumpy" UI updates?

I want to run simulations and update a progress bar, but the progress bar gets updated in a "jumpy" way. Eg. it moves from 21% to 34% to 76%. I'd like to to move one-by-one from 21% to 22% to 23% etc.
Consider this as a SSCCE:
http://embed.plnkr.co/XIKxpV6oWyWiwklFBSLh/
HTML
<button>Simulate</button>
JS
$(document).ready(function () {
var $button = $('button');
var worker = new Worker("worker.js");
worker.addEventListener("message", function(e) {
if (e.data) {
switch (e.data.cmd) {
case "progress":
$button.text((e.data.value / 250).toFixed(0) + "%");
break;
case "complete":
$button.text("Simulate");
break;
}
}
});
$button.on("click", function() {
worker.postMessage("start");
});
});
Worker
this.addEventListener("message", function(e) {
if (e.data === "start") {
for (var n = 0; n < 250000000; ++n) {
if (n % 10000 === 1) {
this.postMessage({cmd: "progress", value: n / 10000});
}
}
this.postMessage({cmd: "complete"});
}
});
How can I get it to update smoothly? Why isn't it updating smoothly to begin with?
You could change progress to provide fill values.
The reason why it's not smooth is that it doesn't post a progress value for all values, only every 10000 (if you try to post every value it's very likely your browser is going to freeze)
if (n % 10000 === 1) {
this.postMessage({cmd: "progress", value: n / 10000});
}
So you don't get all possible % values, you need to fill.
Something like this
$(document).ready(function () {
var $button = $('button');
var last = 0
var worker = new Worker("worker.js");
worker.addEventListener("message", function process(e) {
if (e.data) {
switch (e.data.cmd) {
case "progress":
var next = (e.data.value / 250)
var current = last
// fill values
// from last % to new %
for (; current < next; ++current) {
$button.text(current + "%")
}
last = current
$button.text(next.toFixed(0) + "%");
break;
case "complete":
$button.text("Simulate");
break;
}
}
});
$button.on("click", function() {
worker.postMessage("start");
});
});

Starting timer when clicking first card of memory game

Ok I am trying to wrap up a project and the only thing holding me back is it that they call for the timer to start on clicking a match game. The timer starts when the HTML file loads which is not what the project wants and I have tried some methods but ends up freezing the game. I want the timer to be able to start when clicking a card.
var open = [];
var matched = 0;
var moveCounter = 0;
var numStars = 3;
var timer = {
seconds: 0,
minutes: 0,
clearTime: -1
};
//Start timer
var startTimer = function () {
if (timer.seconds === 59) {
timer.minutes++;
timer.seconds = 0;
} else {
timer.seconds++;
};
// Ensure that single digit seconds are preceded with a 0
var formattedSec = "0";
if (timer.seconds < 10) {
formattedSec += timer.seconds
} else {
formattedSec = String(timer.seconds);
}
var time = String(timer.minutes) + ":" + formattedSec;
$(".timer").text(time);
};
This is the code for clicking on a card. I have tried to include a startTimer code into this but doesn't work.
var onClick = function() {
if (isValid( $(this) )) {
if (open.length === 0) {
openCard( $(this) );
} else if (open.length === 1) {
openCard( $(this) );
moveCounter++;
updateMoveCounter();
if (checkMatch()) {
setTimeout(setMatch, 300);
} else {
setTimeout(resetOpen, 700);
}
}
}
};
And this class code I use for my HTML file
<span class="timer">0:00</span>
Try this: https://codepen.io/anon/pen/boWQbe
All you needed to do was remove resetTimer() call from the function that happens on page load and then just do a check in the onClick (of card) to see if the timer has started yet. timer.seconds == 0 && timer.minutes == 0.

jQuery Animated Text Colors

I'm trying to get each letter color to swap from red to green and back to red.
What I have now works, but I don't like the fading, is there a better way to do this?
const ltr = $('h1').text().split('');
function colorChange() {
$( 'h1' ).fadeOut(500, function() {
redGreen();
}).fadeIn(500).fadeOut(500, function() {
greenRed();
}).fadeIn(500);
}
setInterval( function() {
colorChange();
}, 1);
function redGreen() {
$('h1').text('');
for(var i = 0; i < ltr.length; i++) {
if(i % 2 == 0) {
$('h1').append('<span class="red">' + ltr[i] + '</span>');
} else {
$('h1').append('<span class="green">' + ltr[i] + '</span>');
}
}
}
function greenRed() {
$('h1').text('');
for(var i = 0; i < ltr.length; i++) {
if(i % 2 == 0) {
$('h1').append('<span class="green">' + ltr[i] + '</span>');
} else {
$('h1').append('<span class="red">' + ltr[i] + '</span>');
}
}
}
Referred to the solution for toggling class animation here : ToggleClass animate jQuery?. You should change your colorChange function to something like this :
function colorChange() {
$( 'h1 > span' ).toggleClass( "red green", 1000, "easeInOutQuad" );
}
And make sure you build the spans at the beginning with alternative classes to each item (use one of your redGreen() or greenRed() function for the first time only).
Check this Fiddle
You need to include jQuery UI to have the effect.
I managed to remove the fade effect by using setTimeout.
See the plunker here

jQuery addClass to multiple elements shows an unexpected delay

I'm making a flipping counter which is supposed to change color when reaching the target number (1000 in the example). But the thing is the different parts of the counter doesn't change color at the same time, we can clearly see a delay between the tiles that make up the counter...
I'm using a simple jQuery addClass to trigger the color change:
$("#rhcounter .count").addClass("red");
Any ideas what could be causing that ?
Here is the fiddle: http://jsfiddle.net/ka6ke28m/6/
Thanks for your help !
First issue:
There was a huge amount of wasted processing going on. jQuery selectors have an overhead so reduce them to a minimum and complex selectors more-so. I have reduced that considerably.
Second issue:
There is a nasty visual glitch on some browsers that looked like this:
Which you can eliminate by using background-color: instead of background: (which tries to completely re-render the area instead of just fill the background colour).
Third issue:
The color blue left behind was down to slow repainting of the screen. The above two fixes had a huge impact and I also tried adding specific CSS animations that worked only with the red class. This can probably be improved now you know the causes of the slow painting (e.g. have blue and red CSS animation?):
http://jsfiddle.net/TrueBlueAussie/ka6ke28m/10/
$(function () {
var total = 1000,
current = 950,
timeout = 150,
inc = 7,
prevTiles = ["0", "0", "0", "0"],
interval = setInterval(function () {
increase()
}, timeout),
increase = function () {
current += inc;
if (current >= total) {
clearInterval(interval);
current = total;
}
if (current === total) {
$("#rhcounter .count").addClass("red");
}
// instant timer to delay css
setTimeout(function () {
var tiles = [false, false, false, false],
currStr = (current + "").split("").reverse().join("");
for (var i = 0; i < currStr.length; i++) {
if (currStr[i] !== prevTiles[i]) {
tiles[i] = true;
prevTiles[i] = currStr[i];
}
}
tiles.forEach(function (tile, index) {
if (!tile) {
return;
}
// Get the current tile
var $tile = $("#rhcounter div.tile" + index);
$tile.children('span.curr').each(function () {
$(this).text($tile.text());
});
$tile.removeClass("flip");
setTimeout(function () {
$tile.addClass("flip");
}, 5);
var top = $tile.find("span.count.next.top"),
bottom = $tile.find("span.count.next.bottom"),
delay = (index === 0 ? timeout : 250);
setTimeout(function () {
top.text(prevTiles[index]);
}, delay / 2);
setTimeout(function () {
bottom.text(prevTiles[index]);
}, delay / 2);
});
}, 1);
};
});
that was happening because you were changing color before changing text. i just shifted if condition and i think that is what you wanted DEMO
$(window).load(function() {
var total = 1000, current = 950, timeout = 150, inc = 7,
prevTiles = ["0","0","0","0"],
interval = setInterval(function(){increase()}, timeout),
increase = function () {
current += inc;
if (current >= total) {
clearInterval(interval);
current = total;
}
var tiles = [false, false, false, false],
currStr = (current+"").split("").reverse().join("");
for (var i = 0; i < currStr.length; i++) {
if (currStr[i] !== prevTiles[i]) {
tiles[i] = true;
prevTiles[i] = currStr[i];
}
}
tiles.forEach(function (tile, index) {
if (!tile) { return; }
$("#rhcounter > div[class~='tile"+index+"'] > span[class~='curr']").each(function() {
$(this).text($("#rhcounter > div[class~='tile"+index+"'] > span.count.next.top").text());
});
$("#rhcounter > div[class~='tile"+index+"']").removeClass("flip");
setTimeout(function(){$("#rhcounter > div[class~='tile"+index+"']").addClass("flip");}, 5);
var top = $("#rhcounter > div[class~='tile"+index+"'] > span.count.next.top"),
bottom = $("#rhcounter > div[class~='tile"+index+"'] > span.count.next.bottom"),
delay = (index === 0 ? timeout : 250);
setTimeout(function(){ top.text(prevTiles[index]); }, delay/2);
setTimeout(function(){ bottom.text(prevTiles[index]); }, delay/2);
});
if (current === total) {
$("#rhcounter .count").addClass("red");
}};
});

Sequentially highlighting divs using javascript

I'm trying to create kind of runway of lights and here's what it looks like now
http://jsfiddle.net/7NQvq/
var divs = document.querySelectorAll('div');
var index = 0;
setInterval(function(){
if(index > divs.length+20){
index = 0;
}
if(divs[index-1]){
divs[index-1].className = '';
}
if(divs[index]){
divs[index].className = 'active';
}
index++;
}, 50);
What I don't like about it is that it's completely inflexible and hard to adjust. Furthermore it also runs additional 20 empty cycles which is wrong. Is there a better way to achieve it (preferrably pure JS)?
It seemes that there must be some combination of setInterval and setTimeout but I just can't make it work.
I've made some adjustments to use a CSS animation rather than messing around with transitions and class toggling.
Updated Fiddle
All the JavaScript does now is define the animation delay for each dot.
You can adjust:
The animation delay - I just have i/10, but you could make it i/5, i/20... experiment!
The animation duration - it's set to 1s in my Fiddle, but try shorter and longer to see what happens
The 50% that indicates when the light has faded out
How about
function cycle(selector, cssClass, interval) {
var elems = document.querySelectorAll(selector),
prev = elems[0],
index = 0,
cssClassRe = new RegExp("\\s*\\b" + cssClass + "\\b");
if (elems.length === 0) return;
return setInterval(function () {
if (prev) prev.className = prev.className.replace(cssClassRe, "");
index %= elems.length;
elems[index].className += " " + cssClass;
prev = elems[index++];
}, interval);
}
and
var runwayIntval = cycle("div", "active", 100);
and at some point
clearInterval(runwayIntval);
See: http://jsfiddle.net/arNY8/1/
Of course you could argue that toggling a CSS class is a little limited. You could work with two callback functions instead: one to switch on a freely definable effect, one to switch it off:
function cycle(elems, enable, disable, interval) {
var prev = elems[0], index = 0;
if (elems.length === 0) return;
return setInterval(function () {
index %= elems.length;
if (prev) disable.call(prev);
enable.call(elems[index]);
prev = elems[index++];
}, interval);
}
and
var cycleIntval = cycle(
document.querySelectorAll("div"),
function () {
this.className += " active";
},
function () {
this.className = this.className.replace(/\s*\bactive\b/, "");
},
100
);

Categories