Smooth horizontal scroll bound to mousewheel
Asked Answered
P

8

10

Here is a working example of horizontal scroll with mousewheel, but it does not scroll smoothly. By smoothly I mean like ordinary vertical scroll in Firefox or Opera.

$(function() {
    $("html, body").mousewheel(function(event, delta) {
        this.scrollLeft -= (delta * 30);
        event.preventDefault();
    });
});

(http://brandonaaron.net/code/mousewheel/docs)

I've made a live demo to demonstrate this. http://jsfiddle.net/Dw4Aj/

I want this scroll to work like the vertical one, both with mousewheel and smoothness.

Can someone help me?

Proselytize answered 21/10, 2012 at 17:8 Comment(3)
create a live demo that replicates issue in jsfiddle.netFrankfort
here is it! linkProselytize
Possibly unrelated, I logged the delta variable scrolling on a MacBook trackpad (no mouse), and it proved pretty unreliable. Conduct your own tests and determine if that might be the weak link of this approach.Multifoliate
P
0

I'm just going to leave this here.

http://jsfiddle.net/Dw4Aj/19/

jQuery(document).ready(function($) {
$("html, body").mousewheel(function(e, delta) { 
    $('html, body').stop().animate({scrollLeft: '-='+(150*delta)+'px' }, 200, 'easeOutQuint');
    e.preventDefault();
});

});
Proselytize answered 10/11, 2012 at 3:39 Comment(1)
This isn't working on Chrome 46. Made small changes but here is an updated version that it's working fine. jsfiddle.net/Dw4Aj/338Cullis
U
3

Smooth scrolling is a browser specific feature.

If you want something that works on all of them then you need to do it on your side. There are multiple implementations of smooth scrolling for jQuery.

And actually you may even need so called kinetic scrolling. If so try jquery.kinetic

Unilingual answered 22/10, 2012 at 4:57 Comment(3)
Thank you but this is not what I want, even not close. I just want horizontal scroll to work like the original one vertical scroll in Firefox or Opera.Proselytize
Since you're doing it programatically, it most likely won't work like the native controls.Multifoliate
@Vladimir Mikhalev As I said smooth scrolling is a browser specific feature. Google Chrome for example does not use smooth scrolling at all. So if you want to see smooth scrolling there and in all others and in the same manner you have to implement this manually or use some existing library for that.Unilingual
G
2

1st i think about it is to remember last scroll event timestamp, play with easing function, to get good result http://jsfiddle.net/oceog/Dw4Aj/13/

$(function() {

    $("html, body").mousewheel(function(event, delta) {
        var mult = 1;
        var $this = $(this);
        if (event.timeStamp - $this.data('oldtimeStamp') < 1000) {
            //calculate easing here
            mult = 1000 / (event.timeStamp - $this.data('oldtimeStamp'));
        }
        $this.data('oldtimeStamp', event.timeStamp);
        this.scrollLeft -= (delta) * mult;
        event.preventDefault();
    });
});​
Grunt answered 22/10, 2012 at 4:53 Comment(5)
Did you perchance test your demo in chrome? Not working for me.Caron
None. The only thing I can really determine is that scrollLeft sometimes doesn't get set.Caron
It's working but very glitchy. I think that right answer is to use .animate to make it work properly.Proselytize
Hm, I checked with FF and chrome, works fine for me, but it may be because of my mouse,Kaffraria
I would not suggest to use animation() you will take some new problems with it (animation stop/start, how to handle new events during animation, how to scroll by minimal possible delta)Kaffraria
I
1

Try to use your function in conjunction with .animate()

$(function() {
    $("html, body").mousewheel(function(event, delta) {
        var scroll_distance = delta * 30
        this.animate(function(){
           left: "-=" + scroll_distance + "px",
        });
        event.preventDefault();
    });
});

I just actually did this myself and it works. I created an instagram feed on the web application that I created, and the other plugins that I was using were breaking all too often:

$('#add_instagram').on('mousewheel', function(e,d){
    var delta = d*10;
    $('#move_this').animate({
        top: "-=" + delta + "px"
    },3);
    e.preventDefault();
});
Interplay answered 22/10, 2012 at 5:32 Comment(6)
does not work, says "syntax error" here: left: -= (delta * 30);Proselytize
Try putting that in quotes: "-=(delta*30)px"Interplay
Not working both ways, now it says "animate is not a function"Proselytize
Ok... Do $(this) instead of just "this"Interplay
Sad, but I have nothing. Here is your script injected jsfiddle.net/Dw4Aj/17Proselytize
Here's a plugin that I created today. This might help: github.com/bjoshuanoah/scrollThis.jsInterplay
G
0

I found other nice solution - to use wrapper, so you scroll absolute to same position as you would to scroll vertical, it works if you just need scroll, no text selection or other(may be can work arounded, but i tired)

$(function() {

    var scroll = $('.scrollme');
    var h = scroll.parent().height();
    var w = scroll.parent().width();
    var t = scroll.offset().top;
    var l = scroll.offset().left;
    var vertscroll_wrap = $("<div>").height(h).width(10000).css('position', 'absolute').css('top', t).css('left', l).css('z-index', 10000).css('opacity', 0.5).css('overflow-y', 'scroll');
    var vertscroll = $('<div>').height(10000).css('width', '100%').css('opacity', 0.5);
    vertscroll_wrap.append(vertscroll);
    $('body').append(vertscroll_wrap);
    vertscroll_wrap.height('100%');
    vertscroll_wrap.scroll(function() {
        $(scroll).parent().scrollLeft($(this).scrollTop());
    });


});​

http://jsfiddle.net/oceog/Dw4Aj/16/

i made another sample, now without wholescreen wrapper, and possibility to select http://jsfiddle.net/oceog/DcyWD/

Grunt answered 22/10, 2012 at 7:12 Comment(0)
P
0

I'm just going to leave this here.

http://jsfiddle.net/Dw4Aj/19/

jQuery(document).ready(function($) {
$("html, body").mousewheel(function(e, delta) { 
    $('html, body').stop().animate({scrollLeft: '-='+(150*delta)+'px' }, 200, 'easeOutQuint');
    e.preventDefault();
});

});
Proselytize answered 10/11, 2012 at 3:39 Comment(1)
This isn't working on Chrome 46. Made small changes but here is an updated version that it's working fine. jsfiddle.net/Dw4Aj/338Cullis
F
0

Just change this:

this.scrollLeft -= (delta * 30);

to this:

this.scrollLeft -= (delta * 1);
Ferri answered 7/7, 2016 at 7:58 Comment(0)
D
0

You could try to utilize the browser built in scroll behavior, by using simply scrollBy:

const target = document.getElementsByClassName('whatever')[0];
target.addEventListener('wheel', event => {
    event.preventDefault();
    target.scrollBy({
    left: event.deltaY*0.5,    
    behavior: 'smooth'
});

})

It is a bit sloppy when you scroll more before the browser finished the smooth scroll (as smooth scrolling takes some time to finish the "animation"), but for some scenarios it does the job.

You can also achieve the same essentially by settings the css property scroll-behavior: smooth, and then explicitly setting target.scrollLeft = ... on the element. Both approaches essentially use the bult-in scroll implementation of the browsers (except Safari and IE10 maybe, as those don't support this smooth behavior at all to date)

Defilade answered 2/7, 2020 at 22:32 Comment(0)
M
0

I fixed the stuck when re-scrolling to the end of the previous scroll.

pure js.

const el = document.getElementsByClassName('whatever')[0];
let sumDelta = 0
let startLeft = 0
let targetLeft = 0
el.addEventListener('wheel', (e) => {
    if (el.scrollLeft === targetLeft) sumDelta = 0
    if (sumDelta === 0) startLeft = el.scrollLeft
    sumDelta -= e.deltaY
    const maxScrollLeft = el.scrollWidth - el.clientWidth;
    const left = Math.min(maxScrollLeft, Math.max(0, startLeft - sumDelta));
    targetLeft = left
    el.scrollTo({left, behavior: "smooth"})
    e.preventDefault();
})
Manure answered 24/5, 2021 at 16:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.