Skip to content

Commit

Permalink
Merge pull request #12 from sateffen/develop
Browse files Browse the repository at this point in the history
v1.2.0
  • Loading branch information
sateffen authored Mar 29, 2017
2 parents c7dd191 + c71a316 commit af12bc1
Show file tree
Hide file tree
Showing 5 changed files with 460 additions and 382 deletions.
10 changes: 9 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@
<body>
<div id="container" style="height: 250px;border: 2px solid black;">
<div style="height: 100px;background-color: red;width:111%;"></div>
<div style="height: 100px;background-color: blue;"></div>
<div style="height: 110px" id="inner">
<div style="height: 100px; width: 111%; touch-action: pan-y; background-color: lightblue"></div>
<div style="height: 100px; background-color: blue"></div>
<div style="height: 100px; background-color: darkblue"></div>
</div>
<div style="height: 100px;background-color: green;"></div>
<div style="height: 100px;background-color: yellow;"></div>
</div>
Expand All @@ -36,6 +40,10 @@
xElementClass: ['scrollbar', 'horizontal'],
yElementClass: ['scrollbar', 'vertical']
});
window.scrollerInnerInstance = new window.PocScrollbar(document.querySelector('#inner'), {
xElementClass: ['scrollbar', 'horizontal'],
yElementClass: ['scrollbar', 'vertical']
});
</script>
</body>

Expand Down
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "poc-scrollbar",
"version": "1.1.0",
"version": "1.2.0",
"description": "A simple scrollbar, that just works",
"homepage": "https://github.com/sateffen/poc-scrollbar",
"bugs": "https://github.com/sateffen/poc-scrollbar/issues",
Expand All @@ -25,15 +25,15 @@
},
"license": "MIT",
"devDependencies": {
"eslint": "~3.14.1",
"eslint-config-airbnb-base": "~11.0.1",
"eslint": "~3.18.0",
"eslint-config-airbnb-base": "~11.1.2",
"eslint-plugin-import": "~2.2.0",
"jasmine-core": "~2.5.2",
"karma": "~1.4.1",
"karma": "~1.5.0",
"karma-chrome-launcher": "~2.0.0",
"karma-jasmine": "~1.1.0",
"karma-rollup-plugin": "~0.2.4",
"rollup": "~0.41.4",
"rollup": "~0.41.6",
"rollup-plugin-buble": "~0.15.0",
"rollup-watch": "~3.2.2"
}
Expand Down
72 changes: 62 additions & 10 deletions src/pocscrollbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,52 @@
import { ScrollView } from './scrollview';
import { debounce, getWheelDeltaAsPixel } from './helper';

/**
* Contains all allowed touch-action values for x direction
* @type {Array<string>}
*/
const ALLOWED_X_TOUCH_ACTIONS = ['auto', 'manipulation', 'pan-x'];

/**
* Contains all allowed touch-action values for y direction
* @type {Array<string>}
*/
const ALLOWED_Y_TOUCH_ACTIONS = ['auto', 'manipulation', 'pan-y'];

/**
* A boolean telling about the passive event listening support
* @type {boolean}
*/
const SUPPORTS_PASSIVE = (() => {
let supportsPassive = false;

try {
const opts = Object.defineProperty({}, 'passive', {
get() {
supportsPassive = true;
}
});
window.addEventListener('test', null, opts);
}
catch (e) {
// supportsPassive is false
}

return supportsPassive;
})();

