Giter Site home page Giter Site logo

ilyashubin / scrollbooster Goto Github PK

View Code? Open in Web Editor NEW
980.0 10.0 80.0 2.71 MB

Enjoyable content drag-to-scroll library

Home Page: https://ilyashubin.github.io/scrollbooster

License: MIT License

HTML 31.16% JavaScript 68.21% CSS 0.63%
drag draggable scroll scrollable scroll-library library ui ui-components

scrollbooster's People

Contributors

alexwidua avatar alirezamirian avatar bragovo avatar bramdoppen avatar dependabot[bot] avatar homerjam avatar ilyashubin avatar joshdavenport avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

scrollbooster's Issues

Allow inputFocus on [contenteditable]

Currently any elements that should be able to receive input because of the contentEditable property are not focusable. Can we get support for those elements as well?

Implement horizontal scrolling using mouse wheel + smooth scrolling doesn't work using mouse wheel

Hello,

This library is capable of implementing horizontal scrolling using mouse wheel, but there is currently no way to do this.

I managed to get it to work simply by changing deltaX to deltaY in here:

this.scrollOffset.x = -event.deltaX;

However, when I did this, I noticed that the horizontal scrolling via mousewheel does indeed work, but it does not smoothly scroll and instead just snaps in place.

This will be one hell of a powerful library if it can be used for many uses cases like this.

Quick webm showing behaviour: http://i.venner.io/2020-03-12_20-03-12.webm

right click

hello.
thank you for your great project!

I have a suggestion.
How about ignoring right-clicks?(contextmenu event)

If you right-click on the content, the content will always be dragged, even if you release the button.

I don't think there is any disadvantage to ignoring the contextmenu event.

I would be happy if you could consider it.

link drag issue

Hi, Ilya!

If there are links in draggable container, they will be fire click event while dragging.

Doesn't work horizontal scrolling on mousewheel

It doesn't work

new ScrollBooster({
    viewport,
    content,
    direction: 'horizontal',
    emulateScroll: true,
    onUpdate: (state) => {
        content.style.transform = `translateX(${-state.position.x}px)`
    }
})

If you use native scrolling, scrolling is handled by browser itself:

new ScrollBooster({
  viewport,
  content,
  direction: 'horizontal',
  onUpdate: (state) => {
    viewport.scrollLeft = state.position.x;
  }
})

Originally posted by @ilyashubin in #19 (comment)

JsFiddle: https://jsfiddle.net/eq3cntow/93/

Jumpy behavior when dragging with mouse after scrolling with touchpad

I've a horizontal scroll view that can be scrolled my touch, touchpad or by dragging with scrollbooster.
When the scroll view is scrolled using the touchpad and afterwards dragged by scrollbooster, I get jumpy behavior :/

Currently I've worked around it by updating the scrollbooster offset in the shouldScroll method:

//Jumpy behavior workaround
shouldScroll: () => {
        sb.setPosition({x: scrollView.scrollLeft});
        return true
}

Bounce drag amount/offset

There is option to set bounceForce, but how to control bounce offset on drag? Currently you can drag content out of viewport and on release it bounces back to start.
Can you add option to control that offset?

Add Changelog

Hi,

Could you add the changelog for V2 release ? Current I use v1 so just wonder is it ok to upgrade to v2 and what new in v2 ?

Thanks!

'isDragging' is incorrectly read as true when querying via 'getState()'

Hi, first of all thanks for the great library.

I'm currently having an issue where isDragging state is set to true but shouldn't be. This is reproducible when completing a mouse drag in the scrollable area then querying the state without clicking in the scrollable area again.

It seems to be to do with how we determine isDragging state in getState().

isDragging: !!(this.dragOffset.x || this.dragOffset.y),

Instead of using this.isDragging which is set to false on mouseup it relys on dragOffset which doesn't seem to be reset until another pointer event occurs.

Couldn't find any other issues relating to this so I am wondering if I am missing something?

Thanks in advance :)

scrollTo issues

Is it possible to control scrollTo animation scroll duration?

Also, how would one go about controlling max X in scrollTo? It seems like onUpdate is not triggered from within setPosition and my checks there are completely ignored (if(state.position.x < 0) tabsSb.scrollTo({x: 0});). I want to set absolute min / max values that includes scrollTo as well, not just manual scroll.

Thanks!

Position problem.

Hello! I work with your library and find some problems. When i use touch devices like a (iphone, ipad etc) scroll position not updated. All time back to start. How i should solved this problem. On your site demo all works perfect

Missing option to choose between mouse and touch

I'm missing a way to only enable scrollbooster for mouse or touch, currently I use the shouldScroll callback to only make it work with mouse events:

//Only use scrollbooster for mouse interaction
shouldScroll: (data, event) => {
        const isTouch = !!(event.touches && event.touches[0]);
        return !isTouch;
}

'Type not assignable...' when not supplying every option parameter to constructor when using TypeScript JS file checking

We're using TypeScript with JS file checking enabled in our project. What that means is that TypeScript will check JS files for JSDoc definitions and interpret them as types.
scrollbooster's index file contains JSDocs definitions:

image

When I try to create a new ScrollBooster instance with the same options as suggested in the README.md like this:

new ScrollBooster({
    viewport: document.querySelector('.viewport'),
    scrollMode: 'transform'
});

... TypeScript complains about missing parameters:

TS2345: Argument of type '{ viewport: HTMLElement; scrollMode: string; }' is not assignable to parameter of type '{ viewport: Element; content: Element; direction: string; pointerMode: string; scrollMode: string; bounce: boolean; bounceForce: number; friction: number; textSelection: boolean; ... 5 more ...; shouldScroll: Function; }'.
  Type '{ viewport: HTMLElement; scrollMode: string; }' is missing the following properties from type '{ viewport: Element; content: Element; direction: string; pointerMode: string; scrollMode: string; bounce: boolean; bounceForce: number; friction: number; textSelection: boolean; ... 5 more ...; shouldScroll: Function; }': content, direction, pointerMode, bounce, and 9 more.

That's because in the JSDocs comments of ScrollBooster's index file, non-essential parameters are not marked as optional. In JSDocs, you can mark parameters as optional by wrapping the parameter name in brackets ( [] ). When I do that for all the non-essential parameters, TypeScript stops complaining and my example works πŸŽ‰ .

@ilyashubin I wanted to open a PR, but I can't seem to create a branch for this repo.

Internet Explorer 11 - Not working

Hello, thanks for this wonderful plugin. It saved me tons of time.

The last example on demo page is not working on IE11 but its not showing any error in console.

My example doesn't work on Internet explorer 11 and it also throws syntax error in console. I copied the code from your examples.

if ( $('.drag-viewport').length ) {

  let viewport = document.querySelector('.drag-viewport');
  let content = viewport.querySelector('.drag-viewport-content');
  let img = content.querySelector('.cards-grid');

  let sb = new ScrollBooster({
    viewport: viewport,
    content: content,
    emulateScroll: true,
    onUpdate: (data)=> { // syntax error
      content.style.transform = `translate(
        ${-data.position.x}px,
        ${-data.position.y}px
      )`
    },
    // prevent click on drag
    onClick: (data, event) => {
      if (Math.abs(data.dragOffsetPosition.x) > 5) {
        event.stopPropagation();
      }
    },
  })

  // set viewport position to the center of an image
  let offsetX = img.scrollWidth - viewport.offsetWidth
  let offsetY = img.scrollHeight - viewport.offsetHeight
  sb.setPosition({
    x: offsetX / 2,
    y: offsetY / 2
  })

}

Detecting when the scrollable element is at the start or end of the scrollable viewport

Hey!

We want to add navigation arrows on each side of the scrollable viewport that can also trigger scroll (by a predefined amount of pixels). When we reach the end or the start of the scrollable viewport, we would like to hide the corresponding arrow.

Taking a quick look at the code base, it seems like you are already calculating those metrics at applyBoundForce(). Do you think you can add those metrics to the getState method, thus making them available in the onUpdate() callback?

Thanks!

If the scroll target contains textbox or selectbox, you can not focus on those input elements.

I am Japanese. I am sorry if my English is strange.
You can use the focus function in the onClick function to focus on the textbox but can not display the selectbox drop-down list.
Is there a solution?

