So basically, I'm working on a website that needs to have fullpage.js working.
After the first section, I have 3 similar in structure sections, where some visual elements are fixed (which aren't part of those sections in the HTML tree), and other elements that pretty much stay on the same spot for these 3 sections.
Now, when we scroll in each of these 3 sections, there needs to be a delayed scroll for an "leaving" animation to happpen on the elements before the next section enters and takes its place.
I've got this part working as intended as you can see in the code below, but when you scroll up or down and the animation class is removed and back on again the delay isn't firing again.
Anyone has any idea how I can manage to make this animation fire every single time?
const delay = 1000;
let timeoutId;
let animationIsFinished = false;
const roseTxt = document.querySelectorAll('.smart-rose');
const smartIllus = document.querySelector('.cc-smart-illustration');
const smartTitle = document.querySelector('.cc-smart-title');
new fullpage('#fullpage', {
//options here
css3: true,
scrollHorizontally: true,
scrollBar: true,
scrollOverflow: true,
normalScrollElements: '.scrollable-content',
fitToSection: true,
fitToSectionDelay: 1000,
onLeave: function (origin, destination, direction) {
var curTime = new Date().getTime();
if (origin.index == 1 && direction == 'up') {
for (var i = 0; i < roseTxt.length; i++) {
roseTxt[i].classList.remove('ccsAnimate');
}
smartIllus.classList.remove('ccAnimate');
smartTitle.classList.remove('ccAnimate');
return true;
}
if (origin.index == 1 && direction == 'down') {
//animating my element
for (var i = 0; i < roseTxt.length; i++) {
roseTxt[i].classList.add('ccsAnimate');
}
smartIllus.classList.add('ccAnimate');
smartTitle.classList.add('ccAnimate');
//roseTxt.classList.add('ccsAnimate');
clearTimeout(timeoutId);
timeoutId = setTimeout(function () {
animationIsFinished = true;
fullpage_api.moveTo(destination.index + 1);
}, delay);
return animationIsFinished;
}
if (origin.index == 2 && destination.index == 1) {
for (var i = 0; i < roseTxt.length; i++) {
roseTxt[i].classList.remove('ccsAnimate');
}
smartIllus.classList.remove('ccAnimate');
smartTitle.classList.remove('ccAnimate');
}
},
});
Thanks in advance!
Related
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");
}};
});
I have almost finished this slider, but I don't know how to implement the functionality for next() and prev(). How can I implement these functions?
http://jsfiddle.net/M4t4L/11/
$(function () {
var container = $("#scene"),
i = 0,
count = container.find("li").length,
j = container.find("li").length - 1,
isAnimating = false;
container.find("li:first").css({
"width": "100%"
});
$("#trigger").click(function (e) {
if (!isAnimating) {
isAnimating = true;
e.preventDefault(e);
i++; if (i >= count) { i = 0; }
j++; if (j >= count) { j = 0; }
container.find("li")
.finish()
.removeClass('active')
.last()
.width(0)
.addClass("active")
.animate({
"width": "100%"
}, 800,
function () {
container.find("li").first().appendTo(container);
isAnimating = false;
});
}
});
});
The problem is that when I implement these functions and press the next or prev. Displays the last slide on one second, and then switches to the desired
http://jsfiddle.net/M4t4L/9
If you want to get a Next or Prev function running, you want to take control of the number of the slider where you are. I'm afraid you will have to play around with your i/j and make the position go in both directions.
Right now you count up your i and j, where you might want to go is to have a position var and an array of slider objects, then the click only would have to call for the next/prev object to be loaded and the animation can begin.
Something like this maybe..
var pos = 0;
var container = $('#scene').find('li');
$('.back').click(function() {
pos = pos - 1;
moveIt(pos);
});
$('.forth').click(function() {
pos = pos +1;
moveIt(pos);
});
function moveIt(pos) {
container[pos]... // Your animation goes here
}
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
);
I'm using a script that animates on click left or right to the next div. It currently works fine but I'm looking to add two features to it. I need it to repeat back to the first slide if it is clicked passed the last slide and go to the last slide if click back from the first slide. Also, I'm interested in getting this to autostart on page load.
I've tried wrapping the clicks in a function and setting a setTimeout but it didn't seem to work. The animation is currently using CSS.
Here's the current JS:
<script>
jQuery(document).ready(function() {
var boxes = jQuery(".box").get(),
current = 0;
jQuery('.right').click(function () {
if (current == (-boxes.length + 1)){
} else {
current--;
updateBoxes();
}
console.log(-boxes.length + 1);
console.log(current);
});
jQuery('.left').click(function () {
if (current === 0){
} else{
current++;
updateBoxes();
}
});
function updateBoxes() {
for (var i = current; i < (boxes.length + current); i++) {
boxes[i - current].style.left = (i * 100 + 50) + "%";
}
}
});
</script>
Let me know if I need a jsfiddle for a better representation. So far, I think the code is pretty straightforward to animate on click.
Thanks.
Try
jQuery(document).ready(function () {
var boxes = jQuery(".box").get(),
current = 0,
timer;
jQuery('.right').click(function () {
if (current == (-boxes.length + 1)) {
current = 0;
} else {
current--;
}
updateBoxes();
}).click(); //initialize the view
jQuery('.left').click(function () {
if (current === 0) {
current = -boxes.length + 1;
} else {
current++;
}
updateBoxes();
});
function updateBoxes() {
//custom implementation for testing
console.log('show', current)
$(boxes).hide().eq(-current).show();
autoPlay();
}
function autoPlay() {
clearTimeout(timer);
//auto play
timer = setTimeout(function () {
jQuery('.right').click();
}, 2500)
}
});
Demo: Fiddle
Here's an example based on my comment (mostly pseudocode):
$(function(){
var boxes = $('.box'),
current = 0,
timer;
// Handler responsible for animation, either from clicking or Interval
function animation(direction){
if (direction === 1) {
// Set animation properties to animate forward
} else {
// Set animation properties to animate backwards
}
if (current === 0 || current === boxes.length) {
// Adjust for first/last
}
// Handle animation here
}
// Sets/Clears interval
// Useful if you want to reset the timer when a user clicks forward/back (or "pause")
function setAutoSlider(set, duration) {
var dur = duration || 2000;
if (set === 1) {
timer = setInterval(function(){
animation(1);
}, dur);
} else {
clearInterval(timer)
}
}
// Bind click events on arrows
// We use jQuery's event binding to pass the data 0 or 1 to our handler
$('.right').on('click', 1, function(e){animation(e.data)});
$('.left').on('click', 0, function(e){animation(e.data)});
// Kick off animated slider
setAutoSlider(1, 2000);
Have fun! If you have any questions, feel free to ask!
If this is for a double-click:
window.addEventListener("dblclick", function(event) { }, false);
How can I capture a triple-click? This is for a pinned tab in Google Chrome.
You need to write your own triple-click implementation because no native event exists to capture 3 clicks in a row. Fortunately, modern browsers have event.detail, which the MDN documentation describes as:
A count of consecutive clicks that happened in a short amount of time, incremented by one.
This means you can simply check the value of this property and see if it is 3:
window.addEventListener('click', function (evt) {
if (evt.detail === 3) {
alert('triple click!');
}
});
Working demo: http://jsfiddle.net/L6d0p4jo/
If you need support for IE 8, the best approach is to capture a double-click, followed by a triple-click — something like this, for example:
var timer, // timer required to reset
timeout = 200; // timer reset in ms
window.addEventListener("dblclick", function (evt) {
timer = setTimeout(function () {
timer = null;
}, timeout);
});
window.addEventListener("click", function (evt) {
if (timer) {
clearTimeout(timer);
timer = null;
executeTripleClickFunction();
}
});
Working demo: http://jsfiddle.net/YDFLV/
The reason for this is that old IE browsers will not fire two consecutive click events for a double click. Don't forget to use attachEvent in place of addEventListener for IE 8.
Since DOM Level 2 you could use mouse click handler and check the detail parameter of event which should be interpreted as:
The detail attribute inherited from UIEvent indicates the number of times a mouse button has been pressed and released over the same screen location during a user action. The attribute value is 1 when the user begins this action and increments by 1 for each full sequence of pressing and releasing. If the user moves the mouse between the mousedown and mouseup the value will be set to 0, indicating that no click is occurring.
So the value of detail === 3 will give you the triple-click event.
More information in specification at http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-MouseEvent.
Thanks to #Nayuki https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail - a DOM3 extension which is WIP https://w3c.github.io/uievents/
Here is the real Triple click event, which triggers only when all of three clicks fired with equal interval.
// Default settings
var minClickInterval = 100,
maxClickInterval = 500,
minPercentThird = 85.0,
maxPercentThird = 130.0;
// Runtime
var hasOne = false,
hasTwo = false,
time = [0, 0, 0],
diff = [0, 0];
$('#btn').on('click', function() {
var now = Date.now();
// Clear runtime after timeout fot the 2nd click
if (time[1] && now - time[1] >= maxClickInterval) {
clearRuntime();
}
// Clear runtime after timeout fot the 3rd click
if (time[0] && time[1] && now - time[0] >= maxClickInterval) {
clearRuntime();
}
// Catch the third click
if (hasTwo) {
time[2] = Date.now();
diff[1] = time[2] - time[1];
var deltaPercent = 100.0 * (diff[1] / diff[0]);
if (deltaPercent >= minPercentThird && deltaPercent <= maxPercentThird) {
alert("Triple Click!");
}
clearRuntime();
}
// Catch the first click
else if (!hasOne) {
hasOne = true;
time[0] = Date.now();
}
// Catch the second click
else if (hasOne) {
time[1] = Date.now();
diff[0] = time[1] - time[0];
(diff[0] >= minClickInterval && diff[0] <= maxClickInterval) ?
hasTwo = true : clearRuntime();
}
});
var clearRuntime = function() {
hasOne = false;
hasTwo = false;
time[0] = 0;
time[1] = 0;
time[2] = 0;
diff[0] = 0;
diff[1] = 0;
};
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Click button three times with equal interval
<button id="btn">Click me</button>
Also, I wrote jquery plugin TrplClick, which enables 'trplclick' event
it's very simple if you do it right, and you can even catch single, double, triple, ... clicks as you like. plain javascript, customizable click delay (timeout):
var clicks = 0;
var timer, timeout = 350;
var doubleClick = function(e) {
console.log('doubleClick');
}
var tripleClick = function(e) {
console.log('tripleClick');
}
// click timer
yourcontainer.addEventListener('click', function(e) {
clearTimeout(timer);
clicks++;
var evt = e;
timer = setTimeout(function() {
if(clicks==2) doubleClick(evt);
if(clicks==3) tripleClick(evt);
clicks = 0;
}, timeout);
});
pseudo-code:
var clicks = 0
onclick:
clicks++;
setTimer(resetClicksToZero);
if clicks == 3: tripleclickdetected(); clicks = 0;
I am working on a javascript code editor and I had to listen for triple click and here is the solution that will work for most browsers:
// Function to get mouse position
var getMousePosition = function (mouseEvent) {
var currentObject = container;
var currentLeft = 0;
var currentTop = 0;
do {
currentLeft += currentObject.offsetLeft;
currentTop += currentObject.offsetTop;
currentObject = currentObject.offsetParent;
} while (currentObject != document.body);
return {
x: mouseEvent.pageX - currentLeft,
y: mouseEvent.pageY - currentTop
}
}
// We will need a counter, the old position and a timer
var clickCounter = 0;
var clickPosition = {
x: null,
y: null
};
var clickTimer;
// The listener (container may be any HTML element)
container.addEventListener('click', function (event) {
// Get the current mouse position
var mousePosition = getMousePosition(event);
// Function to reset the data
var resetClick = function () {
clickCounter = 0;
var clickPosition = {
x: null,
y: null
};
}
// Function to wait for the next click
var conserveClick = function () {
clickPosition = mousePosition;
clearTimeout(clickTimer);
clickTimer = setTimeout(resetClick, 250);
}
// If position has not changed
if (clickCounter && clickPosition.x == mousePosition.x && clickPosition.y == mousePosition.y) {
clickCounter++;
if (clickCounter == 2) {
// Do something on double click
} else {
// Do something on triple click
resetClick();
}
conserveClick();
} else {
// Do something on single click
conserveClick();
}
});
Tested on Firefox 12, Google Chrome 19, Opera 11.64, Internet Explorer 9
This approach checks if the user has not changed cursor's position, you still can do something when you have single click or double click. Hope this solution will help everybody who will need to implement a triple click event listener :)
Configurable n-clicks event detector factory
const nClicks = (minClickStreak, maxClickInterval = 500, resetImmediately = true) => {
let timerId = 0
let clickCount = 0
let lastTarget = null
const reset = () => {
timerId = 0
clickCount = 0
lastTarget = null
}
return (originalEventHandler) => (e) => {
if (lastTarget == null || lastTarget == e.target) { // 2. unless we clicked same target
clickCount++ // 3. then increment click count
clearTimeout(timerId)
}
lastTarget = e.target
timerId = setTimeout(reset, maxClickInterval) // 1. reset state within set time
if (clickCount >= minClickStreak) {
originalEventHandler(e)
if (resetImmediately) {
clickCount = 0
}
}
}
}
Usage
table.addEventListener('click', nClicks(2)(e => { // double click
selectCell(e.target)
}))
table.addEventListener('click', nClicks(3)(e => { // triple click
selectRow(e.target)
}))