@visactor/vrender-kits
Version:
## Description
189 lines (185 loc) • 10.9 kB
JavaScript
import { application, clock, WILDCARD } from "@visactor/vrender-core";
import { EventEmitter } from "@visactor/vutils";
const PRESS_TIME = 251, PRESS_THRESHOLD = 9, SWIPE_VELOCITY = .3, SWIPE_THRESHOLD = 10, TAP_INTERVAL = 300, calcDirection = (start, end) => {
const xDistance = end.x - start.x, yDistance = end.y - start.y;
return Math.abs(xDistance) > Math.abs(yDistance) ? xDistance > 0 ? "right" : "left" : yDistance > 0 ? "down" : "up";
}, calcDistance = (point1, point2) => {
const xDistance = Math.abs(point2.x - point1.x), yDistance = Math.abs(point2.y - point1.y);
return Math.sqrt(xDistance * xDistance + yDistance * yDistance);
}, getCenter = points => {
const pointersLength = points.length;
if (1 === pointersLength) return {
x: Math.round(points[0].x),
y: Math.round(points[0].y)
};
let x = 0, y = 0, i = 0;
for (;i < pointersLength; ) x += points[i].x, y += points[i].y, i++;
return {
x: Math.round(x / pointersLength),
y: Math.round(y / pointersLength)
};
};
export class Gesture extends EventEmitter {
constructor(element, config = {}) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
super(), this.cachedEvents = [], this.startPoints = [], this.processEvent = {},
this.throttleTimer = 0, this.emitThrottles = [], this.lastTapTarget = null, this.onStart = ev => {
this.startTime = clock.now();
const {cachedEvents: cachedEvents, startPoints: startPoints} = this;
ev && cachedEvents.push(ev.clone()), startPoints.length = cachedEvents.length;
for (let i = 0; i < cachedEvents.length; i++) {
const {x: x, y: y} = cachedEvents[i], point = {
x: x,
y: y
};
startPoints[i] = point;
}
if (1 !== startPoints.length) this.startDistance = calcDistance(startPoints[0], startPoints[1]),
this.center = getCenter([ startPoints[0], startPoints[1] ]); else {
const event = cachedEvents[0];
this.pressTimeout = setTimeout((() => {
event.direction = "none", event.deltaX = 0, event.deltaY = 0, event.points = startPoints,
this.triggerStartEvent("press", event), this.triggerEvent("press", event), this.eventType = "press",
this.direction = "none", this.pressTimeout = null;
}), this.config.press.time);
}
}, this.onMove = ev => {
this.clearPressTimeout();
const {startPoints: startPoints, cachedEvents: cachedEvents} = this;
if (!startPoints.length) return;
const moveEvent = ev.clone(), {x: x, y: y, pointerId: pointerId} = moveEvent;
for (let i = 0, len = cachedEvents.length; i < len; i++) if (pointerId === cachedEvents[i].pointerId) {
cachedEvents[i] = moveEvent;
break;
}
const point = {
x: x,
y: y
}, points = cachedEvents.map((cachedEvent => ({
x: cachedEvent.x,
y: cachedEvent.y
}))), now = clock.now();
if (this.prevMoveTime = this.lastMoveTime, this.prevMovePoint = this.lastMovePoint,
this.lastMoveTime = now, this.lastMovePoint = point, 1 === startPoints.length) {
const startPoint = startPoints[0], deltaX = x - startPoint.x, deltaY = y - startPoint.y, direction = this.direction || calcDirection(startPoint, point);
this.direction = direction;
const eventType = this.getEventType(point);
return moveEvent.direction = direction, moveEvent.deltaX = deltaX, moveEvent.deltaY = deltaY,
moveEvent.points = points, this.triggerStartEvent(eventType, moveEvent), void this.triggerEvent(eventType, moveEvent);
}
const {startDistance: startDistance} = this, currentDistance = calcDistance(points[0], points[1]);
moveEvent.scale = currentDistance / startDistance, moveEvent.center = this.center,
moveEvent.points = points, this.triggerStartEvent("pinch", moveEvent), this.triggerEvent("pinch", moveEvent);
}, this.onEnd = ev => {
const endEvent = ev.clone(), {cachedEvents: cachedEvents, startPoints: startPoints} = this, points = cachedEvents.map((ev => ({
x: ev.x,
y: ev.y
})));
if (endEvent.points = points, this.triggerEndEvent(endEvent), 1 === cachedEvents.length) {
const now = clock.now(), lastMoveTime = this.lastMoveTime;
if (now - lastMoveTime < 100) {
const intervalTime = lastMoveTime - (this.prevMoveTime || this.startTime);
if (intervalTime > 0) {
const prevMovePoint = this.prevMovePoint || startPoints[0], lastMovePoint = this.lastMovePoint || startPoints[0], distance = calcDistance(prevMovePoint, lastMovePoint), velocity = distance / intervalTime;
if (velocity > this.config.swipe.velocity && distance > this.config.swipe.threshold) return endEvent.velocity = velocity,
endEvent.direction = calcDirection(prevMovePoint, lastMovePoint), this.triggerEvent("swipe", endEvent),
this.cachedEvents = [], this.startPoints = [], void this.reset();
}
}
now - this.startTime < this.config.press.time && (now - this.lastTapTime < this.config.tap.interval && ev.target === this.lastTapTarget ? this.tapCount++ : this.tapCount = 1,
this.lastTapTime = now, this.lastTapTarget = ev.target, 1 === this.tapCount ? this.triggerEvent("tap", endEvent) : 2 === this.tapCount && (this.triggerEvent("doubletap", endEvent),
this.tapCount = 0));
}
for (let i = 0, len = cachedEvents.length; i < len; i++) if (cachedEvents[i].pointerId === endEvent.pointerId) {
cachedEvents.splice(i, 1), startPoints.splice(i, 1);
break;
}
this.reset(), cachedEvents.length > 0 && this.onStart();
}, this.element = element, this.tapCount = 0, this.lastTapTime = 0, this.config = {
press: {
time: null !== (_b = null === (_a = null == config ? void 0 : config.press) || void 0 === _a ? void 0 : _a.time) && void 0 !== _b ? _b : 251,
threshold: null !== (_d = null === (_c = null == config ? void 0 : config.press) || void 0 === _c ? void 0 : _c.threshold) && void 0 !== _d ? _d : 9
},
swipe: {
threshold: null !== (_f = null === (_e = null == config ? void 0 : config.swipe) || void 0 === _e ? void 0 : _e.threshold) && void 0 !== _f ? _f : 10,
velocity: null !== (_h = null === (_g = null == config ? void 0 : config.swipe) || void 0 === _g ? void 0 : _g.velocity) && void 0 !== _h ? _h : .3
},
tap: {
interval: null !== (_k = null === (_j = null == config ? void 0 : config.tap) || void 0 === _j ? void 0 : _j.interval) && void 0 !== _k ? _k : 300
}
}, this.initEvents();
}
initEvents() {
const {element: element} = this;
element && (element.addEventListener("pointerdown", this.onStart), element.addEventListener("pointermove", this.onMove),
element.addEventListener("pointerup", this.onEnd), element.addEventListener("pointerleave", this.onEnd),
element.addEventListener("pointerupoutside", this.onEnd));
}
removeEvents() {
const {element: element} = this;
element && (element.removeEventListener("pointerdown", this.onStart), element.removeEventListener("pointermove", this.onMove),
element.removeEventListener("pointerup", this.onEnd), element.removeEventListener("pointerleave", this.onEnd),
element.removeEventListener("pointerupoutside", this.onEnd));
}
release() {
this.removeEvents(), this.element = null;
}
getEventType(point) {
const {eventType: eventType, startTime: startTime, startPoints: startPoints} = this;
if ("press" === eventType) return eventType;
let type;
return type = clock.now() - startTime > this.config.press.time && calcDistance(startPoints[0], point) < this.config.press.threshold ? "press" : "pan",
this.eventType = type, type;
}
enable(eventType) {
this.processEvent[eventType] = !0;
}
isProcess(eventType) {
return this.processEvent[eventType];
}
pushEvent(type, ev) {
const {emitThrottles: emitThrottles} = this, newEvent = {
type: type,
ev: ev
};
for (let i = 0, len = emitThrottles.length; i < len; i++) if (emitThrottles[i].type === type) return void emitThrottles.splice(i, 1, newEvent);
emitThrottles.push(newEvent);
}
clearPressTimeout() {
this.pressTimeout && (clearTimeout(this.pressTimeout), this.pressTimeout = null);
}
reset() {
this.clearPressTimeout(), this.startTime = 0, this.startDistance = 0, this.direction = null,
this.eventType = null, this.prevMoveTime = 0, this.prevMovePoint = null, this.lastMoveTime = 0,
this.lastMovePoint = null;
}
triggerEvent(type, ev) {
this.pushEvent(type, ev);
const {throttleTimer: throttleTimer, emitThrottles: emitThrottles} = this;
throttleTimer || (this.throttleTimer = application.global.getRequestAnimationFrame()((() => {
application.global.getCancelAnimationFrame()(this.throttleTimer), this.throttleTimer = null;
for (let i = 0, len = emitThrottles.length; i < len; i++) {
const {type: type, ev: ev} = emitThrottles[i];
this.emitEvent(type, ev);
}
this.emitThrottles.length = 0;
})));
}
triggerStartEvent(type, ev) {
this.isProcess(type) || (this.enable(type), this.triggerEvent(`${type}start`, ev));
}
triggerEndEvent(ev) {
const processEvent = this.processEvent;
Object.keys(processEvent).forEach((type => {
this.triggerEvent(`${type}end`, ev), "press" === type && this.triggerEvent(`${type}up`, ev),
delete processEvent[type];
}));
}
emitEvent(type, e) {
if (!this.element) return;
const listeners = this.element._events[WILDCARD];
if (listeners) if ("fn" in listeners) listeners.fn.call(listeners.context, e, type); else for (let i = 0, j = listeners.length; i < j && !e.propagationImmediatelyStopped; i++) listeners[i].fn.call(listeners[i].context, e, type);
this.emit(type, e);
}
}
//# sourceMappingURL=gesture.js.map