<script type="text/javascript">
    $(function() {
        $(document).ready(function() {
            let viewport = document.querySelector(".viewport");
            let content = viewport.querySelector(".viewport-content");
            let sb = new ScrollBooster({
                "viewport": viewport,
                "content": content,
                "textSelection": false,
                "friction": 0.5,
                "mode": 'x',
                "onUpdate": (data)=> {
                    viewport.scrollLeft = data.position.x
                },
                "onClick": (data, event) => {
                    // Focus on input element
                    event.target.focus();
                }
            })
        });
    });
</script>


<div class="table-responsive viewport">
    <table class="table table-bordered table-sm text-nowrap viewport-content">
        <thead>
            <tr>
                <th>AAAAAAAAAA</th>
                <th>BBBBBBBBBB</th>
                <th>CCCCCCCCCC</th>
                <th>DDDDDDDDDD</th>
                <th>EEEEEEEEEE</th>
                <th>FFFFFFFFFF</th>
                <th>GGGGGGGGGG</th>
                <th>HHHHHHHHHH</th>
                <th>IIIIIIIIII</th>
                <th>JJJJJJJJJJ</th>
                <th>KKKKKKKKKK</th>
                <th>LLLLLLLLLL</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td><input type="checkbox" class="w-100"></td>
                <td><input type="text" class="w-100"/></td>
                <td><input type="text" class="w-100"/></td>
                <td><input type="text" class="w-100"/></td>
                <td><input type="text" class="w-100"/></td>
                <td><input type="text" class="w-100" readonly="readonly"/></td>
                <td><input type="text" class="w-100" readonly="readonly"/></td>
                <td><input type="text" class="w-100"/></td>
                <td>
                    <select class="w-100">
                        <option value="0">000</option>
                        <option value="1">111</option>
                        <option value="2">222</option>
                        <option value="3">333</option>
                        <option value="4">444</option>
                    </select>
                </td>
                <td>
                    <select class="w-100">
                        <option value="10">10</option>
                        <option value="20">20</option>
                    </select>
                </td>
                <td><input type="text" class="w-100"/></td>
                <td><input type="checkbox"class="w-100"/></td>
            </tr>
        </tbody>
    </table>
</div>

Vertical scroll on mobile

Using the following options:

new ScrollBooster({
    viewport: scrollCanvas,
    scrollMode: 'transform',
    direction: "horizontal'
});

Scrolling the page on the Y axis with drag doesn't work, because the viewport catches the events. The users cannot scroll the page on the Y axis, if the canvas occupies the whole screen on mobile.

I can provide an example of a page where the correct behaviour can be observed (our services section): https://www.instrument.com/what-we-do/

The possible fix is to check the first pointer drag event. If it's not in the same direction as the configured direction, then do nothing. Perhaps a more robust solution is to calculate the angle of the two lines and apply a threshold.

event.touches is undefined

Trying in mobile screen in Firefox, i receive error when trying to scroll.

Version: 3.0.2

  417 | }
  418 | 
> 419 | const eventData = isTouch ? event.touches[0] : event;
      | ^  420 | const { pageX, pageY, clientX, clientY } = eventData;
  421 | 
  422 | this.dragOffset.x = pageX - dragOrigin.x;```

updateMetrics works incorrectly

When scroll position not in (0, 0) and we update the size of the scroll area, updateMetrics function works incorrectly, In the result, we get bigger scrollable area than it actually is

Cannot drag elements

Hi guys,

Same with the title, having issues with dragging if I added the option of bounce set to false:

`js
import React from "react";
import ScrollBooster from "scrollbooster";

import styles from "../styles/Home.module.css";

export default function HomePage() {
const viewportRef = React.useRef();
const contentRef = React.useRef();
let posX = 1;
let posY = 1;
let move = false;

React.useEffect(() => {
    // const [viewport, scrollbooster] = useScrollBoost({
    //     direction: "all",
    //     scrollMode: "transform",
    // });

    const SB = new ScrollBooster({
        viewport: viewportRef.current,
        content: contentRef.current,
        emulateScroll: false,
        bounce: true,
        onUpdate: (data) => {
            posX = Math.ceil(data.position.x);
            posY = Math.ceil(data.position.y);
            contentRef.current.style.transform =
                "translate(" +
                -data.position.x +
                "px, " +
                -data.position.y +
                "px)";
        },
        onClick: (state, event, isTouchDevice) => {
            // prevent default link event
            console.log({ state, event, isTouchDevice });
        },
    });
}, []);

return (
    <div className={styles.container} ref={viewportRef}>
        <div className={styles.content} ref={contentRef}>
            <h2 className={styles.heading}>Drag to scroll</h1>
            <h3>Some Content</h2>
            <h3>Some Content</h2>
            <h3>Some Content</h2>
            <h3>Some Content</h2>
            <h3>Some Content</h2>
            <h3>Some Content</h2>
            <h3>Some Content</h3>
        </div>
    </div>
);

}
`

`css
.container {
min-height: 100vh;
overflow: hidden;
position: relative;
}

