Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add inputKey option to PanInput and WheelInput #204

Merged
merged 10 commits into from
Oct 8, 2022
Merged
2 changes: 1 addition & 1 deletion packages/axes/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
"sinon": "^4.1.4",
"string-replace-webpack-plugin": "0.1.3",
"sync-exec": "^0.6.2",
"ts-loader": "^8.0.6",
"ts-loader": "8.0.6",
"typescript": "^4.6.2",
"uglifyjs-webpack-plugin": "^1.1.6",
"webpack": "^3.10.0",
Expand Down
7 changes: 7 additions & 0 deletions packages/axes/src/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ export const MOUSE_LEFT = "left";
export const MOUSE_RIGHT = "right";
export const MOUSE_MIDDLE = "middle";

export const ANY = "any";
export const NONE = "none";
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
47 changes: 45 additions & 2 deletions packages/axes/src/eventInput/EventInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@ import { ExtendedEvent, InputEventType, LatestInterval } from "../types";
import { getAngle } from "../utils";
import { window } from "../browser";
import {
ALT,
ANY,
CTRL,
META,
MOUSE_LEFT,
MOUSE_MIDDLE,
MOUSE_RIGHT,
NONE,
SHIFT,
VELOCITY_INTERVAL,
} from "../const";

Expand All @@ -17,6 +23,28 @@ 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.indexOf(ANY) > -1 ||
(inputKey.indexOf(NONE) > -1 &&
!event.shiftKey &&
!event.ctrlKey &&
!event.altKey &&
!event.metaKey) ||
(inputKey.indexOf(SHIFT) > -1 && event.shiftKey) ||
(inputKey.indexOf(CTRL) > -1 && event.ctrlKey) ||
(inputKey.indexOf(ALT) > -1 && event.altKey) ||
(inputKey.indexOf(META) > -1 && event.metaKey)
) {
return true;
}
return false;
};

export abstract class EventInput {
public prevEvent: ExtendedEvent;
private _latestInterval: LatestInterval;
Expand All @@ -35,7 +63,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 +144,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))
Comment on lines +160 to +161
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't every event valid when there're no items in inputKey and inputButton in this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since array is type of object, an empty Array is conversed to true and !inputButton is conversed to false.
The reason to check !inputButton here is that inputButton is optional like in the case of a pinch gesture where there is no inputButton option.
But for what you said, it seemed nice to add the case where inputButton is an empty array to the unit test, so I added it.

);
}

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
29 changes: 25 additions & 4 deletions packages/axes/src/inputType/PanInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
DIRECTION_VERTICAL,
DIRECTION_HORIZONTAL,
MOUSE_LEFT,
ANY,
} from "../const";
import { ActiveEvent, ElementType, InputEventType } from "../types";

Expand All @@ -31,6 +32,7 @@ import {

export interface PanInputOption {
inputType?: string[];
inputKey?: string[];
inputButton?: string[];
scale?: number[];
thresholdAngle?: number;
Expand Down Expand Up @@ -66,6 +68,19 @@ export const getDirectionByAngle = (
* - touch: 터치 입력 장치
* - mouse: 마우스
* - pointer: 마우스 및 터치</ko>
* @param {String[]} [inputKey=["any"]] List of key combinations to allow input
* - any: any key
* - shift: shift key
* - ctrl: ctrl key and pinch gesture on the trackpad
* - alt: alt key
* - meta: meta key
* - none: none of these keys are pressed <ko>입력을 허용할 키 조합 목록
* - any: 아무 키
* - shift: shift 키
* - ctrl: ctrl 키 및 트랙패드의 pinch 제스쳐
* - alt: alt 키
* - meta: meta 키
* - none: 아무 키도 눌리지 않은 상태 </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 +142,7 @@ export class PanInput implements InputType {
this.element = $(el);
this.options = {
inputType: ["touch", "mouse", "pointer"],
inputKey: [ANY],
inputButton: [MOUSE_LEFT],
scale: [1, 1],
thresholdAngle: 45,
Expand Down Expand Up @@ -224,10 +240,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 +267,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
30 changes: 22 additions & 8 deletions packages/axes/src/inputType/WheelInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
* egjs projects are licensed under the MIT license
*/
import { $, getDirection, useDirection } from "../utils";
import { DIRECTION_HORIZONTAL, DIRECTION_VERTICAL } from "../const";
import { ANY, 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,19 @@ export interface WheelInputOption {
/**
* @typedef {Object} WheelInputOption The option object of the eg.Axes.WheelInput module
* @ko eg.Axes.WheelInput 모듈의 옵션 객체
* @param {String[]} [inputKey=["any"]] List of key combinations to allow input
* - any: any key
* - shift: shift key
* - ctrl: ctrl key and pinch gesture on the trackpad
* - alt: alt key
* - meta: meta key
* - none: none of these keys are pressed <ko>입력을 허용할 키 조합 목록
* - any: 아무 키
* - shift: shift 키
* - ctrl: ctrl 키 및 트랙패드의 pinch 제스쳐
* - alt: alt 키
* - meta: meta 키
* - none: 아무 키도 눌리지 않은 상태 </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 @@ -63,12 +78,11 @@ export class WheelInput implements InputType {
public constructor(el: ElementType, options?: WheelInputOption) {
this.element = $(el);
this.options = {
...{
scale: 1,
releaseDelay: 300,
useNormalized: true,
useAnimation: false,
},
inputKey: [ANY],
scale: 1,
releaseDelay: 300,
useNormalized: true,
useAnimation: false,
...options,
};
this._onWheel = this._onWheel.bind(this);
Expand Down Expand Up @@ -130,7 +144,7 @@ export class WheelInput implements InputType {
}

private _onWheel(event: WheelEvent) {
if (!this._enabled) {
if (!this._enabled || !isValidKey(event, this.options.inputKey)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wheel will have to be careful.

Multi-touch in the browser works with ctrl + wheel.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since ctrl + wheel also has a zoom function in the browser, it looks like it should be handled with preventDefault.
In Axes, preventDefault is already applied for ctrl + wheel, can be there any additional processing needed?

return;
}

Expand Down
Loading