/* * Copyright (c) 2015 NAVER Corp. * egjs projects are licensed under the MIT license */ import Axes from "../Axes"; import { ElementType, ExtendedEvent } from "../types"; import { getAngle } from "../utils"; import { toAxis } from "./InputType"; import { PanInput, PanInputOption } from "./PanInput"; /** * A module that passes the angle moved by touch to Axes and uses one axis of rotation. * [Details](https://github.com/naver/egjs-axes/wiki/RotatePanInput) * @ko 터치에 의해 움직인 각도를 Axes 에 전달하며 1개의 회전축만 사용한다. * [상세내용](https://github.com/naver/egjs-axes/wiki/RotatePanInput-%7C-%ED%95%9C%EA%B5%AD%EC%96%B4) * * @example * ```js * const input = new eg.Axes.RotatePanInput("#area"); * * var axes = new eg.Axes({ * // property name('angle') could be anything you want (eg. x, y, z...) * angle: { * range: [-180, 180] // from -180deg to 180deg * } * }); * * axes.connect("angle", input) * ``` * @param {HTMLElement|String|jQuery} element An element to use the eg.Axes.RotatePanInput module eg.Axes.RotatePanInput 모듈을 사용할 엘리먼트 * @param {PanInputOption} [options] The option object of the eg.Axes.PanInput moduleeg.Axes.PanInput 모듈의 옵션 객체 * @extends PanInput */ export class RotatePanInput extends PanInput { private _rotateOrigin: number[]; private _prevAngle: number; private _prevQuadrant: number = null; private _lastDiff = 0; private _coefficientForDistanceToAngle: number; /** * */ public constructor(el: ElementType, options?: PanInputOption) { super(el, options); } public mapAxes(axes: string[]) { this._direction = Axes.DIRECTION_ALL; this.axes = axes; } protected _onPanstart(event: MouseEvent) { const { inputKey, inputButton } = this.options; const activeEvent = this._activeEvent; const panEvent = activeEvent.onEventStart(event, inputKey, inputButton); if (!panEvent || !this.isEnabled()) { return; } const rect = this.element.getBoundingClientRect(); this._observer.hold(this, panEvent); this._attachWindowEvent(activeEvent); // TODO: how to do if element is ellipse not circle. this._coefficientForDistanceToAngle = 360 / (rect.width * Math.PI); // from 2*pi*r * x / 360 // TODO: provide a way to set origin like https://developer.mozilla.org/en-US/docs/Web/CSS/transform-origin this._rotateOrigin = [ rect.left + (rect.width - 1) / 2, rect.top + (rect.height - 1) / 2, ]; // init angle. this._prevAngle = null; this._triggerChange(panEvent); activeEvent.prevEvent = panEvent; } protected _onPanmove(event: MouseEvent) { const { inputKey, inputButton } = this.options; const activeEvent = this._activeEvent; const panEvent = activeEvent.onEventMove(event, inputKey, inputButton); if (!panEvent || !this.isEnabled()) { return; } if (panEvent.srcEvent.cancelable !== false) { panEvent.srcEvent.preventDefault(); } panEvent.srcEvent.stopPropagation(); this._triggerChange(panEvent); activeEvent.prevEvent = panEvent; } protected _onPanend(event: MouseEvent) { const activeEvent = this._activeEvent; activeEvent.onEventEnd(event); if (!this.isEnabled()) { return; } const prevEvent = activeEvent.prevEvent; this._triggerChange(prevEvent); const vx = prevEvent.velocityX; const vy = prevEvent.velocityY; const velocity = Math.sqrt(vx * vx + vy * vy) * (this._lastDiff > 0 ? -1 : 1); // clockwise activeEvent.onRelease(); this._observer.release(this, prevEvent, [ velocity * this._coefficientForDistanceToAngle, ]); this._detachWindowEvent(activeEvent); } private _triggerChange(event: ExtendedEvent) { const { x, y } = this._getPosFromOrigin(event.center.x, event.center.y); const angle = getAngle(x, y); const positiveAngle = angle < 0 ? 360 + angle : angle; const quadrant = this._getQuadrant(event.center.x, event.center.y); const diff = this._getDifference( this._prevAngle, positiveAngle, this._prevQuadrant, quadrant ); this._prevAngle = positiveAngle; this._prevQuadrant = quadrant; if (diff === 0) { return; } this._lastDiff = diff; this._observer.change(this, event, toAxis(this.axes, [-diff])); // minus for clockwise } private _getDifference( prevAngle: number, angle: number, prevQuadrant: number, quadrant: number ) { let diff: number; if (prevAngle === null) { diff = 0; } else if (prevQuadrant === 1 && quadrant === 4) { diff = -prevAngle - (360 - angle); } else if (prevQuadrant === 4 && quadrant === 1) { diff = 360 - prevAngle + angle; } else { diff = angle - prevAngle; } return diff; } private _getPosFromOrigin(posX: number, posY: number) { return { x: posX - this._rotateOrigin[0], y: this._rotateOrigin[1] - posY, }; } private _getQuadrant(posX: number, posY: number) { /** * Quadrant * y(+) * | * 2 | 1 * --------------->x(+) * 3 | 4 * | */ const { x, y } = this._getPosFromOrigin(posX, posY); let q = 0; if (x >= 0 && y >= 0) { q = 1; } else if (x < 0 && y >= 0) { q = 2; } else if (x < 0 && y < 0) { q = 3; } else if (x >= 0 && y < 0) { q = 4; } return q; } }