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.
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
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.
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.
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.
- 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'sclassName
, viewport, etc. These indirect changes cannot be caught by the observer and users should callupdate
manually. scrollTop
andscrollLeft
changes are captured byscroll
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.
The destroy process consists of the following subprocesses.
- Destroy emulators
- Destroy
MutationObserver
- Destroy
requestAnimationFrame
event loop
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.
Todos
render()
to actually calculate scrollbar geometriesdestroy()