Skip to content

Commit

Permalink
feat: add horizontal snapping to PanelSnap
Browse files Browse the repository at this point in the history
fixes #7
  • Loading branch information
guidobouman committed May 12, 2018
1 parent 236e968 commit 913cde7
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 18 deletions.
17 changes: 17 additions & 0 deletions docs/new.html
Expand Up @@ -143,6 +143,23 @@ <h1>Third</h1>
</div>
</section>

<section class="horizontal_demo">
<h1>
Horizontal snapping
</h1>
<div class="panels horizontal">
<section>
<h1>First</h1>
</section>
<section>
<h1>Second</h1>
</section>
<section>
<h1>Third</h1>
</section>
</div>
</section>

<section class="manual_demo">
<h1>
Manual snapping
Expand Down
4 changes: 4 additions & 0 deletions docs/scripts.js
Expand Up @@ -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')
});
12 changes: 12 additions & 0 deletions docs/style.css
Expand Up @@ -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;
Expand Down
70 changes: 52 additions & 18 deletions src/panelSnap.js
Expand Up @@ -10,6 +10,7 @@ import {
} from './utilities';

let INSTANCE_COUNTER = 0;
const TWEEN_MAX_VALUE = 10000;

const defaultOptions = {
container: document.body,
Expand Down Expand Up @@ -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 });
Expand Down Expand Up @@ -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;
}

Expand All @@ -113,22 +124,30 @@ 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) {
throw new Error('PanelSnap could not find a snappable panel, aborting.');
}

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;
}

Expand All @@ -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) {
Expand All @@ -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();
Expand All @@ -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;
Expand All @@ -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;
}

Expand Down

0 comments on commit 913cde7

Please sign in to comment.