/*
* Copyright (c) 2015 NAVER Corp.
* egjs projects are licensed under the MIT license
*/
import { OnRelease } from "@egjs/axes";
import FlickingError from "../core/FlickingError";
import * as ERROR from "../const/error";
import { getFlickingAttached } from "../utils";
import Control from "./Control";
/**
* An options for the {@link FreeControl}
* @ko {@link FreeControl} 생성시 사용되는 옵션
* @interface
* @property {boolean} stopAtEdge Make scroll animation to stop at the start/end of the scroll area, not going out the bounce area
* 스크롤 애니메이션을 스크롤 영역의 시작과 끝부분에서 멈추도록 하여, 바운스 영역을 넘어가지 않도록 합니다
*/
export interface FreeControlOptions {
stopAtEdge: boolean;
}
/**
* A {@link Control} that can be scrolled freely without alignment
* @ko 패널이 정해진 지점에 정렬되지 않고, 자유롭게 스크롤할 수 있는 이동 방식을 사용하는 {@link Control}
*/
class FreeControl extends Control {
private _stopAtEdge: FreeControlOptions["stopAtEdge"];
/**
* Make scroll animation to stop at the start/end of the scroll area, not going out the bounce area
* @ko 스크롤 애니메이션을 스크롤 영역의 시작과 끝부분에서 멈추도록 하여, 바운스 영역을 넘어가지 않도록 합니다
* @type {boolean}
* @default true
*/
public get stopAtEdge() { return this._stopAtEdge; }
public set stopAtEdge(val: FreeControlOptions["stopAtEdge"]) { this._stopAtEdge = val; }
/** */
public constructor({
stopAtEdge = true
}: Partial = {}) {
super();
this._stopAtEdge = stopAtEdge;
}
/**
* Update position after resizing
* @ko resize 이후에 position을 업데이트합니다
* @param {number} progressInPanel Previous camera's progress in active panel before resizeResize 이전 현재 선택된 패널 내에서의 카메라 progress 값
* @throws {FlickingError}
* {@link ERROR_CODE NOT_ATTACHED_TO_FLICKING} When {@link Camera#init init} is not called before
* {@link ERROR_CODE NOT_ATTACHED_TO_FLICKING} {@link Camera#init init}이 이전에 호출되지 않은 경우
* @chainable
* @return {Promise}
*/
public updatePosition(progressInPanel: number): void {
const flicking = getFlickingAttached(this._flicking);
const camera = flicking.camera;
const activePanel = this._activePanel;
if (activePanel) {
const panelRange = activePanel.range;
const newPosition = panelRange.min + (panelRange.max - panelRange.min) * progressInPanel;
camera.lookAt(camera.clampToReachablePosition(newPosition));
}
}
/**
* Move {@link Camera} to the given position
* @ko {@link Camera}를 주어진 좌표로 이동합니다
* @param {number} position The target position to move이동할 좌표
* @param {number} duration Duration of the panel movement animation (unit: ms).패널 이동 애니메이션 진행 시간 (단위: ms)
* @param {object} [axesEvent] {@link https://naver.github.io/egjs-axes/release/latest/doc/eg.Axes.html#event:release release} event of {@link https://naver.github.io/egjs-axes/ Axes}
* {@link https://naver.github.io/egjs-axes/ Axes}의 {@link https://naver.github.io/egjs-axes/release/latest/doc/eg.Axes.html#event:release release} 이벤트
* @fires Flicking#moveStart
* @fires Flicking#move
* @fires Flicking#moveEnd
* @fires Flicking#willChange
* @fires Flicking#changed
* @fires Flicking#willRestore
* @fires Flicking#restored
* @fires Flicking#needPanel
* @fires Flicking#visibleChange
* @fires Flicking#reachEdge
* @throws {FlickingError}
* |code|condition|
* |---|---|
* |{@link ERROR_CODE POSITION_NOT_REACHABLE}|When the given panel is already removed or not in the Camera's {@link Camera#range range}|
* |{@link ERROR_CODE NOT_ATTACHED_TO_FLICKING}|When {@link Control#init init} is not called before|
* |{@link ERROR_CODE ANIMATION_INTERRUPTED}|When the animation is interrupted by user input|
* |{@link ERROR_CODE STOP_CALLED_BY_USER}|When the animation is interrupted by user input|
*
*
* |code|condition|
* |---|---|
* |{@link ERROR_CODE POSITION_NOT_REACHABLE}|주어진 패널이 제거되었거나, Camera의 {@link Camera#range range} 밖에 있을 경우|
* |{@link ERROR_CODE NOT_ATTACHED_TO_FLICKING}|{@link Control#init init}이 이전에 호출되지 않은 경우|
* |{@link ERROR_CODE ANIMATION_INTERRUPTED}|사용자 입력에 의해 애니메이션이 중단된 경우|
* |{@link ERROR_CODE STOP_CALLED_BY_USER}|발생된 이벤트들 중 하나라도 `stop()`이 호출된 경우|
*
*
* @return {Promise} A Promise which will be resolved after reaching the target position해당 좌표 도달시에 resolve되는 Promise
*/
public moveToPosition(position: number, duration: number, axesEvent?: OnRelease) {
const flicking = getFlickingAttached(this._flicking);
const camera = flicking.camera;
const targetPos = camera.clampToReachablePosition(position);
const anchorAtPosition = camera.findAnchorIncludePosition(targetPos);
if (!anchorAtPosition) {
return Promise.reject(new FlickingError(ERROR.MESSAGE.POSITION_NOT_REACHABLE(position), ERROR.CODE.POSITION_NOT_REACHABLE));
}
const targetPanel = anchorAtPosition.panel;
// Trigger only change event
if (targetPanel !== this._activePanel) {
this._triggerIndexChangeEvent(targetPanel, position, axesEvent);
}
return this._animateToPosition({ position: this._stopAtEdge ? targetPos : position, duration, newActivePanel: targetPanel, axesEvent });
}
}
export default FreeControl;