fix(datetime): fix gesture

This commit is contained in:
Manu Mtz.-Almeida
2018-08-02 23:15:19 +02:00
parent b69d01de26
commit e9fd184175
9 changed files with 198 additions and 303 deletions

View File

@ -1,6 +1,6 @@
import { Component, Element, Prop, QueueApi } from '@stencil/core';
import { Gesture, GestureDetail, Mode, PickerColumn, PickerColumnOption } from '../../interface';
import { Gesture, GestureDetail, Mode, PickerColumn } from '../../interface';
import { hapticSelectionChanged } from '../../utils';
import { clamp } from '../../utils/helpers';
import { createThemedClasses } from '../../utils/theme';
@ -18,13 +18,12 @@ export class PickerColumnCmp {
private minY!: number;
private maxY!: number;
private optHeight = 0;
private pos: number[] = [];
private rotateFactor = 0;
private scaleFactor = 1;
private startY?: number;
private velocity = 0;
private y = 0;
private gesture?: Gesture;
private rafId: any;
@Element() el!: HTMLElement;
@ -54,27 +53,17 @@ export class PickerColumnCmp {
this.refresh();
this.gesture = (await import('../../utils/gesture/gesture')).create({
this.gesture = (await import('../../utils/gesture/gesture')).createGesture({
el: this.el,
queue: this.queue,
gestureName: 'picker-swipe',
gesturePriority: 10,
threshold: 0,
canStart: this.canStart.bind(this),
onStart: this.onDragStart.bind(this),
onMove: this.onDragMove.bind(this),
onEnd: this.onDragEnd.bind(this),
});
this.gesture.disabled = false;
}
private optClick(ev: Event, index: number) {
if (!this.velocity) {
ev.preventDefault();
ev.stopPropagation();
this.setSelected(index, 150);
}
this.gesture.setDisabled(false);
}
private setSelected(selectedIndex: number, duration: number) {
@ -85,6 +74,7 @@ export class PickerColumnCmp {
this.velocity = 0;
// set what y position we're at
cancelAnimationFrame(this.rafId);
this.update(y, duration, true, true);
}
@ -92,16 +82,8 @@ export class PickerColumnCmp {
// ensure we've got a good round number :)
y = Math.round(y);
let i: number;
let button: any;
let opt: PickerColumnOption;
let optOffset: number;
let visible: boolean;
let translateY = 0;
let translateZ = 0;
let rotateX: number;
let transform: string;
let selected: boolean;
const parent = this.el.querySelector('.picker-opts')!;
const children = parent.children;
@ -111,15 +93,15 @@ export class PickerColumnCmp {
const durationStr = (duration === 0) ? null : duration + 'ms';
const scaleStr = `scale(${this.scaleFactor})`;
for (i = 0; i < length; i++) {
button = children[i];
opt = this.col.options[i];
optOffset = (i * this.optHeight) + y;
visible = true;
transform = '';
for (let i = 0; i < length; i++) {
const button = children[i] as HTMLElement;
const opt = this.col.options[i];
const optOffset = (i * this.optHeight) + y;
let visible = true;
let transform = '';
if (this.rotateFactor !== 0) {
rotateX = optOffset * this.rotateFactor;
const rotateX = optOffset * this.rotateFactor;
if (Math.abs(rotateX) > 90) {
visible = false;
} else {
@ -135,7 +117,7 @@ export class PickerColumnCmp {
}
}
selected = selectedIndex === i;
const selected = selectedIndex === i;
if (visible) {
transform += `translate3d(0px,${translateY}px,${translateZ}px) `;
if (this.scaleFactor !== 1 && !selected) {
@ -204,7 +186,7 @@ export class PickerColumnCmp {
? Math.max(this.velocity, 1)
: Math.min(this.velocity, -1);
y = Math.round(this.y - this.velocity);
y = Math.round(this.y + this.velocity);
if (y > this.minY) {
// whoops, it's trying to scroll up farther than the options we have!
@ -223,7 +205,7 @@ export class PickerColumnCmp {
if (notLockedIn) {
// isn't locked in yet, keep decelerating until it is
this.queue.read(() => this.decelerate());
this.rafId = requestAnimationFrame(() => this.decelerate());
}
} else if (this.y % this.optHeight !== 0) {
@ -246,11 +228,8 @@ export class PickerColumnCmp {
}
// TODO should this check disabled?
private canStart() {
return true;
}
private onDragStart(detail: GestureDetail): boolean {
private onDragStart(detail: GestureDetail) {
// We have to prevent default in order to block scrolling under the picker
// but we DO NOT have to stop propagation, since we still want
// some "click" events to capture
@ -259,14 +238,8 @@ export class PickerColumnCmp {
detail.event.stopPropagation();
}
// remember where the pointer started from
this.startY = detail.startY;
// reset everything
this.velocity = 0;
this.pos.length = 0;
this.pos.push(this.startY, Date.now());
cancelAnimationFrame(this.rafId);
const options = this.col.options;
let minY = (options.length - 1);
let maxY = 0;
@ -279,7 +252,6 @@ export class PickerColumnCmp {
this.minY = (minY * this.optHeight * -1);
this.maxY = (maxY * this.optHeight * -1);
return true;
}
private onDragMove(detail: GestureDetail) {
@ -288,15 +260,8 @@ export class PickerColumnCmp {
detail.event.stopPropagation();
}
const currentY = detail.currentY;
this.pos.push(currentY, Date.now());
if (this.startY === undefined) {
return;
}
// update the scroll position relative to pointer start position
let y = this.y + (currentY - this.startY);
let y = this.y + detail.deltaY;
if (y > this.minY) {
// scrolling up higher than scroll area
@ -321,12 +286,6 @@ export class PickerColumnCmp {
}
private onDragEnd(detail: GestureDetail) {
if (this.startY === undefined) {
return;
}
this.velocity = 0;
if (this.bounceFrom > 0) {
// bounce back up
this.update(this.minY, 100, true, true);
@ -337,36 +296,20 @@ export class PickerColumnCmp {
return;
}
const endY = detail.currentY;
this.velocity = clamp(-MAX_PICKER_SPEED, detail.velocityY * 17, MAX_PICKER_SPEED);
if (this.velocity === 0 && detail.deltaY === 0) {
const opt = (detail.event.target as Element).closest('.picker-opt');
if (opt && opt.hasAttribute('opt-index')) {
this.setSelected(parseInt(opt.getAttribute('opt-index')!, 10), 150);
}
this.pos.push(endY, Date.now());
const endPos = (this.pos.length - 1);
let startPos = endPos;
const timeRange = (Date.now() - 100);
// move pointer to position measured 100ms ago
for (let i = endPos; i > 0 && this.pos[i] > timeRange; i -= 2) {
startPos = i;
} else {
if (Math.abs(detail.deltaY) > 3) {
const y = this.y + detail.deltaY;
this.update(y, 0, true, true);
}
this.decelerate();
}
if (startPos !== endPos) {
// compute relative movement between these two points
const timeOffset = (this.pos[endPos] - this.pos[startPos]);
const movedTop = (this.pos[startPos - 1] - this.pos[endPos - 1]);
// based on XXms compute the movement to apply for each render step
const velocity = ((movedTop / timeOffset) * FRAME_MS);
this.velocity = clamp(-MAX_PICKER_SPEED, velocity, MAX_PICKER_SPEED);
}
if (Math.abs(endY - this.startY) > 3) {
const y = this.y + (endY - this.startY);
this.update(y, 0, true, true);
}
this.startY = undefined;
this.decelerate();
}
private refresh() {
@ -406,45 +349,35 @@ export class PickerColumnCmp {
const col = this.col;
const options = col.options.map(o => {
if (typeof o === 'string') {
o = { text: o };
}
return o;
return (typeof o === 'string')
? { text: o }
: o;
})
.filter(o => o !== null);
const results: JSX.Element[] = [];
if (col.prefix) {
results.push(
const Button = 'button' as any;
return [
col.prefix && (
<div class="picker-prefix" style={{ width: col.prefixWidth! }}>
{col.prefix}
</div>
);
}
results.push(
),
<div class="picker-opts" style={{ maxWidth: col.optionsWidth! }}>
{options.map((o, index) =>
<button
<Button
class={{ 'picker-opt': true, 'picker-opt-disabled': !!o.disabled }}
disable-activated
onClick={event => this.optClick(event, index)}>
opt-index={index}>
{o.text}
</button>
</Button>
)}
</div>
);
if (col.suffix) {
results.push(
</div>,
col.suffix && (
<div class="picker-suffix" style={{ width: col.suffixWidth! }}>
{col.suffix}
</div>
);
}
return results;
)
];
}
}