/**
* @typedef {Object} PocScrollbarOptions
* @property {boolean} [aOptions.disableInteractionWithScrollbars=false]
* @property {boolean} [aOptions.disableTouchScrollingOnContainer=false]
* @property {boolean} [aOptions.useMutationObserver=false]
* @property {number} [aOptions.checkInterval=300]
* @property {boolean} [aOptions.disableXScrolling=false]
* @property {boolean} [aOptions.disableYScrolling=false]
* @property {Object} [aOptions.xElementStyles={}]
* @property {Object} [aOptions.xElementStyles={}]
* @property {array.<String>|String} [aOptions.xElementClass=[]]
* @property {array.<String>|String} [aOptions.yElementClass=[]]
* @property {Array.<String>|String} [aOptions.xElementClass=[]]
* @property {Array.<String>|String} [aOptions.yElementClass=[]]
* @property {number} [aOptions.xMinSize]
* @property {number} [aOptions.yMinSize]
* @property {number} [aOptions.wheelDeltaSize]
Expand Down Expand Up @@ -182,23 +217,40 @@ export default class PocScrollbar {
let tmpMoverX = aEvent.touches[touchToTrack].clientX;
let tmpMoverY = aEvent.touches[touchToTrack].clientY;

// read the touch-action from the target element for checking
const touchActionValue = window.getComputedStyle(aEvent.target, null).getPropertyValue('touch-action');
const xPanAllowed = ALLOWED_X_TOUCH_ACTIONS.indexOf(touchActionValue) > -1;
const yPanAllowed = ALLOWED_Y_TOUCH_ACTIONS.indexOf(touchActionValue) > -1;

// then setup a move function pointer
let tmpMovePointer = (aaEvent) => {
// prevented events should not be handled
if (aaEvent.defaultPrevented) {
return;
}

// which only tracks the correct touch
if (aaEvent.which !== touchToTrack) {
return;
}

// calculates the distance
const distanceX = tmpMoverX - aaEvent.touches[touchToTrack].clientX;
const distanceY = tmpMoverY - aaEvent.touches[touchToTrack].clientY;
// check, if the touch is allowed to scroll in x direction
if (xPanAllowed) {
const distanceX = tmpMoverX - aaEvent.touches[touchToTrack].clientX;
this.scrollLeft(this._container.scrollLeft + distanceX);
aaEvent.preventDefault();
}

// check, if the touch is allowed to scroll in y direction
if (yPanAllowed) {
const distanceY = tmpMoverY - aaEvent.touches[touchToTrack].clientY;
this.scrollTop(this._container.scrollTop + distanceY);
aaEvent.preventDefault();
}

// and update the tmp movers
tmpMoverX = aaEvent.touches[touchToTrack].clientX;
tmpMoverY = aaEvent.touches[touchToTrack].clientY;

// and triggers an update for scrollTop and scrollLeft
this.scrollTop(this._container.scrollTop + distanceY);
this.scrollLeft(this._container.scrollLeft + distanceX);
};

// finally setup a pointer to a touchend function handler
Expand All @@ -218,7 +270,7 @@ export default class PocScrollbar {
};

// and finally add the event handlers, so this will actually work correctly
document.body.addEventListener('touchmove', tmpMovePointer);
document.body.addEventListener('touchmove', tmpMovePointer, SUPPORTS_PASSIVE ? { passive: false } : false);
document.body.addEventListener('touchend', tmpEndPointer);
document.body.addEventListener('touchleave', tmpEndPointer);
}
Expand Down
48 changes: 25 additions & 23 deletions src/scrollview.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class ScrollView {
*/
constructor(aParentInstance, aOptions) {
// first save the details about the parent instance and it's container element
this._parent = aParentInstance._container;
this._parentElement = aParentInstance._container;
this._scrollerParent = aParentInstance;
this._options = aOptions;
this._destroyCallbacks = [];
Expand All @@ -24,8 +24,8 @@ export class ScrollView {
// itself. The problem is, that if the user grabs the vertical scrollbar, and drags it
// 10px down the scrollTop changed not only by ten, but 10*scrollHeight/height. This is
// because of the absolute positioning relative to the parent
this._scrollHeightFactor = this._parent.scrollHeight / this._parent.clientHeight;
this._scrollWidthFactor = this._parent.scrollWidth / this._parent.clientWidth;
this._scrollHeightFactor = this._parentElement.scrollHeight / this._parentElement.clientHeight;
this._scrollWidthFactor = this._parentElement.scrollWidth / this._parentElement.clientWidth;

// setup scroll elements
this._xElement = aOptions.disableXScrolling ? null : this._setupElement(true);
Expand Down Expand Up @@ -61,6 +61,7 @@ export class ScrollView {
element.style.top = '0px';
element.style.left = '0px';
element.style.position = 'absolute';
element.style.touchAction = 'none';

applyOptionsToScrollBarElement(element, details.name, this._options);

Expand All @@ -75,9 +76,11 @@ export class ScrollView {
});
}

this._parent.appendChild(element);
this._parentElement.appendChild(element);
this._destroyCallbacks.push(() => {
this._parent.removeChild(element);
if (Array.prototype.indexOf.call(this._parentElement.children, element) >= 0) {
this._parentElement.removeChild(element);
}
});

return element;
Expand All @@ -88,11 +91,11 @@ export class ScrollView {
* Warning: You need to set the this context of this function to the scrollView instance you're working with!
*
* @param {string} aAttribute The attribute to use from the event for calculation
* @param {string} aPropertyFactor The factor for scroll top and left to compensate for normal distances
* @param {string} aScaleFactor The factor for scroll top and left to compensate for normal distances
* @param {string} aParentWriteCallback The name for the callback where to write to
* @return {Object} An object containing event handlers for the scrollbar
*/
_generateEventHandlerForElement(aAttribute, aPropertyFactor, aParentWriteCallback) {
_generateEventHandlerForElement(aAttribute, aScaleFactor, aParentWriteCallback) {
return {
mousedown: (aEvent) => {
// first of all we need to prevent the default behaviour, because otherwise the mouse
Expand All @@ -101,16 +104,16 @@ export class ScrollView {
// then setup some cache variables, that contain the last page value and the current
// scroll value we want to modify
let tmpMover = aEvent[aAttribute];
let scrollPositionFloat = this._scrollerParent[aParentWriteCallback]();
let scrollPosition = this._scrollerParent[aParentWriteCallback]();

// then setup a pointer to the move function for registering and unregistering
let tmpMovePointer = (e) => {
// here we calculate the new scrollPosition
scrollPositionFloat += (e[aAttribute] - tmpMover) * this[aPropertyFactor];
scrollPosition += (e[aAttribute] - tmpMover) * this[aScaleFactor];
// save to the cache
tmpMover = e[aAttribute];
// and set the new scroll positioning. The callback will tell us, what it did with the value
scrollPositionFloat = this._scrollerParent[aParentWriteCallback](Math.round(scrollPositionFloat));
scrollPosition = this._scrollerParent[aParentWriteCallback](Math.round(scrollPosition));
};

// then we setup a function for the end function, which cleans up everything
Expand All @@ -137,7 +140,7 @@ export class ScrollView {
const touchToTrack = aEvent.which || 0;
// and init the cache variables
let tmpMover = aEvent.touches[touchToTrack][aAttribute];
let scrollPositionFloat = this._scrollerParent[aParentWriteCallback]();
let scrollPosition = this._scrollerParent[aParentWriteCallback]();

// setup a move function, that we can register and unregister
let tmpMovePointer = (aaEvent) => {
Expand All @@ -146,13 +149,12 @@ export class ScrollView {
return;
}
// first calculate the scroll new scroll position
scrollPositionFloat += (aaEvent.touches[touchToTrack][aAttribute] - tmpMover);
scrollPositionFloat *= this[aPropertyFactor];
scrollPosition += (aaEvent.touches[touchToTrack][aAttribute] - tmpMover) * this[aScaleFactor];
// then update the cache
tmpMover = aaEvent.touches[touchToTrack][aAttribute];

// and write the new scroll value
scrollPositionFloat = this._scrollerParent[aParentWriteCallback](Math.round(scrollPositionFloat));
scrollPosition = this._scrollerParent[aParentWriteCallback](Math.round(scrollPosition));
};

// and setup a clean up function, if the touch ends
Expand Down Expand Up @@ -222,14 +224,14 @@ export class ScrollView {
*/
parentUpdated() {
// read and recalculate all needed data
this._parentWidth = this._parent.clientWidth;
this._parentScrollWidth = this._parent.scrollWidth;
this._parentWidth = this._parentElement.clientWidth;
this._parentScrollWidth = this._parentElement.scrollWidth;
this._elementWidth = (this._parentWidth * this._parentWidth) / this._parentScrollWidth;
this._parentHeight = this._parent.clientHeight;
this._parentScrollHeight = this._parent.scrollHeight;
this._parentHeight = this._parentElement.clientHeight;
this._parentScrollHeight = this._parentElement.scrollHeight;
this._elementHeight = (this._parentHeight * this._parentHeight) / this._parentScrollHeight;
this._scrollHeightFactor = this._parent.scrollHeight / this._parent.clientHeight;
this._scrollWidthFactor = this._parent.scrollWidth / this._parent.clientWidth;
this._scrollHeightFactor = this._parentElement.scrollHeight / this._parentElement.clientHeight;
this._scrollWidthFactor = this._parentElement.scrollWidth / this._parentElement.clientWidth;

// determine visibility of x element
if (this._xElement) {
Expand All @@ -239,7 +241,7 @@ export class ScrollView {
this._elementWidth = this._options.xMinSize;
}

this.scrollTopUpdated(this._parent.scrollTop);
this.scrollTopUpdated(this._parentElement.scrollTop);
this._xElement.style.display = 'block';
this._xElement.style.width = `${this._elementWidth}px`;
}
Expand All @@ -256,7 +258,7 @@ export class ScrollView {
this._elementHeight = this._options.yMinSize;
}

this.scrollLeftUpdated(this._parent.scrollLeft);
this.scrollLeftUpdated(this._parentElement.scrollLeft);
this._yElement.style.display = 'block';
this._yElement.style.height = `${this._elementHeight}px`;
}
Expand All @@ -276,7 +278,7 @@ export class ScrollView {
this._destroyCallbacks.forEach(aCallback => aCallback());

// and then null all data, so the GC can clean it up
this._parent = null;
this._parentElement = null;
this._scrollerParent = null;
this._xElement = null;
this._yElement = null;
Expand Down
Loading

0 comments on commit af12bc1

Please sign in to comment.