Skip to content

Commit

Permalink
feat: add inputKey option to PanInput and WheelInput
Browse files Browse the repository at this point in the history
  • Loading branch information
malangfox committed Sep 27, 2022
1 parent 93e7279 commit 0fa4331
Show file tree
Hide file tree
Showing 14 changed files with 189 additions and 40 deletions.
5 changes: 5 additions & 0 deletions packages/axes/src/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ export const MOUSE_LEFT = "left";
export const MOUSE_RIGHT = "right";
export const MOUSE_MIDDLE = "middle";

export const SHIFT = "shift";
export const CTRL = "ctrl";
export const ALT = "alt";
export const META = "meta";

export const VELOCITY_INTERVAL = 16;

export const AXES_METHODS = [
Expand Down
40 changes: 38 additions & 2 deletions packages/axes/src/eventInput/EventInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@ import { ExtendedEvent, InputEventType, LatestInterval } from "../types";
import { getAngle } from "../utils";
import { window } from "../browser";
import {
ALT,
CTRL,
META,
MOUSE_LEFT,
MOUSE_MIDDLE,
MOUSE_RIGHT,
SHIFT,
VELOCITY_INTERVAL,
} from "../const";

Expand All @@ -17,6 +21,23 @@ export const SUPPORT_POINTER = "PointerEvent" in window;
export const SUPPORT_MSPOINTER = "MSPointerEvent" in window;
export const SUPPORT_POINTER_EVENTS = SUPPORT_POINTER || SUPPORT_MSPOINTER;

export const isValidKey = (
event: InputEventType | WheelEvent,
inputKey?: string[]
): boolean => {
if (
!inputKey ||
!inputKey.length ||
(inputKey.indexOf(SHIFT) > -1 && event.shiftKey === true) ||
(inputKey.indexOf(CTRL) > -1 && event.ctrlKey === true) ||
(inputKey.indexOf(ALT) > -1 && event.altKey === true) ||
(inputKey.indexOf(META) > -1 && event.metaKey === true)
) {
return true;
}
return false;
};

export abstract class EventInput {
public prevEvent: ExtendedEvent;
private _latestInterval: LatestInterval;
Expand All @@ -35,7 +56,11 @@ export abstract class EventInput {

public abstract onRelease(event: InputEventType): void;

public abstract getTouches(event: InputEventType, inputButton?: string[]): number;
public abstract getTouches(
event: InputEventType,
inputKey?: string[],
inputButton?: string[]
): number;

protected abstract _getScale(event: InputEventType): number;

Expand Down Expand Up @@ -112,13 +137,24 @@ export abstract class EventInput {
}

protected _isTouchEvent(event: InputEventType): event is TouchEvent {
return event.type.indexOf("touch") > -1;
return event.type && event.type.indexOf("touch") > -1;
}

protected _isValidButton(button: string, inputButton: string[]): boolean {
return inputButton.indexOf(button) > -1;
}

protected _isValidEvent(
event: InputEventType,
inputKey?: string[],
inputButton?: string[]
): boolean {
return (
(!inputKey || isValidKey(event, inputKey)) &&
(!inputButton || this._isValidButton(this._getButton(event), inputButton))
);
}

protected _preventMouseButton(event: InputEventType, button: string): void {
if (button === MOUSE_RIGHT) {
window.addEventListener("contextmenu", this._stopContextMenu);
Expand Down
9 changes: 4 additions & 5 deletions packages/axes/src/eventInput/MouseEventInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ export class MouseEventInput extends EventInput {

public onEventStart(
event: InputEventType,
inputKey?: string[],
inputButton?: string[]
): ExtendedEvent {
const button = this._getButton(event);
if (inputButton && !this._isValidButton(button, inputButton)) {
if (!this._isValidEvent(event, inputKey, inputButton)) {
return null;
}
this._preventMouseButton(event, button);
Expand All @@ -26,12 +27,10 @@ export class MouseEventInput extends EventInput {

public onEventMove(
event: InputEventType,
inputKey?: string[],
inputButton?: string[]
): ExtendedEvent {
if (
inputButton &&
!this._isValidButton(this._getButton(event), inputButton)
) {
if (!this._isValidEvent(event, inputKey, inputButton)) {
return null;
}
return this.extendEvent(event);
Expand Down
9 changes: 4 additions & 5 deletions packages/axes/src/eventInput/PointerEventInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ export class PointerEventInput extends EventInput {

public onEventStart(
event: InputEventType,
inputKey?: string[],
inputButton?: string[]
): ExtendedEvent {
const button = this._getButton(event);
if (inputButton && !this._isValidButton(button, inputButton)) {
if (!this._isValidEvent(event, inputKey, inputButton)) {
return null;
}
this._preventMouseButton(event, button);
Expand All @@ -32,12 +33,10 @@ export class PointerEventInput extends EventInput {

public onEventMove(
event: InputEventType,
inputKey?: string[],
inputButton?: string[]
): ExtendedEvent {
if (
inputButton &&
!this._isValidButton(this._getButton(event), inputButton)
) {
if (!this._isValidEvent(event, inputKey, inputButton)) {
return null;
}
this._updatePointerEvent(event as PointerEvent);
Expand Down
16 changes: 14 additions & 2 deletions packages/axes/src/eventInput/TouchEventInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,24 @@ export class TouchEventInput extends EventInput {

private _baseTouches: TouchList;

public onEventStart(event: InputEventType): ExtendedEvent {
public onEventStart(
event: InputEventType,
inputKey?: string[]
): ExtendedEvent {
this._baseTouches = (event as TouchEvent).touches;
if (!this._isValidEvent(event, inputKey)) {
return null;
}
return this.extendEvent(event);
}

public onEventMove(event: InputEventType): ExtendedEvent {
public onEventMove(
event: InputEventType,
inputKey?: string[]
): ExtendedEvent {
if (!this._isValidEvent(event, inputKey)) {
return null;
}
return this.extendEvent(event);
}

Expand Down
9 changes: 4 additions & 5 deletions packages/axes/src/eventInput/TouchMouseEventInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ export class TouchMouseEventInput extends EventInput {

public onEventStart(
event: InputEventType,
inputKey?: string[],
inputButton?: string[]
): ExtendedEvent {
const button = this._getButton(event);
if (this._isTouchEvent(event)) {
this._baseTouches = event.touches;
}
if (inputButton && !this._isValidButton(button, inputButton)) {
if (!this._isValidEvent(event, inputKey, inputButton)) {
return null;
}
this._preventMouseButton(event, button);
Expand All @@ -30,12 +31,10 @@ export class TouchMouseEventInput extends EventInput {

public onEventMove(
event: InputEventType,
inputKey?: string[],
inputButton?: string[]
): ExtendedEvent {
if (
inputButton &&
!this._isValidButton(this._getButton(event), inputButton)
) {
if (!this._isValidEvent(event, inputKey, inputButton)) {
return null;
}
return this.extendEvent(event);
Expand Down
24 changes: 20 additions & 4 deletions packages/axes/src/inputType/PanInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {

export interface PanInputOption {
inputType?: string[];
inputKey?: string[];
inputButton?: string[];
scale?: number[];
thresholdAngle?: number;
Expand Down Expand Up @@ -66,6 +67,15 @@ export const getDirectionByAngle = (
* - touch: 터치 입력 장치
* - mouse: 마우스
* - pointer: 마우스 및 터치</ko>
* @param {String[]} [inputKey=[]] Allow input only when one of these keys is pressed. If the array is empty, it accepts all keys with case of no key is pressed.
* - shift: shift key
* - ctrl: ctrl key
* - alt: alt key
* - meta: meta key <ko>이 중 하나의 키가 눌린 상태에서만 입력이 허용된다. 배열이 비었다면 아무 키도 눌리지 않은 상태와 모든 키가 눌린 상태 모두 허용한다.
* - shift: shift 키
* - ctrl: ctrl 키
* - alt: alt 키
* - meta: meta 키 </ko>
* @param {String[]} [inputButton=["left"]] List of buttons to allow input
* - left: Left mouse button and normal touch
* - middle: Mouse wheel press
Expand Down Expand Up @@ -127,6 +137,7 @@ export class PanInput implements InputType {
this.element = $(el);
this.options = {
inputType: ["touch", "mouse", "pointer"],
inputKey: [],
inputButton: [MOUSE_LEFT],
scale: [1, 1],
thresholdAngle: 45,
Expand Down Expand Up @@ -224,10 +235,14 @@ export class PanInput implements InputType {
}

protected _onPanstart(event: InputEventType) {
const inputButton = this.options.inputButton;
const { inputKey, inputButton } = this.options;
const activeEvent = this._activeEvent;
const panEvent = activeEvent.onEventStart(event, inputButton);
if (!panEvent || !this._enabled || activeEvent.getTouches(event, inputButton) > 1) {
const panEvent = activeEvent.onEventStart(event, inputKey, inputButton);
if (
!panEvent ||
!this._enabled ||
activeEvent.getTouches(event, inputButton) > 1
) {
return;
}
if (panEvent.srcEvent.cancelable !== false) {
Expand All @@ -247,12 +262,13 @@ export class PanInput implements InputType {
const {
iOSEdgeSwipeThreshold,
releaseOnScroll,
inputKey,
inputButton,
threshold,
thresholdAngle,
} = this.options;
const activeEvent = this._activeEvent;
const panEvent = activeEvent.onEventMove(event, inputButton);
const panEvent = activeEvent.onEventMove(event, inputKey, inputButton);
const touches = activeEvent.getTouches(event, inputButton);

if (
Expand Down
6 changes: 4 additions & 2 deletions packages/axes/src/inputType/RotatePanInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ export class RotatePanInput extends PanInput {
}

protected _onPanstart(event: MouseEvent) {
const { inputKey, inputButton } = this.options;
const activeEvent = this._activeEvent;
const panEvent = activeEvent.onEventStart(event, this.options.inputButton);
const panEvent = activeEvent.onEventStart(event, inputKey, inputButton);
if (!panEvent || !this.isEnabled()) {
return;
}
Expand All @@ -78,8 +79,9 @@ export class RotatePanInput extends PanInput {
}

protected _onPanmove(event: MouseEvent) {
const { inputKey, inputButton } = this.options;
const activeEvent = this._activeEvent;
const panEvent = activeEvent.onEventMove(event, this.options.inputButton);
const panEvent = activeEvent.onEventMove(event, inputKey, inputButton);
if (!panEvent || !this.isEnabled()) {
return;
}
Expand Down
14 changes: 13 additions & 1 deletion packages/axes/src/inputType/WheelInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import { DIRECTION_HORIZONTAL, DIRECTION_VERTICAL } from "../const";
import { ElementType } from "../types";

import { toAxis, InputType, InputTypeObserver } from "./InputType";
import { isValidKey } from "../eventInput/EventInput";

export interface WheelInputOption {
inputKey?: string[];
scale?: number;
releaseDelay?: number;
useNormalized?: boolean;
Expand All @@ -18,6 +20,15 @@ export interface WheelInputOption {
/**
* @typedef {Object} WheelInputOption The option object of the eg.Axes.WheelInput module
* @ko eg.Axes.WheelInput 모듈의 옵션 객체
* @param {String[]} [inputKey=[]] Allow input only when one of these keys is pressed. If the array is empty, it accepts all keys with case of no key is pressed.
* - shift: shift key
* - ctrl: ctrl key
* - alt: alt key
* - meta: meta key <ko>이 중 하나의 키가 눌린 상태에서만 입력이 허용된다. 배열이 비었다면 아무 키도 눌리지 않은 상태와 모든 키가 눌린 상태 모두 허용한다.
* - shift: shift 키
* - ctrl: ctrl 키
* - alt: alt 키
* - meta: meta 키 </ko>
* @param {Number} [scale=1] Coordinate scale that a user can move<ko>사용자의 동작으로 이동하는 좌표의 배율</ko>
* @param {Number} [releaseDelay=300] Millisecond that trigger release event after last input<ko>마지막 입력 이후 release 이벤트가 트리거되기까지의 밀리초</ko>
* @param {Boolean} [useNormalized=true] Whether to calculate scroll speed the same in all browsers<ko>모든 브라우저에서 스크롤 속도를 동일하게 처리할지 여부</ko>
Expand Down Expand Up @@ -64,6 +75,7 @@ export class WheelInput implements InputType {
this.element = $(el);
this.options = {
...{
inputKey: [],
scale: 1,
releaseDelay: 300,
useNormalized: true,
Expand Down Expand Up @@ -130,7 +142,7 @@ export class WheelInput implements InputType {
}

private _onWheel(event: WheelEvent) {
if (!this._enabled) {
if (!this._enabled || !isValidKey(event, this.options.inputKey)) {
return;
}

Expand Down
36 changes: 36 additions & 0 deletions packages/axes/test/unit/inputType/PanInput.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,42 @@ describe("PanInput", () => {
});
});

describe("inputKey", () => {
it("should not trigger events when the key set in inputKey is not pressed", (done) => {
// Given
const hold = sinon.spy();
const change = sinon.spy();
const release = sinon.spy();
input = new PanInput(el, {
inputType: ["touch", "mouse"],
inputKey: ["shift"],
});
inst.connect(["x", "y"], input);
inst.on("hold", hold);
inst.on("change", change);
inst.on("release", release);

// When
Simulator.gestures.pan(
el,
{
pos: [0, 0],
deltaX: 50,
deltaY: 50,
duration: 200,
easing: "linear",
},
() => {
// Then
expect(hold.called).to.be.equals(false);
expect(change.called).to.be.equals(false);
expect(release.called).to.be.equals(false);
done();
}
);
});
});

describe("inputButton", () => {
["left", "middle", "right"].forEach((button) => {
it("should check only the button set in inputButton is available", (done) => {
Expand Down
4 changes: 1 addition & 3 deletions packages/axes/test/unit/inputType/TestHelper.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
export default class TestHelper {
static dispatchWheel(target, direction, value, callback) {
static dispatchWheel(target, params, callback) {
if (target instanceof Element === false) {
return;
}

const params =
direction === "horizontal" ? { deltaX: value } : { deltaY: value };
let wheelEvent;

try {
Expand Down
Loading

0 comments on commit 0fa4331

Please sign in to comment.