Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

virtual-scroll

Package Overview
Dependencies
Maintainers
1
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

virtual-scroll - npm Package Compare versions

Comparing version 1.5.2 to 2.0.0

lib/virtualscroll.js

20

package.json
{
"name": "virtual-scroll",
"version": "1.5.2",
"description": "Smooth fake scroll using CSS transform",
"main": "src/index.js",
"version": "2.0.0",
"description": "Custom scroll events for smooth, fake scroll",
"main": "lib/virtualscroll.js",
"module": "src/index.js",
"source": "src/index.js",
"scripts": {
"test": "browserify test/index.js | tap-closer | smokestack | faucet",
"build": "microbundle build -i src/index.js --format umd --compress --no-sourcemap --no-pkg-main --external none",
"test": "browserify test/index.js | smokestack | faucet",
"test-debug": "budo test/index.js --live"

@@ -19,3 +22,3 @@ },

],
"author": "= <=>",
"author": "Florian Morel",
"license": "MIT",

@@ -27,6 +30,3 @@ "bugs": {

"dependencies": {
"bindall-standalone": "^1.0.5",
"lethargy": "^1.0.2",
"object-assign": "^4.0.1",
"tiny-emitter": "^1.0.0"
"tiny-emitter": "^2.1.0"
},

@@ -37,4 +37,4 @@ "devDependencies": {

"faucet": "0.0.1",
"microbundle": "^0.13.0",
"smokestack": "^3.4.1",
"tap-closer": "^1.0.0",
"tape": "^4.6.3",

@@ -41,0 +41,0 @@ "tiny-trigger": "scottcorgan/tiny-trigger"

virtual-scroll
=====
Custom scroll event with inertia/momentum, touch (works on <=iOS7) and keyboard compatible.
This is a fork of Bartek Drozdz VirtualScroll util. See his [article](http://www.everyday3d.com/blog/index.php/2014/08/18/smooth-scrolling-with-virtualscroll/) for a complete description.
A **2kb gzipped** low-level library to create custom scrollers with touch and keyboard support.
This is heavily inspired by Bartek Drozdz VirtualScroll util. See his [article](http://www.everyday3d.com/blog/index.php/2014/08/18/smooth-scrolling-with-virtualscroll/) for reference.
### Goals of the fork
- Easier to add in a CommonJS / require environment
- Enable to create several distinct instances by using a prototype rather than a singleton
- Add some extra features
- modules (using a dedicated Emitter for instance)
### Features
- Can create multiple instances with different elements as targets
- Let you do the actual scrolling logic: use CSS Transforms, WebGL animation or anything you like
- Native arrow keys support and shift/space support mimicking default browser behaviour
For high-level libraries based off **virtual-scroll**, check [locomotive-scroll](https://github.com/locomotivemtl/locomotive-scroll) or [smooth-scrolling](https://github.com/baptistebriel/smooth-scrolling).
### Installation

@@ -19,15 +20,24 @@ ```

### Usage & API
For in-depth usage and tutorial, you can check Bartek's article (link above).
#### Constructor
- `new VirtualScroll(options)`
Return a new instance of VirtualScroll. See the options below.
- `el`: the target element for mobile touch events. *Defaults to window.*
- `mouseMultiplier`: General multiplier for all mousewheel (including Firefox). *Default to 1.*
- `touchMultiplier`: Mutiply the touch action by this modifier to make scroll faster than finger movement. *Defaults to 2.*
- `firefoxMultiplier`: Firefox on Windows needs a boost, since scrolling is very slow. *Defaults to 15.*
- `keyStep`: How many pixels to move with each key press. *Defaults to 120.*
- `preventTouch`: If true, automatically call `e.preventDefault` on touchMove. *Defaults to false.*
- `unpreventTouchClass`: Elements with this class won't `preventDefault` on touchMove. For instance, useful for a scrolling text inside a VirtualScroll-controled element. *Defaults to `vs-touchmove-allowed`*.
- `passive`: if used, will use [passive events declaration](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Improving_scrolling_performance_with_passive_listeners) for the wheel and touch listeners. Can be true or false. *Defaults to undefined.*
- `useKeyboard`: if true, allows to use arrows to navigate, and space to jump from one screen. *Defaults to true*
- `useTouch`: if true, uses touch events to simulate scrolling. *Defaults to true*
- `instance.on(fn, context)`
Listen to the scroll event using the specified function (fn) and optional context.
#### Methods
- `instance.on(callback, context)`
Listen to the scroll event using the specified callback and optional context.
- `instance.off(fn, context)`
- `instance.off(callback, context)`
Remove the listener.
- `instance.destroy()`
Will remove all events and unbind the DOM listeners.
Remove all events and unbind the DOM listeners.

@@ -37,16 +47,26 @@ Events note:

### Options
- el: the target element for mobile touch events. *Defaults to window.*
- mouseMultiplier: General multiplier for all mousewheel (including Firefox). *Default to 1.*
- touchMultiplier: Mutiply the touch action by this modifier to make scroll faster than finger movement. *Defaults to 2.*
- firefoxMultiplier: Firefox on Windows needs a boost, since scrolling is very slow. *Defaults to 15.*
- keyStep: How many pixels to move with each key press. *Defaults to 120.*
- preventTouch: If true, automatically call `e.preventDefault` on touchMove. *Defaults to false.*
- unpreventTouchClass: Elements with this class won't `preventDefault` on touchMove. For instance, useful for a scrolling text inside a VirtualScroll-controled element. *Defaults to `vs-touchmove-allowed`*.
- limitInertia: if true, will leverage [Lethargy](https://github.com/d4nyll/lethargy) to avoid everlasting scroll events (mostly on Apple Mouse, Trackpad, and free-wheel mouse). *Defaults to false.*
- passive: if used, will use [passive events declaration](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Improving_scrolling_performance_with_passive_listeners) for the wheel and touch listeners. Can be true or false. *Defaults to undefined.*
- useKeyboard: if true, allows to use arrows to navigate, and space to jump from one screen. *Defaults to true*
- useTouch: if true, uses touch events to simulate scrolling. *Defaults to true*
#### Event
When a scroll event happens, all the listeners attached with *instance.on(callback, context)* will get triggered with the following event:
```js
{
x, // total distance scrolled on the x axis
y, // total distance scrolled on the y axis
deltaX, // distance scrolled since the last event on the x axis
deltaY, // distance scrolled since the last event on the y axis
originalEvent // the native event triggered by the pointer device or keyboard
}
```
### Example
```js
import VirtualScroll from 'virtual-scroll'
const scroller = new VirtualScroll()
scroller.on(event => {
wrapper.style.transform = `translateY(${event.y}px)`
})
```
### License
MIT.

@@ -1,221 +0,264 @@

'use strict';
import Emitter from 'tiny-emitter'
import { support } from './support'
import { keyCodes } from './keycodes'
var objectAssign = require('object-assign');
var Emitter = require('tiny-emitter');
var Lethargy = require('lethargy').Lethargy;
var support = require('./support');
var clone = require('./clone');
var bindAll = require('bindall-standalone');
var EVT_ID = 'virtualscroll';
const EVT_ID = 'virtualscroll'
module.exports = VirtualScroll;
export default class VirtualScroll {
#options
#el
#emitter
#event
#touchStart
#bodyTouchAction
var keyCodes = {
LEFT: 37,
UP: 38,
RIGHT: 39,
DOWN: 40,
SPACE: 32
};
constructor(options) {
// Make sure these events listeners have the proper context (for both .addEventListener and .removeEventListener)
bindThis([
'_onWheel',
'_onMouseWheel',
'_onTouchStart',
'_onTouchMove',
'_onKeyDown'
], this)
function VirtualScroll(options) {
bindAll(this, '_onWheel', '_onMouseWheel', '_onTouchStart', '_onTouchMove', '_onKeyDown');
this.#el = window
if (options && options.el) {
this.#el = options.el
delete options.el
}
this.#options = Object.assign(
{
mouseMultiplier: 1,
touchMultiplier: 2,
firefoxMultiplier: 15,
keyStep: 120,
preventTouch: false,
unpreventTouchClass: 'vs-touchmove-allowed',
useKeyboard: true,
useTouch: true
},
options
)
this.el = window;
if (options && options.el) {
this.el = options.el;
delete options.el;
this.#emitter = new Emitter()
this.#event = {
y: 0,
x: 0,
deltaX: 0,
deltaY: 0
}
this.#touchStart = {
x: null,
y: null
}
this.#bodyTouchAction = null
if (this.#options.passive !== undefined) {
this.listenerOptions = { passive: this.#options.passive }
}
}
this.options = objectAssign({
mouseMultiplier: 1,
touchMultiplier: 2,
firefoxMultiplier: 15,
keyStep: 120,
preventTouch: false,
unpreventTouchClass: 'vs-touchmove-allowed',
limitInertia: false,
useKeyboard: true,
useTouch: true
}, options);
if (this.options.limitInertia) this._lethargy = new Lethargy();
_notify(e) {
var evt = this.#event
evt.x += evt.deltaX
evt.y += evt.deltaY
this._emitter = new Emitter();
this._event = {
y: 0,
x: 0,
deltaX: 0,
deltaY: 0
};
this.touchStartX = null;
this.touchStartY = null;
this.bodyTouchAction = null;
if (this.options.passive !== undefined) {
this.listenerOptions = {passive: this.options.passive};
this.#emitter.emit(EVT_ID, {
x: evt.x,
y: evt.y,
deltaX: evt.deltaX,
deltaY: evt.deltaY,
originalEvent: e
})
}
}
VirtualScroll.prototype._notify = function(e) {
var evt = this._event;
evt.x += evt.deltaX;
evt.y += evt.deltaY;
_onWheel(e) {
var options = this.#options
var evt = this.#event
this._emitter.emit(EVT_ID, {
x: evt.x,
y: evt.y,
deltaX: evt.deltaX,
deltaY: evt.deltaY,
originalEvent: e
});
};
// In Chrome and in Firefox (at least the new one)
evt.deltaX = e.wheelDeltaX || e.deltaX * -1
evt.deltaY = e.wheelDeltaY || e.deltaY * -1
VirtualScroll.prototype._onWheel = function(e) {
var options = this.options;
if (this._lethargy && this._lethargy.check(e) === false) return;
var evt = this._event;
// for our purpose deltamode = 1 means user is on a wheel mouse, not touch pad
// real meaning: https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent#Delta_modes
if (support.isFirefox && e.deltaMode === 1) {
evt.deltaX *= options.firefoxMultiplier
evt.deltaY *= options.firefoxMultiplier
}
// In Chrome and in Firefox (at least the new one)
evt.deltaX = e.wheelDeltaX || e.deltaX * -1;
evt.deltaY = e.wheelDeltaY || e.deltaY * -1;
evt.deltaX *= options.mouseMultiplier
evt.deltaY *= options.mouseMultiplier
// for our purpose deltamode = 1 means user is on a wheel mouse, not touch pad
// real meaning: https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent#Delta_modes
if(support.isFirefox && e.deltaMode == 1) {
evt.deltaX *= options.firefoxMultiplier;
evt.deltaY *= options.firefoxMultiplier;
this._notify(e)
}
evt.deltaX *= options.mouseMultiplier;
evt.deltaY *= options.mouseMultiplier;
_onMouseWheel(e) {
var evt = this.#event
this._notify(e);
};
// In Safari, IE and in Chrome if 'wheel' isn't defined
evt.deltaX = e.wheelDeltaX ? e.wheelDeltaX : 0
evt.deltaY = e.wheelDeltaY ? e.wheelDeltaY : e.wheelDelta
VirtualScroll.prototype._onMouseWheel = function(e) {
if (this.options.limitInertia && this._lethargy.check(e) === false) return;
this._notify(e)
}
var evt = this._event;
_onTouchStart(e) {
var t = e.targetTouches ? e.targetTouches[0] : e
this.#touchStart.x = t.pageX
this.#touchStart.y = t.pageY
}
// In Safari, IE and in Chrome if 'wheel' isn't defined
evt.deltaX = (e.wheelDeltaX) ? e.wheelDeltaX : 0;
evt.deltaY = (e.wheelDeltaY) ? e.wheelDeltaY : e.wheelDelta;
_onTouchMove(e) {
var options = this.#options
if (
options.preventTouch &&
!e.target.classList.contains(options.unpreventTouchClass)
) {
e.preventDefault()
}
this._notify(e);
};
var evt = this.#event
VirtualScroll.prototype._onTouchStart = function(e) {
var t = (e.targetTouches) ? e.targetTouches[0] : e;
this.touchStartX = t.pageX;
this.touchStartY = t.pageY;
};
var t = e.targetTouches ? e.targetTouches[0] : e
VirtualScroll.prototype._onTouchMove = function(e) {
var options = this.options;
if(options.preventTouch
&& !e.target.classList.contains(options.unpreventTouchClass)) {
e.preventDefault();
}
evt.deltaX = (t.pageX - this.#touchStart.x) * options.touchMultiplier
evt.deltaY = (t.pageY - this.#touchStart.y) * options.touchMultiplier
var evt = this._event;
this.#touchStart.x = t.pageX
this.#touchStart.y = t.pageY
var t = (e.targetTouches) ? e.targetTouches[0] : e;
this._notify(e)
}
evt.deltaX = (t.pageX - this.touchStartX) * options.touchMultiplier;
evt.deltaY = (t.pageY - this.touchStartY) * options.touchMultiplier;
_onKeyDown(e) {
var evt = this.#event
evt.deltaX = evt.deltaY = 0
var windowHeight = window.innerHeight - 40
this.touchStartX = t.pageX;
this.touchStartY = t.pageY;
switch (e.keyCode) {
case keyCodes.LEFT:
case keyCodes.UP:
evt.deltaY = this.#options.keyStep
break
this._notify(e);
};
case keyCodes.RIGHT:
case keyCodes.DOWN:
evt.deltaY = -this.#options.keyStep
break
case keyCodes.SPACE && e.shiftKey:
evt.deltaY = windowHeight
break
case keyCodes.SPACE:
evt.deltaY = -windowHeight
break
default:
return
}
VirtualScroll.prototype._onKeyDown = function(e) {
var evt = this._event;
evt.deltaX = evt.deltaY = 0;
var windowHeight = window.innerHeight - 40
this._notify(e)
}
switch(e.keyCode) {
case keyCodes.LEFT:
case keyCodes.UP:
evt.deltaY = this.options.keyStep;
break;
_bind() {
if (support.hasWheelEvent) {
this.#el.addEventListener(
'wheel',
this._onWheel,
this.listenerOptions
)
}
case keyCodes.RIGHT:
case keyCodes.DOWN:
evt.deltaY = - this.options.keyStep;
break;
case keyCodes.SPACE && e.shiftKey:
evt.deltaY = windowHeight;
break;
case keyCodes.SPACE:
evt.deltaY = - windowHeight;
break;
default:
return;
}
if (support.hasMouseWheelEvent) {
this.#el.addEventListener(
'mousewheel',
this._onMouseWheel,
this.listenerOptions
)
}
this._notify(e);
};
if (support.hasTouch && this.#options.useTouch) {
this.#el.addEventListener(
'touchstart',
this._onTouchStart,
this.listenerOptions
)
this.#el.addEventListener(
'touchmove',
this._onTouchMove,
this.listenerOptions
)
}
VirtualScroll.prototype._bind = function() {
if(support.hasWheelEvent) this.el.addEventListener('wheel', this._onWheel, this.listenerOptions);
if(support.hasMouseWheelEvent) this.el.addEventListener('mousewheel', this._onMouseWheel, this.listenerOptions);
if (support.hasPointer && support.hasTouchWin) {
this.#bodyTouchAction = document.body.style.msTouchAction
document.body.style.msTouchAction = 'none'
this.#el.addEventListener('MSPointerDown', this._onTouchStart, true)
this.#el.addEventListener('MSPointerMove', this._onTouchMove, true)
}
if(support.hasTouch && this.options.useTouch) {
this.el.addEventListener('touchstart', this._onTouchStart, this.listenerOptions);
this.el.addEventListener('touchmove', this._onTouchMove, this.listenerOptions);
if (support.hasKeyDown && this.#options.useKeyboard) {
document.addEventListener('keydown', this._onKeyDown)
}
}
if(support.hasPointer && support.hasTouchWin) {
this.bodyTouchAction = document.body.style.msTouchAction;
document.body.style.msTouchAction = 'none';
this.el.addEventListener('MSPointerDown', this._onTouchStart, true);
this.el.addEventListener('MSPointerMove', this._onTouchMove, true);
}
_unbind() {
if (support.hasWheelEvent) {
this.#el.removeEventListener('wheel', this._onWheel)
}
if(support.hasKeyDown && this.options.useKeyboard) document.addEventListener('keydown', this._onKeyDown);
};
if (support.hasMouseWheelEvent) {
this.#el.removeEventListener('mousewheel', this._onMouseWheel)
}
VirtualScroll.prototype._unbind = function() {
if(support.hasWheelEvent) this.el.removeEventListener('wheel', this._onWheel);
if(support.hasMouseWheelEvent) this.el.removeEventListener('mousewheel', this._onMouseWheel);
if (support.hasTouch) {
this.#el.removeEventListener('touchstart', this._onTouchStart)
this.#el.removeEventListener('touchmove', this._onTouchMove)
}
if(support.hasTouch) {
this.el.removeEventListener('touchstart', this._onTouchStart);
this.el.removeEventListener('touchmove', this._onTouchMove);
}
if (support.hasPointer && support.hasTouchWin) {
document.body.style.msTouchAction = this.#bodyTouchAction
this.#el.removeEventListener(
'MSPointerDown',
this._onTouchStart,
true
)
this.#el.removeEventListener(
'MSPointerMove',
this._onTouchMove,
true
)
}
if(support.hasPointer && support.hasTouchWin) {
document.body.style.msTouchAction = this.bodyTouchAction;
this.el.removeEventListener('MSPointerDown', this._onTouchStart, true);
this.el.removeEventListener('MSPointerMove', this._onTouchMove, true);
if (support.hasKeyDown && this.#options.useKeyboard) {
document.removeEventListener('keydown', this._onKeyDown)
}
}
if(support.hasKeyDown && this.options.useKeyboard) document.removeEventListener('keydown', this._onKeyDown);
};
on(cb, ctx) {
this.#emitter.on(EVT_ID, cb, ctx)
VirtualScroll.prototype.on = function(cb, ctx) {
this._emitter.on(EVT_ID, cb, ctx);
var events = this.#emitter.e
if (events && events[EVT_ID] && events[EVT_ID].length === 1)
this._bind()
}
var events = this._emitter.e;
if (events && events[EVT_ID] && events[EVT_ID].length === 1) this._bind();
};
off(cb, ctx) {
this.#emitter.off(EVT_ID, cb, ctx)
VirtualScroll.prototype.off = function(cb, ctx) {
this._emitter.off(EVT_ID, cb, ctx);
var events = this.#emitter.e
if (!events[EVT_ID] || events[EVT_ID].length <= 0) this._unbind()
}
var events = this._emitter.e;
if (!events[EVT_ID] || events[EVT_ID].length <= 0) this._unbind();
};
destroy() {
this.#emitter.off()
this._unbind()
}
}
VirtualScroll.prototype.reset = function() {
var evt = this._event;
evt.x = 0;
evt.y = 0;
};
VirtualScroll.prototype.destroy = function() {
this._emitter.off();
this._unbind();
};
function bindThis(fns, ctx) {
fns.forEach(fn => {
ctx[fn] = ctx[fn].bind(ctx)
})
}

@@ -1,13 +0,9 @@

'use strict';
module.exports = (function getSupport() {
return {
hasWheelEvent: 'onwheel' in document,
hasMouseWheelEvent: 'onmousewheel' in document,
hasTouch: ('ontouchstart' in window) || window.TouchEvent || window.DocumentTouch && document instanceof DocumentTouch,
hasTouchWin: navigator.msMaxTouchPoints && navigator.msMaxTouchPoints > 1,
hasPointer: !!window.navigator.msPointerEnabled,
hasKeyDown: 'onkeydown' in document,
isFirefox: navigator.userAgent.indexOf('Firefox') > -1
};
})();
export const support = {
hasWheelEvent: 'onwheel' in document,
hasMouseWheelEvent: 'onmousewheel' in document,
hasTouch: 'ontouchstart' in document,
hasTouchWin: navigator.msMaxTouchPoints && navigator.msMaxTouchPoints > 1,
hasPointer: !!window.navigator.msPointerEnabled,
hasKeyDown: 'onkeydown' in document,
isFirefox: navigator.userAgent.indexOf('Firefox') > -1
}
'use strict';
var test = require('tape');
var VirtualScroll = require('../src/index.js');
var VirtualScroll = require('../');
var trigger = require('tiny-trigger');

@@ -15,3 +15,3 @@

el.style.height = '400px';
el.style.backgroundColor = 'red';
el.style.backgroundColor = 'transparent';
document.body.appendChild(el);

@@ -156,1 +156,7 @@

}
// Just used to close the testing window
test('Wrap up testing', function(assert) {
assert.end()
window.close()
})
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc