Skip to content

Instantly share code, notes, and snippets.

@hatashiro
Last active June 8, 2017 12:34
Show Gist options
  • Save hatashiro/b5202b6131509a46c7e041791036ca86 to your computer and use it in GitHub Desktop.
Save hatashiro/b5202b6131509a46c7e041791036ca86 to your computer and use it in GitHub Desktop.
perfect-scrollbar v1 design draft for public

PS v1 is still in heavy development and everything can be discussed and changed later. It's being developed in the next branch, so please take a look if interested.

API

The following code shows how PS v1 will be used. For the detailed information, some parts are tagged with a number following *. The details are described below with the indexing number.

import ps from 'perfect-scrollbar';

// initialise
const instance = ps('#container', { // *1
  mount: '#scrollbar-mount',
  emulators: [
    ... // *2
  ]
});

// instance management
instance.update(); // *3
instance.destroy(); // *4

// events
instance.addEventListener('reach-start-x', () => { }); // *5

1. Initialisation

The main function will return a perfect-scrollbar instance. Until now, PS assign each initialisation an random ID, and saves its data in a separated object with using the ID as a key. It was good to make PS work similar to how jQuery works, but make its data and event management extremly complicated. Now, everything will be done in this instance, which doesn't depend on weird random IDs or separated data structures.

2. Emulators

The option emulators contains scroll emulators, such as rail clicking and dragging, keyboard, wheel, or touch. I'm thinking about making the emulators as separated NPM modules, which make it easy for contributors to maintain each emulator, and also make users to create and use their own scroll emulators.

The option's value will have a type Array<?> but the ? is not decided yet. When using String for each emulator, it will work in the similar way to how Webpack loads loaders.

ps('#container', {
  mount: '#mount',
  emulators: ['wheel', 'touch'],
});

The code above will load ps-emulator-wheel and ps-emulator-touch for example. But the problem is when we need to specify options for each emulator, for example stopPropagate for wheel.

ps('#container', {
  mount: '#mount',
  emulators: [
    ['wheel', { stopPropagate: false }], // eslint style
    'touch',
  ],
});

The code above doesn't look so consistent to me.

Another way is to use the module itself.

import wheel from 'ps-emulator-wheel';
import touch from 'ps-emulator-touch';

ps('#container', {
  mount: '#mount',
  emulators: [
    wheel({ stopPropagate: false }),
    touch, // the same as touch()
  ],
});

The code above looks okay to me, but it's a little bit verbose.

If you have any idea or suggestion, please feel free to leave a comment here or on the PS v1 issue.

3. Update

The update method works quite similarly to how it has been worked. It can still be called manually as we've done so far, but it will automatically be called when possible.

  1. MutationObserver will be watching a container element.
    A case an observer can't observe is computed style changes. For example, when geometry is changed because of the change of parent's className, viewport, etc. These indirect changes cannot be caught by the observer and users should call update manually.
  2. scrollTop and scrollLeft changes are captured by scroll event.

Also, it won't manipulate scrollbar geometry directly. It will only set a flag dirty to true. The actual manipulation will be done in a separated event loop, fired by requestAnimationFrame, only when dirty is true.

It will make PS easier to maintain, as well as performant.

4. Destroy

The destroy process consists of the following subprocesses.

  • Destroy emulators
  • Destroy MutationObserver
  • Destroy requestAnimationFrame event loop

5. Events

Now, the PS instance will work as a EventEmitter. It makes it easy to manage events under an instance, without polluting the original element's event namespace.

Rendering flow

rendering flow

@hatashiro
Copy link
Author

Todos

  • implement basic instance render cycle
  • implement render() to actually calculate scrollbar geometries
  • add custom events
  • implement destroy()
  • scroll emulator design

@hatashiro
Copy link
Author

hatashiro commented Sep 26, 2016

A quick implementation for the render function: https://jsfiddle.net/pk2f0gsw/2/

@Tigerino
Copy link

Tigerino commented Jun 8, 2017

Hey noraesae,

any update on when you plan to implement this concept? Are you still working on it, or is it on hold? I really do like your library, yet, I would love to see the requestAnimationFrame changes in.

cheers
Tigerino

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