Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ Feature Request ] useSmoothScrolling #98

Open
waffleflopper opened this issue Jul 31, 2023 · 2 comments
Open

[ Feature Request ] useSmoothScrolling #98

waffleflopper opened this issue Jul 31, 2023 · 2 comments

Comments

@waffleflopper
Copy link

For a project I was doing I wanted all my users to have a nice scrolling experience - like macOS users have when using native hardware (magic mouse, trackpad). It's always bothered me how rough it can feel on windows or using a 3rd party mouse. So I wrote this action and thought I would see if it's something people are interested in. I'm not very familiar with Svelte-Legos, so I didn't want to just fork and open a pull request out of nowhere. Was sent here from a different library project when I asked if they were interested in the action.

/* helper */
function clamp(num: number, min: number, max: number): number {
    return Math.min(Math.max(num, min), max);
}

/* useSmoothScrolling */
interface SmoothScrollOptions {
  friction?: number;
  maxSpeed?: number;
  deadZone?: number;
}

//friction around 0.94-0.95 feels the best, imo (behaves oddly outside of 0.9 to 0.96)
//lower max speed does weird things if users mouse wheel is set to scroll a lot of lines at once
//deadZone is to prevent run away scrolling from small scroll movements
export default function useSmoothScrolling(node: HTMLElement, params: SmoothScrollOptions) {
    const {friction = 0.94, maxSpeed = 60, deadZone = 0.5} = params;

    //todo: map 1-100 to 0.9 to 0.96 to make friction more readable
    let clampedFriction = clamp(friction, 0.9, 0.96);
    
    let velocity = 0;
    const wheelHandler = (e: WheelEvent) => {
        velocity += e.deltaY * 0.1;
        velocity = clamp(velocity, -maxSpeed, maxSpeed);
    };

    const animationHandler = () => {
            if (Math.abs(velocity) > deadZone) {
        velocity *= clampedFriction;
        node.scrollTop += velocity;
      } else {
        velocity = 0;
      }
      frameId = window.requestAnimationFrame(animationHandler);
    };

    node.addEventListener('wheel', wheelHandler, { passive: true });
    let frameId = window.requestAnimationFrame(animationHandler);

    return {
      destroy() {
        node.removeEventListener('wheel', wheelHandler);
        window.cancelAnimationFrame(frameId);
      }
    };
}

Link to Gist

If it's something people are interested in I'm happy to refactor it and make a more robust implementation.

@ankurrsinghal
Copy link
Owner

Hey @waffleflopper thanks for the idea. Totally if you can create a proper implementation and raise a PR it will be helpful, :).

@waffleflopper
Copy link
Author

Been without internet for a while (Army stuff for upcoming deployment), but I'll get something put together. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants