Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 38 additions & 7 deletions packages/axes/src/InputObserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { InputType, InputTypeObserver, toAxis } from "./inputType/InputType";
import { EventManager, ChangeEventOption } from "./EventManager";
import { AxisManager, Axis } from "./AxisManager";
import { AxesOption } from "./Axes";
import { DIRECTION_HORIZONTAL, DIRECTION_VERTICAL } from "./const";
import {
isOutside,
getInsidePosition,
Expand Down Expand Up @@ -54,6 +55,7 @@ export class InputObserver implements InputTypeObserver {
if (this._interruptManager.isInterrupted() || !input.axes.length) {
return;
}

const changeOption: ChangeEventOption = {
input,
event,
Expand All @@ -70,17 +72,25 @@ export class InputObserver implements InputTypeObserver {
}

public change(input: InputType, event, offset: Axis, useAnimation?: boolean) {
const nativeEvent = event.srcEvent ? event.srcEvent : event;

/**
* 아래 early return 판단 조건 중, 최초 동작 축(방향)과 동일한 축인지 확인하는 조건 추가
* TODO(@gyutato): 비직관적인 변수명이므로 가능하다면 변경
*/
const isCrossInput = nativeEvent.__axesPrimaryDirection && !this._isSameAxisWithPrimary(nativeEvent, input)

/* early return condition */
if (
this._isStopped ||
!this._interruptManager.isInterrupting() ||
this._axisManager.every(offset, (v) => v === 0)
this._axisManager.every(offset, (v) => v === 0) ||
nativeEvent.__childrenAxesAlreadyChanged ||
isCrossInput
) {
return;
}
const nativeEvent = event.srcEvent ? event.srcEvent : event;
if (nativeEvent.__childrenAxesAlreadyChanged) {
return;
}

let depaPos: Axis = this._moveDistance || this._axisManager.get(input.axes);
let destPos: Axis;

Expand All @@ -102,10 +112,14 @@ export class InputObserver implements InputTypeObserver {
) {
this._isOutside = false;
}

depaPos = this._atOutside(depaPos);
destPos = this._atOutside(destPos);

if (!this.options.nested || !this._isEndofAxis(offset, depaPos, destPos)) {
if (
!this.options.nested ||
!this._isEndofAxis(offset, depaPos, destPos)
) {
nativeEvent.__childrenAxesAlreadyChanged = true;
}

Expand Down Expand Up @@ -137,14 +151,14 @@ export class InputObserver implements InputTypeObserver {
velocity: number[],
inputDuration?: number
) {
const nativeEvent = event.srcEvent ? event.srcEvent : event;
if (
this._isStopped ||
!this._interruptManager.isInterrupting() ||
!this._moveDistance
) {
return;
}
const nativeEvent = event.srcEvent ? event.srcEvent : event;
if (nativeEvent.__childrenAxesAlreadyReleased) {
velocity = velocity.map(() => 0);
}
Expand Down Expand Up @@ -270,4 +284,21 @@ export class InputObserver implements InputTypeObserver {
))
);
}

/* 최초 동작 축(방향)과 동일한 축인지 확인 */
private _isSameAxisWithPrimary(nativeEvent: any, input: InputType): boolean {
const primary = nativeEvent.__axesPrimaryDirection;

if (!primary) {
return false;
}
const hasX = !!input.axes[0];
const hasY = !!input.axes[1];
if (primary === DIRECTION_HORIZONTAL) {
return hasX && !hasY;
} else if (primary === DIRECTION_VERTICAL) {
return hasY && !hasX;
}
return false;
}
}
98 changes: 95 additions & 3 deletions packages/axes/src/inputType/PanInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
DIRECTION_HORIZONTAL,
MOUSE_LEFT,
ANY,
DIRECTION_ALL,
} from "../const";
import { ActiveEvent, ElementType, InputEventType } from "../types";

Expand Down Expand Up @@ -137,6 +138,10 @@ export class PanInput implements InputType {
private _rightEdgeTimer = 0;
private _dragged = false;
private _isOverThreshold = false;
private _isDirectionSet = false;
/* 최초 동작 축(방향) */
private _primaryDirection = DIRECTION_NONE;
private _ignoreChange = false;

/**
*
Expand Down Expand Up @@ -237,6 +242,28 @@ export class PanInput implements InputType {
return this._enabled;
}

/**
* Disables change event propagation to subsequent events
* @ko 후속 이벤트에서 change 가 동작하지 않도록 설정한다.
* @return {PanInput} An instance of a module itself <ko>모듈 자신의 인스턴스</ko>
*/
public ignoreChange() {
this._ignoreChange = true;

return this;
}

/**
* Resumes change event propagation to subsequent events
* @ko 후속 이벤트에서 change 가 동작하도록 설정한다.
* @returns {PanInput} An instance of a module itself <ko>모듈 자신의 인스턴스</ko>
*/
public resumeChange() {
this._ignoreChange = false;

return this;
}

/**
* Releases current user input.
* @ko 사용자의 입력을 강제로 중단시킨다.
Expand All @@ -255,6 +282,7 @@ export class PanInput implements InputType {
const { inputKey, inputButton, preventDefaultOnDrag } = this.options;
const activeEvent = this._activeEvent;
const panEvent = activeEvent.onEventStart(event, inputKey, inputButton);

if (
!panEvent ||
!this._enabled ||
Expand All @@ -267,6 +295,8 @@ export class PanInput implements InputType {

this._dragged = false;
this._isOverThreshold = false;
/* 최초 동작 축(방향) 초기화 */
this._primaryDirection = DIRECTION_NONE;
this._observer.hold(this, panEvent);
this._atRightEdge =
IS_IOS_SAFARI && panEvent.center.x > window.innerWidth - edgeThreshold;
Expand Down Expand Up @@ -298,7 +328,7 @@ export class PanInput implements InputType {
return;
}

if (!panEvent || !this._enabled || touches > 1) {
if (!panEvent || !this._enabled || touches > 1 || this._ignoreChange) {
return;
}

Expand All @@ -314,6 +344,21 @@ export class PanInput implements InputType {
userDirection
);

/* 현재 동작 축(방향) 결정 */
if (this._primaryDirection === DIRECTION_NONE) {
switch (true) {
case useHorizontal && !useVertical:
this._primaryDirection = DIRECTION_HORIZONTAL;
break;
case useVertical && !useHorizontal:
this._primaryDirection = DIRECTION_VERTICAL;
break;
case useHorizontal && useVertical:
this._primaryDirection = DIRECTION_ALL;
break;
}
}

if (activeEvent.prevEvent && IS_IOS_SAFARI) {
const swipeLeftToRight = panEvent.center.x < 0;

Expand Down Expand Up @@ -352,10 +397,47 @@ export class PanInput implements InputType {
panEvent.srcEvent.stopPropagation();
}
panEvent.preventSystemEvent = prevent;

/**
* 전파되는 이벤트 객체에 여태까지의 Input 객체를 저장한 스택을 추가.
* 위의 early return 조건에 의해, 이미 diasabled 상태인 경우에는 스택에 추가되지 않음
*/
// TODO(@gyutato): any 타입 캐스팅 개선 (InternalSrcEvent, ExtendedEvent 타입 정의 필요)
const panSrcEvent = panEvent?.srcEvent as Record<PropertyKey, any>
& { __inputStack?: PanInput[], __axesPrimaryDirection?: number };

if (panSrcEvent) {
const stack = panSrcEvent.__inputStack || [];
panSrcEvent.__inputStack = stack.concat([this]);
}

/**
* 부모에게 전달되는 네이티브 이벤트 객체(srcEvent)에 방향 정보 추가
*/
const hasPrimaryDirection = panSrcEvent.__axesPrimaryDirection;

// (1) 전달받은 최초 동작 축이 없고, (2) 현재 동작이 threshold 이상인 경우 축 정보를 설정
const delta = this._primaryDirection === DIRECTION_HORIZONTAL ?
activeEvent.prevEvent.deltaX :
activeEvent.prevEvent.deltaY;

if (!this._isDirectionSet && Math.abs(delta) >= threshold) {
this._isDirectionSet = true;
}

/* 이벤트를 통해 최초 방향을 전파하기 위한 조건 */
if (this._primaryDirection !== DIRECTION_NONE && panSrcEvent && !hasPrimaryDirection && this._isDirectionSet) {
panSrcEvent.__axesPrimaryDirection = this._primaryDirection;
const childrenInput = panSrcEvent.__inputStack.slice(0, -1);
childrenInput.forEach((input, index) => {
input.ignoreChange();
});
}

if (prevent && (this._isOverThreshold || distance >= threshold)) {
this._dragged = preventClickOnDrag;
this._isOverThreshold = true;
this._observer.change(this, panEvent, toAxis(this.axes, offset));
this._observer.change(this, panEvent, toAxis(this.axes, offset))
}
activeEvent.prevEvent = panEvent;
}
Expand All @@ -370,7 +452,7 @@ export class PanInput implements InputType {
this._detachWindowEvent(activeEvent);
clearTimeout(this._rightEdgeTimer);
const prevEvent = activeEvent.prevEvent;
const velocity = this._isOverThreshold ? this._getOffset(
let velocity = this._isOverThreshold ? this._getOffset(
[
Math.abs(prevEvent.velocityX) * prevEvent.directionX,
Math.abs(prevEvent.velocityY) * prevEvent.directionY,
Expand All @@ -380,8 +462,18 @@ export class PanInput implements InputType {
useDirection(DIRECTION_VERTICAL, this._direction),
]
) : [0, 0];

/* _ignoreChange 옵션에 의해 change 가 동작하지 않은 경우, velocity 를 0으로 설정 */
if (this._ignoreChange) {
velocity = [0, 0];
}

activeEvent.onRelease();
this._observer.release(this, prevEvent, velocity);
/* 최초 동작 축(방향) 초기화 */
this._primaryDirection = DIRECTION_NONE;
this._isDirectionSet = false;
this._ignoreChange = false;
}

protected _attachWindowEvent(activeEvent: ActiveEvent) {
Expand Down
Loading
Loading