flickity icon indicating copy to clipboard operation
flickity copied to clipboard

Enable scrolling via mousewheel

Open iamstiil opened this issue 10 years ago • 45 comments

I'm experiencing a little problem with one of my sliders.

// Defined in seperate JS file
function scrollEnd(flickelem) {
    // FLICKITY DragEnd

    if ( flickelem.options.freeScroll ) {
        flickelem.isFreeScrolling = true;
    }
    // set selectedIndex based on where flick will end up
    var index = flickelem.dragEndRestingSelect();

    if ( flickelem.options.freeScroll && !flickelem.options.wrapAround ) {
    // if free-scroll & not wrap around
    // do not free-scroll if going outside of bounding cells
    // so bounding cells can attract slider, and keep it in bounds
    var restingX = flickelem.getRestingPosition();
    flickelem.isFreeScrolling = -restingX > flickelem.cells[0].target &&
    -restingX < flickelem.getLastCell().target;
    } else if ( !flickelem.options.freeScroll && index == flickelem.selectedIndex ) {
    // boost selection if selected index has not changed
    index += flickelem.dragEndBoostSelect();
    }
    // apply selection
    // TODO refactor this, selecting here feels weird
    flickelem.select( index );
}

// Inside a <script> tag inside the document
$(function(){
    $(document).ready(function(e) {
        var cellCount = $('.gallery-cell').length;
        var canWrapAround = false;
        if(cellCount > 1) canWrapAround = true;
        $('.text-slider').flickity({
            accessibility: true,
            autoPlay: false,
            cellAlign: 'center',
            cellSelector: undefined,
            contain: true,
            draggable: true,
            freeScroll: true,
            friction: 0.2,
            imagesLoaded: true,
            initialIndex: 0,
            percentPosition: true,
            prevNextButtons: false,
            pageDots: false,
            resize: true,
            rightToLeft: false,
            watchCSS: false,
            wrapAround: canWrapAround
        }).mousewheel(function(e) {
            e.preventDefault();
            var flickelem = Flickity.data(this);

            if (e.deltaX) {
                flickelem.x += e.deltaX * e.deltaFactor;
            }
            else if (e.deltaY) {
                flickelem.x += e.deltaY * e.deltaFactor;
            }
            scrollEnd(flickelem);
        });
        $(window).load(function(e) {
            $('.text-slider').flickity('resize');
        });
    });
});

I am using the jQuery mousewheelplugin to support scrolling inside flickity. But somehow on mobile devices occasionally I can't reach the last cell. It always flicks back to the previous one. Could it be a bug or could it be a fault of me?

iamstiil avatar Feb 11 '15 13:02 iamstiil

Add a 👍 reaction to this issue if you would like to see this feature added. Do not add +1 comments — They will be deleted.


Thanks for taking a shot at this. I haven't looked in to supporting mousewheel, or providing an API to manipulate the slider position. I'll categorize this as a feature request for now. I'm focusing on releasing the core feature set for Flickity at the moment. I'll come back to this afterwards.

desandro avatar Feb 11 '15 17:02 desandro

Reclassifying this issue as a feature request. Flickity does not currently support manipulation of the slider position. If you would like to see this feature, please +1 or subscribe to this issue.

desandro avatar Feb 28 '15 21:02 desandro

+1

nvartolomei avatar Feb 28 '15 22:02 nvartolomei

+1

(I have a homegrown carousel in place at http://theartificial.nl/ and want to replace it, but mousey-scroll is important to me.)

gerwitz avatar Mar 01 '15 11:03 gerwitz

+1

FabianGabor avatar Mar 02 '15 17:03 FabianGabor

Got something here ;)

