diff --git a/docs/new.html b/docs/new.html
index 0e33594..2be12c2 100644
--- a/docs/new.html
+++ b/docs/new.html
@@ -143,6 +143,23 @@
Third
+
+
+ Horizontal snapping
+
+
+
+
+
+
+
+
Manual snapping
diff --git a/docs/scripts.js b/docs/scripts.js
index 0086a01..3d1e129 100644
--- a/docs/scripts.js
+++ b/docs/scripts.js
@@ -7,3 +7,7 @@ var event_demo = new PanelSnap({
var large_demo = new PanelSnap({
container: document.querySelector('.large_demo .panels')
});
+
+var horizontal_demo = new PanelSnap({
+ container: document.querySelector('.horizontal_demo .panels')
+});
diff --git a/docs/style.css b/docs/style.css
index ccfd41f..843a872 100644
--- a/docs/style.css
+++ b/docs/style.css
@@ -63,6 +63,18 @@ section section:nth-child(2n) {
height: 150%;
}
+.panels.horizontal {
+ display: flex;
+}
+
+.panels.horizontal section {
+ min-width: 100%;
+}
+
+.panels.horizontal section:nth-of-type(2) {
+ min-width: 150%;
+}
+
section .explanation,
section pre {
background: #16a085;
diff --git a/src/panelSnap.js b/src/panelSnap.js
index 0f0e8b3..346b245 100644
--- a/src/panelSnap.js
+++ b/src/panelSnap.js
@@ -10,6 +10,7 @@ import {
} from './utilities';
let INSTANCE_COUNTER = 0;
+const TWEEN_MAX_VALUE = 10000;
const defaultOptions = {
container: document.body,
@@ -47,7 +48,14 @@ export default class PanelSnap {
this.isInteracting = false;
this.scrollTimeout = null;
this.animation = null;
- this.currentScrollTop = this.scrollContainer.scrollTop;
+ this.currentScrollOffset = {
+ top: this.scrollContainer.scrollTop,
+ left: this.scrollContainer.scrollLeft,
+ };
+ this.targetScrollOffset = {
+ top: 0,
+ left: 0,
+ };
this.scrollEventContainer.addEventListener('keydown', this.onInteractStart.bind(this), passiveIsSupported && { passive: true });
this.scrollEventContainer.addEventListener('keyup', this.onInteractStop.bind(this), passiveIsSupported && { passive: true });
@@ -103,7 +111,10 @@ export default class PanelSnap {
return;
}
- if (this.currentScrollTop === this.scrollContainer.scrollTop) {
+ if (
+ this.currentScrollOffset.top === this.scrollContainer.scrollTop
+ && this.currentScrollOffset.left === this.scrollContainer.scrollLeft
+ ) {
return;
}
@@ -113,8 +124,12 @@ export default class PanelSnap {
}
findSnapTarget() {
- const deltaY = this.scrollContainer.scrollTop - this.currentScrollTop;
- this.currentScrollTop = this.scrollContainer.scrollTop;
+ const deltaY = this.scrollContainer.scrollTop - this.currentScrollOffset.top;
+ const deltaX = this.scrollContainer.scrollLeft - this.currentScrollOffset.left;
+ this.currentScrollOffset = {
+ top: this.scrollContainer.scrollTop,
+ left: this.scrollContainer.scrollLeft,
+ };
const panelsInViewport = getElementsInContainerViewport(this.container, this.panelList);
if (panelsInViewport.length === 0) {
@@ -122,13 +137,17 @@ export default class PanelSnap {
}
if (panelsInViewport.length > 1) {
- if (Math.abs(deltaY) < this.options.directionThreshold && this.activePanel) {
- this.snapToPanel(this.activePanel, deltaY > 0);
+ if (
+ Math.abs(deltaY) < this.options.directionThreshold
+ && Math.abs(deltaX) < this.options.directionThreshold
+ && this.activePanel
+ ) {
+ this.snapToPanel(this.activePanel, deltaY > 0, deltaX > 0);
return;
}
- const targetIndex = deltaY > 0 ? 1 : panelsInViewport.length - 2;
- this.snapToPanel(panelsInViewport[targetIndex], deltaY < 0);
+ const targetIndex = deltaY > 0 || deltaX > 0 ? 1 : panelsInViewport.length - 2;
+ this.snapToPanel(panelsInViewport[targetIndex], deltaY < 0, deltaX < 0);
return;
}
@@ -141,10 +160,10 @@ export default class PanelSnap {
// TODO: Only one partial panel in viewport, add support for space between panels?
// eslint-disable-next-line no-console
console.error('PanelSnap does not support space between panels, snapping back.');
- this.snapToPanel(visiblePanel, deltaY > 0);
+ this.snapToPanel(visiblePanel, deltaY > 0, deltaX > 0);
}
- snapToPanel(panel, toBottom = false) {
+ snapToPanel(panel, toBottom = false, toRight = false) {
this.activatePanel(panel);
if (!this.isEnabled) {
@@ -155,16 +174,14 @@ export default class PanelSnap {
this.animation.stop();
}
- const targetScrollOffset = getTargetScrollOffset(this.container, panel, toBottom);
- const start = this.scrollContainer.scrollTop;
- const end = targetScrollOffset.top;
+ this.targetScrollOffset = getTargetScrollOffset(this.container, panel, toBottom, toRight);
+
+ const start = 0;
+ const end = TWEEN_MAX_VALUE;
const duration = 300;
this.animation = new Tweezer({ start, end, duration });
-
- this.animation.on('tick', (value) => {
- this.scrollContainer.scrollTop = value;
- });
+ this.animation.on('tick', this.animationTick.bind(this));
this.animation.on('done', () => {
this.emit('snapStop', panel);
this.clearAnimation();
@@ -174,6 +191,16 @@ export default class PanelSnap {
this.animation.begin();
}
+ animationTick(value) {
+ const scrollTopDelta = this.targetScrollOffset.top - this.currentScrollOffset.top;
+ const scrollTop = this.currentScrollOffset.top + (scrollTopDelta * value / TWEEN_MAX_VALUE);
+ this.scrollContainer.scrollTop = scrollTop;
+
+ const scrollLeftDelta = this.targetScrollOffset.left - this.currentScrollOffset.left;
+ const scrollLeft = this.currentScrollOffset.left + (scrollLeftDelta * value / TWEEN_MAX_VALUE);
+ this.scrollContainer.scrollLeft = scrollLeft;
+ }
+
stopAnimation() {
if (!this.animation) {
return;
@@ -184,7 +211,14 @@ export default class PanelSnap {
}
clearAnimation() {
- this.currentScrollTop = this.scrollContainer.scrollTop;
+ this.currentScrollOffset = {
+ top: this.scrollContainer.scrollTop,
+ left: this.scrollContainer.scrollLeft,
+ };
+ this.targetScrollOffset = {
+ top: 0,
+ left: 0,
+ };
this.animation = null;
}