.content {
background: #b2e1f8;
color: rgb(27, 82, 202);
width: 100vw;
height: 100vh;
}

.heading {
margin: 0;
}

`

Possible to let scrollTo use translate3d in stead of translate

I have a question > is it possible to let scrollTo use translate3d in stead of translate. We are using this function the whole time. And when we add hover effects (transform) on elements that are in the content area, animations start to lag a bit.

Thanks for this great piece of code by the way πŸ‘

onPointerDown/onPointerUp options not working

It doesn't seem that it's posting a mouseup or mousedown listener. Example:

const wrapper = document.querySelector('.wrapper');
const content = wrapper.querySelector('.content');

new ScrollBooster({
    viewport: wrapper,
    content: content,
    scrollMode: 'transform',
    onPointerDown() {
      wrapper.style.backgroundColor = '#6fd0c7';
    },
    onPointerUp() {
      wrapper.style.backgroundColor = '#6f9ed0';
    },
    onPointerMove() {
      wrapper.style.backgroundColor = 'yellow';
    }
});

Working pen: https://codepen.io/kpagcha/pen/jOmWjJa

setPosition doesn’t smoothly scroll

Is there a way to make setPosition smoothly scroll?

scroll() {
	this.scrollBooster.setPosition({
		x: this.scrollBooster.getUpdate().position.x += 200
	});
}

This is attached to a button that will scroll the content. I tried to maually set the translate:

scroll() {
	contentElement.style.transform = `translateX(${-data.position.x}px)`
}

But this gets overidden instantly and set back to the current position stored with ScollBooster.

Method scrollPosition

Hi, first of all, great work!!
I was working with Scrollbooster and I just noticed that there could be a great addition
scrollPosition
where we could pass a coordinate and the container moves to it in a smooth manner
Similar to - window.scrollBy(x-coord, y-coord);
We are working on it, don't know if with the best perspective, but hopefully, we can show you guys something

Drag to scrool whole website

I try like

new ScrollBooster({
    viewport: $('body')[0],
    scrollMode: 'native',
    textSelection: true,
});

But not work, is possible in this lib ?

Editable field

Hi i have conet editable div is not activated when use scrollbooster, any idea to fix ?

touch tap doesn't work

I have tab elements, and I made them scrollable. I can't change tabs because you made return in toucstart event.

How to get scroll position to use in slider navigation

This is a brilliant plugin, just what I need. I'm using it as a horizontal slider on desktop, but want to show a navigation element below it which goes from 0% to 100% depending on the position of the scroller content.

Anybody know how to return a value from 0% to 100% on scroll?

I've checked out the onUpdate event, and can see the position.x shows 0 at the start, but can't figure out how to get 100%

bumbing when content not fully loaded

I am using Scroll Booster for an image slideshow. I noticed that Scroll Booster bumps, if the images were not fully loaded at the time I start scrolling.

So basically, I load the website only shortly, start scrolling > bug
If I load the website and wait a while before I start scrolling > I can scroll till the end.

<script type="text/javascript">
			jQuery(document).ready(function( $ ){
				let viewport = document.querySelector('#photo_slider')
				let content = viewport.querySelector('.photos')

				let sb = new ScrollBooster({
				  viewport,
				  content,
				  mode: 'x',
				  onUpdate: (data)=> {
					viewport.scrollLeft = data.position.x
				  }
				})
			});
</script>

Please find here the example I am working on. It would be great to get some support on this:
http://boenemann.com/photo/ukraine/

Mac safari 10/11 version not working

Hi Ilyashubin,

It's working well in Chrome, Firefox and safari 12+ but not in mac safari 10/11, facing issues with the package and my application is stable before adding this package.

Even your reference website (https://ilyashubin.github.io/scrollbooster/) also not working in mac safari 10/11 -
Unexpected token '...'. Expected a property name.

Please resolve the issue.

Unable to test component using Scrollbooster

Hi!

I have a component which relies on refs and Scrollbooster (see below). When trying to mount the component with Enzyme I get an error saying:

at processImmediate (timers.js:658:5) ReferenceError: Element is not defined
at new t (/home/vikenfalk/third-party-client/tp-client/node_modules/scrollbooster/dist/webpack:/ScrollBooster/src/index.js:76:70)

If I log the reference that I'm passing to the Scrollbooster it looks fine and identical to what I can see when I run the actual application. The error seems to be related to JSDOM, but I'm wondering if anybody knows a way around this issue so that I can mount the component and test the calculations within the UseEffect function?

const ProgressBarScroller = (props) => {
    const [progressbarWrapperRef, progressBarTaskRef, progressBarRef] = [
        useRef(null),
        useRef(null),
        useRef(null)
    ];
    const getElementWidth = (refName) => refName.current.getBoundingClientRect().width;

    useEffect(() => {
        if (!progressBarTaskRef.current || !progressBarRef.current || !progressbarWrapperRef.current) return;

        const taskWidth = getElementWidth(progressBarTaskRef);
        const progressBarWidth = getElementWidth(progressBarRef);
        const progressbarWrapperWidth = getElementWidth(progressbarWrapperRef);

        const middleOfActiveTask =
            (props.currentTaskIndex + 1) * taskWidth - taskWidth / 2;
        const requestedX = middleOfActiveTask - progressbarWrapperWidth / 2;
        const maxPossibleX = progressBarWidth - progressbarWrapperWidth;


        const scroller = new scrollBooster({
            viewport: progressbarWrapperRef.current,
            direction: 'horizontal',
            scrollMode: 'transform',
            friction: 0.2
        });

        /**
         * returns 0 if requested X position is lower than minimum.
         * returns the highest possible X position if requested X is higher than maximum.
         * Otherwise returns requested X position.
         * @returns {number}
         */
        const getValidXPosition = () => {
            return requestedX <= 0 ? 0
                : requestedX >= maxPossibleX ? maxPossibleX
                    : requestedX;
        };

        (function moveToDefaultPosition() {
            scroller.setPosition({
                x: getValidXPosition(),
                y: 0
            });
        })();

        //Cleans up ScrollBooster on unmount
        return () => {
            scroller.destroy();
        };
    }, []);

    return (
        <div className={'cs-progressbar'} ref={progressbarWrapperRef}>
            <ul className={`cs-progressbar__list`} ref={progressBarRef}>
                {props.tasks.map((task, index) => {
                    return (
                        <ProgressBarTask
                            {...task}
                            key={task.id}
                            innerRef={
                                props.currentTaskIndex === index
                                    ? progressBarTaskRef
                                    : null
                            }
                        />
                    );
                })}
            </ul>
        </div>
    );
};
library version
enzyme 3.11.0
react 16.13.1
react-dom 16.13.1
react-test-renderer 17.0.1
scrollbooster 2.3.0

destroy() is incomplete.

Thanks for this great plugin. First of all, thank you.

I found an event that I couldn't delete with destroy().
The following events cannot be deleted because there was a problem while attaching and removing the scroollBooster.

this.props.content.addEventListener("load",this.events.contentLoad,!0)
this.props.content.removeEventListener("load",this.events.contentLoad)

The cause is that the arguments don't match.

this.props.content.removeEventListener("load",this.events.contentLoad,!0)

Translated with www.DeepL.com/Translator (free version)

click event

it would be great to have some option to avoid clicks when scrolling.

It can be done manually, comparing coordinates in mousedown with coordinates in click, but I think it is a common use case, so I think an option would be better.

scrollTo blocks dragging until animation is finished

I'm working on a project that depends on scrollTo for navigating between specific coordinates on the page. it works smoothly, but I also want the user to be able to stop the animation (triggered by scrollTo) if the user starts to drag and give the control back to the user.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.