I made a custom content scroller with jQuery and GSAP in the aim to have a nicer effect than the Chrome basic & stuttered scroll. But I just saw (on Chrome then) that the scroll is laggy and well bugged when I test it with my Wacom tablet (with pen + horizontal scroll, or any trackpad/touch stuff I guess).
Any thoughts about tweaks to render a more natural scroll effect, not hacked like now?
Maybe it's something about the mousewheel DOMMouseScroll event, or my method is bad, the markup/css need some changes…?
$("section").on("mousewheel DOMMouseScroll", function (e) {
e.preventDefault();
var delta = e.originalEvent.wheelDelta / 120 || -e.originalEvent.detail; // Chrome || FF
// Move thumbs
TweenLite.to($("#photos"), 1.2, {
scrollLeft: $("#photos").scrollLeft() - parseInt(delta * 35),
ease: Expo.easeOut,
overwrite: 5,
onUpdate: move
});
});
—
http://jsfiddle.net/h66tatp6/
Many thanks for your lights!
Cooked up something. Not really sure if it fits your needs or if it is exactly the type of thing you were looking for, but perhaps it will give you some ideas.
Codepen.
JavaScript:
/*global TweenMax,Power2,Power4*/
var initIntervalID=null,initInterval=10;
var scrollBar=document.querySelector('.scrollbar');
var scrollBarHandle=document.querySelector('.scrollbar__handle');
var scrollBarBg=document.querySelector('.scrollbar__bg');
var container=document.querySelector('.container');
var images=document.querySelector('.images');
var thumbs=document.querySelectorAll('.thumb');
var numThumbs=thumbs.length;
var resizeID=null,resizeTimeout=400,resizeDuration=.4,resizeEase=Power2.easeOut;
var initDuration=.4,initEase=Power2.easeOut,initStagger=.1;
var duration=.4,ease=Power2.easeOut;
var force3D=true;
var windowWidth=window.innerWidth;
var windowHeight=window.innerHeight;
var thumbWidth=200,thumbHeight=200,thumbGutter=20;
var scrollBarBgHeight=10,scrollBarBgWidth=thumbWidth,scrollBarHandleWidth=20,scrollBarGutter=10;
var imagesPositions=[],iterator=0,percentages=[],scrollBarHandlePositions=[];
function init(){
initImages();
initScrollbar();
initPositions();
assignListeners();
}
function onMouseWheel(event){
var e=window.event||event;
var delta=Math.max(-1,Math.min(1,(e.wheelDelta|| -e.detail)));
if(delta>0){
iterator-=1;
if(iterator<0){
iterator=0;
TweenMax.to(images,duration*2,{bezier:{type:'thru',values:[{x:imagesPositions[iterator]+(thumbWidth+thumbGutter)*.1},{x:imagesPositions[iterator]}]},ease:ease});
TweenMax.killTweensOf(scrollBar);
TweenMax.to(scrollBar,duration,{autoAlpha:1,ease:ease});
TweenMax.to(scrollBarHandle,duration*2,{
transformOrigin:'left',
bezier:{type:'thru',values:[{scaleX:.2},{scaleX:1}]},
ease:ease,
onComplete:function(){ TweenMax.to(scrollBar,duration,{autoAlpha:0,ease:ease}); }
});
}else{
TweenMax.to(images,duration,{x:imagesPositions[iterator],ease:ease});
TweenMax.killTweensOf(scrollBar);
TweenMax.to(scrollBar,duration,{autoAlpha:1,ease:ease});
TweenMax.to(scrollBarHandle,duration,{
x:scrollBarHandlePositions[iterator],
ease:ease,
onComplete:function(){ TweenMax.to(scrollBar,duration,{autoAlpha:0,ease:ease}); }
});
}
}else{
iterator+=1;
if(iterator>numThumbs-1){
iterator=numThumbs-1;
TweenMax.to(images,duration*2,{bezier:{type:'thru',values:[{x:imagesPositions[iterator]-(thumbWidth+thumbGutter)*.1},{x:imagesPositions[iterator]}]},ease:ease});
TweenMax.killTweensOf(scrollBar);
TweenMax.to(scrollBar,duration,{autoAlpha:1,ease:ease});
TweenMax.to(scrollBarHandle,duration*2,{
transformOrigin:'right',
bezier:{type:'thru',values:[{scaleX:.2},{scaleX:1}]},
ease:ease,
onComplete:function(){ TweenMax.to(scrollBar,duration,{autoAlpha:0,ease:ease}); }
});
}else{
TweenMax.to(images,duration,{x:imagesPositions[iterator],ease:ease});
TweenMax.killTweensOf(scrollBar);
TweenMax.to(scrollBar,duration,{autoAlpha:1,ease:ease});
TweenMax.to(scrollBarHandle,duration,{
x:scrollBarHandlePositions[iterator],
ease:ease,
onComplete:function(){ TweenMax.to(scrollBar,duration,{autoAlpha:0,ease:ease}); }
});
}
}
return false;
}
function listenToMouseWheel(){
if(container.addEventListener){
container.addEventListener('mousewheel',onMouseWheel,false);
container.addEventListener('DOMMouseScroll',onMouseWheel,false);
}else{
container.attachEvent('onmousewheel',onMouseWheel);
}
}
function adjustContainerOnResize(){
windowWidth=window.innerWidth;
windowHeight=window.innerHeight;
TweenMax.to(container,resizeDuration,{
x:windowWidth*.5-thumbWidth*.5,
y:windowHeight*.5-thumbHeight*.5,
ease:resizeEase,
force3D:force3D
});
}
function onResize(){
clearTimeout(resizeID);
resizeID=setTimeout(adjustContainerOnResize,resizeTimeout);
}
function listenToResize(){
(window.addEventListener)?window.addEventListener('resize',onResize,false):window.attachEvent('onresize',onResize);
}
function assignListeners(){
listenToResize();
listenToMouseWheel();
}
function initPositions(){
for(var i=0; i<numThumbs; i+=1){
imagesPositions[i]=-i*(thumbWidth+thumbGutter);
percentages[i]=i*(100/numThumbs);
if(i===0){
scrollBarHandlePositions[i]=0;
}else if(i===numThumbs-1){
scrollBarHandlePositions[i]=scrollBarBgWidth-scrollBarHandleWidth;
}else{
scrollBarHandlePositions[i]=i*(scrollBarBgWidth/(numThumbs-1))-scrollBarHandleWidth*.5;
}
}
}
function initScrollbar(){
TweenMax.set(scrollBar,{
y:scrollBarGutter,
width:scrollBarBgWidth,
height:scrollBarBgHeight,
force3D:force3D
});
TweenMax.set(scrollBarHandle,{y:0,force3D:force3D});
TweenMax.to(scrollBar,initDuration,{delay:initStagger,opacity:1,ease:initEase});
}
function initImages(){
for(var i=0; i<numThumbs; i+=1){
TweenMax.set(thumbs[i],{x:i*(thumbWidth+thumbGutter),force3D:force3D});
}
var width=numThumbs*(thumbWidth+thumbGutter)-thumbGutter;
TweenMax.set(container,{y:windowHeight*.5-thumbHeight*.5,x:windowWidth*.5-thumbWidth*.5,height:thumbHeight+scrollBarGutter+scrollBarBgHeight,width:thumbWidth,force3D:force3D});
TweenMax.set(images,{height:thumbHeight,width:width,force3D:force3D});
TweenMax.staggerTo(thumbs,initDuration,{opacity:1,ease:initEase},initStagger);
}
initIntervalID=setInterval(function(){
if(document.readyState==="complete"){
clearInterval(initIntervalID);
init();
}
},initInterval);
Beware of ugly, non-refactored, highly un-tested, un-optimized code. Created only for fun. If I find time, may be I will build upon it later and include other mouse interactions such as mousedown, mousemove, mouseup as well as touch interactions such as touchstart, touchmove, touchend events. Should be fun. For now, use it as you may like.
I have to do some Special things for my Webpage to work on Android the correct way. Some Images are displayed (one visible, the other unvisible) and through swipe it should be possible to Change them. No Problem so far on all OS.
But it also should be possible to zoom. Now Android starts to be Buggy. It stops the zoom-gesture because of the swipe callback. The callback itself doesn't Change the page because the view is zoomed, so there should be no break.
Now I work arround through turning my swipeleft and swiperight off while two fingers touching the Display, and tourning back on if the fingers leave the Display.
On First run I can swipe, then I can zoom with no break, but then I can't swipe anymore. The function to set the callbacks back on again is called, it set's the callbacks, but they won't be executed...
Here's the code:
app.utils.scroll = (function(){
var $viewport = undefined;
var swipeDisabled = false;
var init = function(){
$viewport = $('#viewport');
$viewport.mousewheel(mayChangePage);
// On touchstart with two fingers, remove the swipe listeners.
$viewport.on('touchstart', function (e) {
if (e.originalEvent.touches.length > 1) {
removeSwipe();
swipeDisabled = true;
}
});
// On touchend, re-define the swipe listeners, if they where removed through two-finger-gesture.
$viewport.on('touchend', function (e) {
if (swipeDisabled === true) {
swipeDisabled = false;
initSwipe();
}
});
initSwipe();
}
var mayChangePage = function(e){
// If page is not zoomed, change page (next or prev).
if (app.utils.zoom.isZoomed() === false) {
if (e.deltaY > 0) {
app.utils.pagination.prev(e);
} else {
app.utils.pagination.next(e);
}
}
// Stop scrolling page through mouse wheel.
e.preventDefault();
e.stopPropagation();
};
var next = function (e) {
// If page is not zoomed, switch to next page.
if (app.utils.zoom.isZoomed() === false) {
app.utils.pagination.next(e);
}
};
var prev = function (e) {
// If page is not zoomed, switch to prev page.
if (app.utils.zoom.isZoomed() === false) {
app.utils.pagination.prev(e);
}
};
var initSwipe = function () {
// Listen to swipeleft / swiperight-Event to change page.
$viewport.on('swipeleft.next', next);
$viewport.on('swiperight.prev', prev);
};
var removeSwipe = function () {
// Remove listen to swipeleft / swiperight-Event for changing page to prevent android-bug.
$viewport.off('swipeleft.next');
$viewport.off('swiperight.prev');
};
$(document).ready(init);
}());
Pastebin
Any ideas what I can do to get the Events back on again?
Thanks for all Ideas.
Regards
lippoliv
Fixed it:
jQuery Mobile itself prevents the swipe Event if an handler is registered, to kill an "scroll".
So I overwrote the $.event.special.swipe.scrollSupressionThreshold value and set it to 10000, to prevent jQueryMobile's preventDefault-call:
$.event.special.swipe.scrollSupressionThreshold = 10000;
Now my Code Looks like
app.utils.scroll = (function(){
var $viewport = undefined;
var swipeDisabled = false;
var init = function(){
$viewport = $('#viewport');
$viewport.mousewheel(mayChangePage);
// See #23.
$.event.special.swipe.scrollSupressionThreshold = 10000;
// Listen to swipeleft / swiperight-Event to change page.
$viewport.on('swipeleft.next', next);
$viewport.on('swiperight.prev', prev);
}
var mayChangePage = function(e){
// If page is not zoomed, change page (next or prev).
if (app.utils.zoom.isZoomed() === false) {
if (e.deltaY > 0) {
app.utils.pagination.prev(e);
} else {
app.utils.pagination.next(e);
}
}
// Stop scrolling page through mouse wheel.
e.preventDefault();
e.stopPropagation();
};
var next = function (e) {
// If page is not zoomed, switch to next page.
if (app.utils.zoom.isZoomed() === false) {
app.utils.pagination.next(e);
}
};
var prev = function (e) {
// If page is not zoomed, switch to prev page.
if (app.utils.zoom.isZoomed() === false) {
app.utils.pagination.prev(e);
}
};
$(document).ready(init);
}());
Thanks to Omar- who wrote with me several minutes / hours in the jquery IRC and gave some suggestions regarding overwriting Standard values for jQueryMobile.
I am trying to bring in an effect like hugeinc.com in my website udowalzfinal.umatechcorner.com
I did the following
$(window).on({
'DOMMouseScroll mousewheel': ScrollBegin
});
var delta = 0;
var scrollThreshold = 10;
function ScrollBegin(e) {
// --- Scrolling up ---
if (e.originalEvent.detail < 0 || e.originalEvent.wheelDelta > 0) {
delta--;
console.log(delta);
if ( Math.abs(delta) >= scrollThreshold) {
timer = setTimeout(function () {
MoveScreen('up');
}, 800);
clearTimeout(timer);
}
}
// --- Scrolling down ---
else {
delta++;
console.log(delta);
if (delta >= scrollThreshold) {
timer = setTimeout(function () {
MoveScreen('down');
clearTimeout(timer);
}, 800);
}
}
// Prevent page from scrolling
return false;
}
I don't know what value to set for scrollThreshold.
mousewheel event is raised & ScrollBegin is executed. But this is too slow in IE & too fast in Safari with Apple Mouse.
Mousewheel event is raised for each mouse wheel move. In my case, it raises the event 10 times when I scroll the mouse wheel once. How can I disable those 9 events and only handle it once.
How to get a MouseWheel Event to fire only once in jQuery? article does not fix my problem. This only gives support for moving up once & then down once. But I have 5 slides in my page.
Can someone please help?
You should probably use a debouncer.
//Debouncer functions add a delay between an event and a reaction, so scaling and scrolling don't evoke a function dozens of times.
function debouncer(func, timeout) {
var timeoutID , timeout = timeout || 200;
return function () {
var scope = this , args = arguments;
clearTimeout( timeoutID );
timeoutID = setTimeout( function () {
func.apply( scope , Array.prototype.slice.call( args ) );
} , timeout );
};
}
This is a general purpose function. Use it like this:
jQuery(window).scroll(debouncer(function(){
//Call your scroll handler here.
}));
To really fine tune the delay, add a cecond parameter, like:
jQuery(window).scroll(debouncer(function(){
//Call your scroll handler here.
}), 600);
You want to look at something like Underscore.js's Debounce function. You can either use debounce directly by bringing underscore onto your page (not a bad idea), or implement it yourself. There's also throttle, which works slightly differently; here's an example with both:
http://goo.gl/jEl9lA
Im a JQuery noob trying to write a simple jQuery code to get a text to blink three times. My initial code was as follows:
$("#welcome").click(function () {
var i = 1;
while (++i < 10) {
$("#welcome").fadeOut("slow", function () { $("#welcome").fadeIn("slow"); })();
}
});
But since I probably meddled in forces I could not comprehend, the above code made the text blink only once. I read up on closures and got convinced that the below code could make a change. Unfortunately, it doesnt.
$("#welcome").click(function () {
var i = 1;
while (++i < 10) {
(function (i) {
$("#welcome").fadeOut("slow", function () { $("#welcome").fadeIn("slow"); })();
})(i);
}
});
Can anyone tell me whats going on here?
You need make use of the animation queue
var $welcome = $("#welcome").click(function () {
var i = 1;
//clear previous animations
$welcome.stop(true, true);
while (++i < 10) {
$welcome.fadeOut("slow").fadeIn("slow");
}
});
Demo: Fiddle
Fading in and out takes some time, and you have to wait for your animation to be over before you can run the next one.
The provided answers solve your problem since jQuery is clever enough to bufferize your animation queue, but it may creates even more confusion for begginers, and also if you want to do something else between the fading animations, you can't rely on it anymore.
You then have to write your code on what is called an asynchronous recursive way (woah). Simply trying to understand that snippet may help you a lot with javascript general programming.
function blink(nbBlinks) {
// Only blink if the nbBlinks counter is not zero
if(nbBlinks > 0) {
$('#welcome').fadeOut('slow', function() {
// Do stuff after the fade out animation
$(this).fadeIn('slow', function() {
// Now we're done with that iteration, blink again
blink(nbBlinks-1);
})
});
}
}
// Launch our blinking function 10 times
blink(10);
This works perfectly. Demo http://jsfiddle.net/X5Qy3/
$("#welcome").click(function () {
for (var x = 0; x < 3; x += 1) {
$("#welcome").fadeOut("slow");
$("#welcome").fadeIn("slow");
}
});
Also, if you know how many times you want to do something. You should use a For Loop. While Loops are for when you don't know how many times you want it to run.
Set in queue
$("#welcome").click(function () {
var i = 1;
//clear animations whcih are running at that time
$(this).stop(true, true);
while (++i < 10) {
$(this).fadeOut("slow").fadeIn("slow");
}
});
You can not use jQuery delay function inside a looping/iteration hence you have to user closures:
$(document).ready(function(){
$(".click1").click(function () {
for (i=0;i<=10;i++) {
setTimeout(function(x) {
return function() {
$("#wrapper").fadeOut("slow", function () { $("#wrapper").fadeIn("slow"); })();
};
}(i), 1000*i);
}
});
});
<div id="wrapper"></div><div class="click1">click</div>
You can later change the count how many times you want to blink the <div>.
How should I do to get the scrolling direction using onScrollStart and not onScrollEnd?
For now, here is the code I found for onScrollEnd :
var current_page = 1;
var old_page = 0;
funnction load() {
myScrollH = new iScroll('wrapper', {
onScrollEnd: function () {
current_page = this.currPageX+1
if(old_page<current_page) {
console.log('right');
}
else if (old_page>current_page) {
console.log('left');
};
old_page=current_page;
}
})
};
load();
If someone has the answer...
Tchuss !
V.
During onScrollStart, the event Scroll is not fired. So its not possible (i think) to implement the same.
Only on onScrollEnd, the Scroll Event is fired which has the direction.