Skip to content

[feature] Scroll speed according to distance from edge #1907

Description

@koraykural

Currently scroll speed is either 0 or the specified scrollSpeed if dragged item is near to edge.

Requested feature is that scroll speed will increase gradually according to the distance to the edge. If it's closer, it will be faster.

This can be done probably with scrollFn property but it would be much better if there were some built in functions like 'linear', 'ease-in', 'ease-out' etc.

Activity

  1. gap777 commented on Mar 23, 2023

    @gap777

    @koraykural Can you post your work-around implementation of scrollFn?

  2. koraykural commented on Mar 23, 2023

    @koraykural
    Author

    @koraykural Can you post your work-around implementation of scrollFn?

    Sorry, I could not find it. It was an old project.

  3. jongmin4943 commented on Apr 4, 2023

    @jongmin4943

    @gap777 Well, I made something work-around implementation that scroll speed increases gradually. It also considers the mouse position outside of window. If u need it, just adjust some codes below

    const onStart = (e) => {
      startScrollFn(e);
    };
    const onEnd = (e) => {
      initScrollVars();
    };
    
    type Direction = "up" | "down" | "right" | "left";
    let pointerId: number;
    let targetScrollEl: HTMLElement | null;
    let lastPointerEvent: PointerEvent;
    let interval;
    let scrollSpeed = 3;
    const speedIncreaseFactor = 0.001;
    let lastMoveDirection: Direction;
    const initScrollSpeed = () => (scrollSpeed = 3);
    const onMouseMove = (event: PointerEvent) => {
      if (targetScrollEl) {
        lastPointerEvent = event;
      }
    };
    
    const startScrollFn = (e) => {
      const context = e.from as HTMLDivElement;
      pointerId = e.originalEvent.pointerId;
      targetScrollEl = getClosestScrollableEl(context);
      if (targetScrollEl) {
        targetScrollEl.setPointerCapture(pointerId);
        targetScrollEl.addEventListener("pointermove", onMouseMove);
        clearInterval(interval);
        interval = setInterval(() => {
          scrollFn(lastPointerEvent, targetScrollEl);
        });
      }
    };
    
    const initScrollVars = () => {
      if (targetScrollEl?.hasPointerCapture(pointerId)) {
        targetScrollEl?.releasePointerCapture(pointerId);
      }
      targetScrollEl?.removeEventListener("pointermove", onMouseMove);
      targetScrollEl = null;
      initScrollSpeed();
      clearInterval(interval);
    };
    
    const scrollFn = (e: PointerEvent, container: HTMLElement | null) => {
      if (!container || !e) return false;
    
      targetScrollEl = container;
      const { moveDirection, x, y } = getScrollResult(container, e);
    
      container.scrollBy(x, y);
      if (e !== lastPointerEvent) {
        scrollSpeed = round(scrollSpeed + speedIncreaseFactor, 3);
      }
      if (moveDirection !== lastMoveDirection) {
        initScrollSpeed();
      }
      if (moveDirection) lastMoveDirection = moveDirection;
      return false;
    };
    
    const getScrollResult = (container: HTMLElement, e: PointerEvent) => {
      let moveDirection: Direction | null = null;
      let x = 0;
      let y = 0;
      const scrollSensitivity = props.option?.scrollSensitivity || 50;
      const { top, bottom, left, right } = container.getBoundingClientRect();
      const topDistance = e.clientY - top;
      const bottomDistance = bottom - e.clientY;
      const leftDistance = e.clientX - left;
      const rightDistance = right - e.clientX;
      if (topDistance < scrollSensitivity) {
        y = (-(scrollSensitivity - topDistance) / scrollSensitivity) * scrollSpeed;
        moveDirection = "up";
      } else if (bottomDistance < scrollSensitivity) {
        y =
          ((scrollSensitivity - bottomDistance) / scrollSensitivity) * scrollSpeed;
        moveDirection = "down";
      }
      if (leftDistance < scrollSensitivity) {
        x = (-(scrollSensitivity - leftDistance) / scrollSensitivity) * scrollSpeed;
        moveDirection = "left";
      } else if (rightDistance < scrollSensitivity) {
        x = ((scrollSensitivity - rightDistance) / scrollSensitivity) * scrollSpeed;
        moveDirection = "right";
      }
      return { moveDirection, x, y };
    };

    FYI, It's actually for Vue components and used customized Sortable so some function's parameters might be different from original. You also have to call the method scrollFn in above code snippet when Sortable calls original scrollFn too.

    <draggable
      :scroll-fn="(e, container)=>scrollFn(e, container)"
    >
    </draggable>

    something like this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions