diff --git a/packages/axes/package.json b/packages/axes/package.json index 511caf2f..5c6f1b50 100644 --- a/packages/axes/package.json +++ b/packages/axes/package.json @@ -3,7 +3,7 @@ "version": "3.8.0", "description": "A module used to change the information of user action entered by various input devices such as touch screen or mouse into the logical virtual coordinates. You can easily create a UI that responds to user actions.", "sideEffects": false, - "main": "dist/axes.js", + "main": "dist/axes.cjs.js", "module": "dist/axes.esm.js", "types": "declaration/index.d.ts", "scripts": { diff --git a/packages/axes/rollup.config.js b/packages/axes/rollup.config.js index eac85821..97dc69f8 100644 --- a/packages/axes/rollup.config.js +++ b/packages/axes/rollup.config.js @@ -1,8 +1,8 @@ const buildHelper = require("@egjs/build-helper"); const external = { - "@egjs/agent": "eg.agent", - "@egjs/component": "eg.Component", + "@egjs/agent": "eg.agent", + "@egjs/component": "Component", }; const name = "eg.Axes"; const fileName = "axes"; @@ -38,6 +38,12 @@ export default buildHelper([ resolve: true, uglify: true }, + { + input: "./src/index.cjs.ts", + output: `./dist/${fileName}.cjs.js`, + format: "cjs", + exports: "named", + }, { input: "./src/index.ts", output: `./dist/${fileName}.esm.js`, diff --git a/packages/axes/src/index.cjs.ts b/packages/axes/src/index.cjs.ts new file mode 100644 index 00000000..143f2af2 --- /dev/null +++ b/packages/axes/src/index.cjs.ts @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2015 NAVER Corp. + * egjs projects are licensed under the MIT license + */ +import Axes, * as modules from "./index"; + +for (const name in modules) { + (Axes as any)[name] = (modules as any)[name]; +} + +declare const module: any; +module.exports = Axes; +export default Axes; +export * from "./index"; diff --git a/packages/axes/src/inputType/PanInput.ts b/packages/axes/src/inputType/PanInput.ts index a8c8097d..f499e996 100644 --- a/packages/axes/src/inputType/PanInput.ts +++ b/packages/axes/src/inputType/PanInput.ts @@ -118,6 +118,7 @@ export class PanInput implements InputType { private _atRightEdge = false; private _rightEdgeTimer = 0; private _dragged = false; + private _isOverThreshold = false; /** * @@ -233,6 +234,7 @@ export class PanInput implements InputType { const edgeThreshold = this.options.iOSEdgeSwipeThreshold; this._dragged = false; + this._isOverThreshold = false; this._observer.hold(this, panEvent); this._atRightEdge = IS_IOS_SAFARI && panEvent.center.x > window.innerWidth - edgeThreshold; @@ -246,6 +248,7 @@ export class PanInput implements InputType { iOSEdgeSwipeThreshold, releaseOnScroll, inputButton, + threshold, thresholdAngle, } = this.options; const activeEvent = this._activeEvent; @@ -265,6 +268,16 @@ export class PanInput implements InputType { } const userDirection = getDirectionByAngle(panEvent.angle, thresholdAngle); + const useHorizontal = useDirection( + DIRECTION_HORIZONTAL, + this._direction, + userDirection + ); + const useVertical = useDirection( + DIRECTION_VERTICAL, + this._direction, + userDirection + ); if (activeEvent.prevEvent && IS_IOS_SAFARI) { const swipeLeftToRight = panEvent.center.x < 0; @@ -287,12 +300,13 @@ export class PanInput implements InputType { } } } + const distance = this._getDistance( + [panEvent.deltaX, panEvent.deltaY], + [useHorizontal, useVertical] + ); const offset = this._getOffset( [panEvent.offsetX, panEvent.offsetY], - [ - useDirection(DIRECTION_HORIZONTAL, this._direction, userDirection), - useDirection(DIRECTION_VERTICAL, this._direction, userDirection), - ] + [useHorizontal, useVertical] ); const prevent = offset.some((v) => v !== 0); @@ -303,8 +317,9 @@ export class PanInput implements InputType { panEvent.srcEvent.stopPropagation(); } panEvent.preventSystemEvent = prevent; - if (prevent) { + if (prevent && (this._isOverThreshold || distance >= threshold)) { this._dragged = true; + this._isOverThreshold = true; this._observer.change(this, panEvent, toAxis(this.axes, offset)); } activeEvent.prevEvent = panEvent; @@ -320,7 +335,7 @@ export class PanInput implements InputType { this._detachWindowEvent(activeEvent); clearTimeout(this._rightEdgeTimer); const prevEvent = activeEvent.prevEvent; - const velocity = this._getOffset( + const velocity = this._isOverThreshold ? this._getOffset( [ Math.abs(prevEvent.velocityX) * (prevEvent.offsetX < 0 ? -1 : 1), Math.abs(prevEvent.velocityY) * (prevEvent.offsetY < 0 ? -1 : 1), @@ -329,7 +344,7 @@ export class PanInput implements InputType { useDirection(DIRECTION_HORIZONTAL, this._direction), useDirection(DIRECTION_VERTICAL, this._direction), ] - ); + ) : [0, 0]; activeEvent.onRelease(); this._observer.release(this, prevEvent, velocity); } @@ -360,6 +375,13 @@ export class PanInput implements InputType { ]; } + private _getDistance(delta: number[], direction: boolean[]): number { + return Math.sqrt( + Number(direction[0]) * Math.pow(delta[0], 2) + + Number(direction[1]) * Math.pow(delta[1], 2) + ); + } + private _attachElementEvent(observer: InputTypeObserver) { const activeEvent = convertInputType(this.options.inputType); const element = this.element; diff --git a/packages/axes/src/inputType/PinchInput.ts b/packages/axes/src/inputType/PinchInput.ts index 157e6266..69044711 100644 --- a/packages/axes/src/inputType/PinchInput.ts +++ b/packages/axes/src/inputType/PinchInput.ts @@ -58,6 +58,7 @@ export class PinchInput implements InputType { private _originalCssProps: { [key: string]: string }; private _activeEvent: ActiveEvent = null; private _baseValue: number; + private _isOverThreshold = false; /** * @@ -149,10 +150,12 @@ export class PinchInput implements InputType { this._baseValue = this._observer.get(this)[this.axes[0]]; this._observer.hold(this, event); this._pinchFlag = true; + this._isOverThreshold = false; activeEvent.prevEvent = pinchEvent; } private _onPinchMove(event: InputEventType) { + const threshold = this.options.threshold; const activeEvent = this._activeEvent; const pinchEvent = activeEvent.onEventMove(event); if ( @@ -164,11 +167,16 @@ export class PinchInput implements InputType { return; } + const distance = this._getDistance(pinchEvent.scale); const offset = this._getOffset( pinchEvent.scale, activeEvent.prevEvent.scale ); - this._observer.change(this, event, toAxis(this.axes, [offset])); + + if (this._isOverThreshold || distance >= threshold) { + this._isOverThreshold = true; + this._observer.change(this, event, toAxis(this.axes, [offset])); + } activeEvent.prevEvent = pinchEvent; } @@ -233,4 +241,8 @@ export class PinchInput implements InputType { private _getOffset(pinchScale: number, prev: number = 1): number { return this._baseValue * (pinchScale - prev) * this.options.scale; } + + private _getDistance(pinchScale: number): number { + return Math.abs(pinchScale - 1); + } } diff --git a/packages/axes/test/unit/inputType/PanInput.spec.js b/packages/axes/test/unit/inputType/PanInput.spec.js index 39a4563f..caf6ab9f 100644 --- a/packages/axes/test/unit/inputType/PanInput.spec.js +++ b/packages/axes/test/unit/inputType/PanInput.spec.js @@ -361,6 +361,64 @@ describe("PanInput", () => { }); }); + describe("threshold", () => { + it("should not trigger change event when moving below threshold", (done) => { + // Given + const change = sinon.spy(); + input = new PanInput(el, { + inputType: ["touch", "mouse"], + threshold: 100, + }); + inst.connect(["x", "y"], input); + inst.on("change", change); + + // When + Simulator.gestures.pan( + el, + { + pos: [0, 0], + deltaX: 50, + deltaY: 0, + duration: 200, + easing: "linear", + }, + () => { + // Then + expect(change.called).to.be.false; + done(); + } + ); + }); + + it("should trigger change event when moving above threshold", (done) => { + // Given + const change = sinon.spy(); + input = new PanInput(el, { + inputType: ["touch", "mouse"], + threshold: 100, + }); + inst.connect(["x", "y"], input); + inst.on("change", change); + + // When + Simulator.gestures.pan( + el, + { + pos: [0, 0], + deltaX: 150, + deltaY: 0, + duration: 200, + easing: "linear", + }, + () => { + // Then + expect(change.called).to.be.true; + done(); + } + ); + }); + }); + describe("inputButton", () => { ["left", "middle", "right"].forEach((button) => { it("should check only the button set in inputButton is available", (done) => { diff --git a/packages/axes/test/unit/inputType/PinchInput.spec.js b/packages/axes/test/unit/inputType/PinchInput.spec.js index a393edde..f9733796 100644 --- a/packages/axes/test/unit/inputType/PinchInput.spec.js +++ b/packages/axes/test/unit/inputType/PinchInput.spec.js @@ -7,7 +7,7 @@ describe("PinchInput", () => { let inst; let observer; - describe("instance method", () => { + describe("Methods", () => { beforeEach(() => { inst = new PinchInput(sandbox()); }); @@ -18,7 +18,7 @@ describe("PinchInput", () => { } cleanup(); }); - it("should check status after disconnect", () => { + it("should check status is completely empty after disconnect", () => { // Given inst.connect({}); @@ -29,7 +29,7 @@ describe("PinchInput", () => { expect(observer).to.be.not.exist; expect(inst.element).to.be.exist; }); - it("should check status after destroy", () => { + it("should check status is completely empty after destroy", () => { // Given inst.connect({}); @@ -222,7 +222,7 @@ describe("PinchInput", () => { el = sandbox(); inst = new Axes({ zoom: { - range: [0, 100], + range: [1, 100], }, }); }); @@ -238,9 +238,61 @@ describe("PinchInput", () => { cleanup(); }); + describe("threshold", () => { + it("should not trigger change event when moving below threshold", (done) => { + // Given + const change = sinon.spy(); + input = new PinchInput(el, { + inputType: ["touch"], + threshold: 0.5, + }); + inst.connect("zoom", input); + inst.on("change", change); + + // When + Simulator.gestures.pinch( + el, + { + duration: 500, + scale: 1.3, + }, + () => { + // Then + expect(change.called).to.be.false; + done(); + } + ); + }); + + it("should trigger change event when moving above threshold", (done) => { + // Given + const change = sinon.spy(); + input = new PinchInput(el, { + inputType: ["touch"], + threshold: 0.5, + }); + inst.connect("zoom", input); + inst.on("change", change); + + // When + Simulator.gestures.pinch( + el, + { + duration: 500, + scale: 2, + }, + () => { + // Then + expect(change.called).to.be.true; + done(); + } + ); + }); + }); + ["auto", "none", "manipulation", "pan-x", "pan-y"].forEach( (touchAction) => { - it(`should check 'touchAction' option (${touchAction})`, () => { + it(`should check whether the style set in touchAction is applied correctly (touchAction: ${touchAction})`, () => { // Given input = new PinchInput(el, { touchAction,