$('.gallery').mousewheel(function(e) {
    e.preventDefault();
    var flkty = Flickity.data(this);

    if (!window.wheeling) {
        // console.log('start wheeling!');

        if(e.deltaX > 0 || e.deltaY < 0){
            flkty.next();
        } else if(e.deltaX < 0 || e.deltaY > 0){
            flkty.previous();
        }
    }

    clearTimeout(window.wheeling);
    window.wheeling = setTimeout(function() {
        // console.log('stop wheeling!');

        delete window.wheeling;

        // reset wheeldelta
        window.wheeldelta.x = 0;
        window.wheeldelta.y = 0;
    }, 250);

    window.wheeldelta.x += e.deltaFactor * e.deltaX;
    window.wheeldelta.y += e.deltaFactor * e.deltaY;
    if(window.wheeldelta.x > 500 || window.wheeldelta.y > 500 || window.wheeldelta.x < -500 || window.wheeldelta.y < -500){
        window.wheeldelta.x = 0;
        window.wheeldelta.y = 0;

        if(e.deltaX > 0 || e.deltaY < 0){
            flkty.next();
        } else if(e.deltaX < 0 || e.deltaY > 0){
            flkty.previous();
        }
    }

    // console.log(window.wheeldelta);

});

Found this approach over here: http://stackoverflow.com/questions/3515446/jquery-mousewheel-detecting-when-the-wheel-stops

iamstiil avatar Mar 05 '15 12:03 iamstiil

+1

damienroche avatar Mar 10 '15 21:03 damienroche

I'm presently hacking my way through this, trying to engage Flickity's lovely touch/click behavior for horizontal scrolling. The best discussion about relevant issues seems to be this issue on the jQuery mousewheel plugin: https://github.com/jquery/jquery-mousewheel/issues/36

gerwitz avatar Mar 15 '15 10:03 gerwitz

+1 This feature would make me a very happy human.

trstn-c avatar Mar 27 '15 06:03 trstn-c

+1

StefanEndress avatar Apr 10 '15 12:04 StefanEndress

Thanks @pavethiran, the approach you are recommending is really interesting, but it only scrolls to the next cell/element. Do you have an idea how to do an actual free mouse scroll with it. Would be so useful, because flickity would cover then almost every possible interaction with the user.

StefanEndress avatar Apr 14 '15 08:04 StefanEndress

@gerwitz hello. You can try our wheel-indicator to implement wheel support for gallery.

f0rmat1k avatar May 29 '15 11:05 f0rmat1k

+1

tomagladiator avatar Oct 02 '15 16:10 tomagladiator

+1

brandexponents avatar Oct 12 '15 08:10 brandexponents

+1

akisvolanis avatar Nov 03 '15 13:11 akisvolanis

+1

nevernotsean avatar Dec 07 '15 21:12 nevernotsean

+1 :)

hybridvision avatar Dec 27 '15 22:12 hybridvision

+1

stevemckinney avatar Jan 13 '16 08:01 stevemckinney

After much searching I found the functionality I was looking for in iDangerous' Swiper: https://github.com/nolimits4web/Swiper - it's also beautifully written and well documented so I'd recommend checking it out :)

hybridvision avatar Jan 13 '16 10:01 hybridvision

If this is still and issue then this might help someone: https://iamsteve.me/blog/entry/enhancing-horizontal-scrolling-with-flickity-js

Sjael avatar Apr 26 '16 00:04 Sjael

I have decided to not add this feature. Mouse wheel are most commonly used for vertical scrolling. Flickity is horizontal. Adding this feature would add additional complexity for a small feature. Sorry to say no here!

desandro avatar Jun 21 '16 22:06 desandro

For people who want to "hack in" mousewheel support, this was my approach:

import wheel from 'wheel';
import normalizeWheel from 'normalize-wheel';

wheel.addWheelListener(slider.$element[0], event => {
    const wheelNormalized = normalizeWheel(event);
    slider.applyForce(-wheelNormalized.pixelY / 4);
    slider.startAnimation();
    slider.dragEnd();
});

This uses Flickity's internal force, animation and drag functions to perform slider movement and therefore correctly handles slide indexes, freescroll, friction etc.

Note that this uses the pixelY property of the normalized wheel event. So it registers any vertical mouse scrolling as horizontal movement and does nothing with any horizontal mouse scrolling.

Also note that the slider.dragEnd() call fires the 'dragEnd' event with undefined parameters.

This code example uses NPM packages and arrow functions. If you don't want to use NPM packages or arrow functions, use the following piece of code after loading MDN's cross browser event listener for wheel events and Facebook's fixed-data-table normalizeWheel function yourself:

addWheelListener(slider.$element[0], function (event) {
  var wheelNormalized = normalizeWheel(event);
  slider.applyForce(-wheelNormalized.pixelY / 4);
  slider.startAnimation();
  slider.dragEnd();
});

Where slider again is the Flickity instance.

UPDATE January 18th 2018: Added velocity normalization across browsers

LuudJanssen avatar Jan 18 '18 12:01 LuudJanssen

People keep asking for this feature, so I'm re-opening.

Add a 👍 reaction to this issue if you would like to see this feature added. Do not add +1 comments — They will be deleted.

desandro avatar Jan 24 '19 19:01 desandro

For people who want to "hack in" mousewheel support, this was my approach:

import wheel from 'wheel';
import normalizeWheel from 'normalize-wheel';

wheel.addWheelListener(slider.$element[0], event => {
    const wheelNormalized = normalizeWheel(event);
    slider.applyForce(-wheelNormalized.pixelY / 4);
    slider.startAnimation();
    slider.dragEnd();
});

This uses Flickity's internal force, animation and drag functions to perform slider movement and therefore correctly handles slide indexes, freescroll, friction etc.

Note that this uses the pixelY property of the normalized wheel event. So it registers any vertical mouse scrolling as horizontal movement and does nothing with any horizontal mouse scrolling.

Also note that the slider.dragEnd() call fires the 'dragEnd' event with undefined parameters.

This code example uses NPM packages and arrow functions. If you don't want to use NPM packages or arrow functions, use the following piece of code after loading MDN's cross browser event listener for wheel events and Facebook's fixed-data-table normalizeWheel function yourself:

addWheelListener(slider.$element[0], function (event) {
  var wheelNormalized = normalizeWheel(event);
  slider.applyForce(-wheelNormalized.pixelY / 4);
  slider.startAnimation();
  slider.dragEnd();
});

Where slider again is the Flickity instance.

UPDATE January 18th 2018: Added velocity normalization across browsers

Anyway to do this without Jquery? Trying to codepen this and having no luck...

joebentaylor avatar Jul 02 '19 12:07 joebentaylor

Anyway to do this without Jquery? Trying to codepen this and having no luck...

@joebentaylor, my hack isn't using jQuery if I'm correct. What issue are you encountering?

LuudJanssen avatar Jul 03 '19 07:07 LuudJanssen

@LuudJanssen

i was looking for something like this for a full width/height slider, thank you! i did some user tests and found out people using safari on apple laptops were likely to scroll horizontally using the trackpad, here is the workaround I've came with

   const wheelNormalized = normalizeWheel(event);
          const highestValue = Math.abs(wheelNormalized.pixelY) >= Math.abs(wheelNormalized.pixelX) ?
            wheelNormalized.pixelY : wheelNormalized.pixelX * 0.75
          this.flkty.applyForce(-highestValue / 8);
          this.flkty.startAnimation();
          this.flkty.dragEnd();

in addition to your code, it performs a simple check for highest value on x or y axis scroll and use this value, i did not try it on iOs though

LeoSeyers avatar Jul 25 '19 13:07 LeoSeyers

Anyway to do this without Jquery? Trying to codepen this and having no luck...

@joebentaylor, my hack isn't using jQuery if I'm correct. What issue are you encountering?

I guess im confused as to what $element is

joebentaylor avatar Aug 05 '19 16:08 joebentaylor

After running the code i get this error TypeError: Cannot read property '0' of undefined

joebentaylor avatar Aug 05 '19 16:08 joebentaylor

@joebentaylor adding $ before a variable is a naming convention for DOM selectors

if the method is not defined then probably the library is not exectuded the right way

finally you can replace slider.$element[0] by a simple DOM element query such as document.querySelector('.carousel') (adapt the code if you have multiple sliders, the original code assume that slider.$element is an available array, it wasn't in my case either)

LeoSeyers avatar Aug 05 '19 16:08 LeoSeyers

@LeoSeyers Im confused as to what element needs to be though, we are replacing slider with our slider name / reference, so whats $element supposed to reference? the slides?

joebentaylor avatar Aug 05 '19 16:08 joebentaylor