빠른 시작: 조작 제스처(HTML)

[ 이 문서는 Windows 런타임 앱을 작성하는 Windows에서 8.x 및 Windows Phone 8.x 개발자를 대상으로 합니다. Windows 10용으로 개발하는 경우에는 최신 설명서를 참조하세요.]

Windows 터치 언어에 설명된 조작 제스처(예: 밀기, 살짝 밀기, 돌리기, 손가락 모으기 및 확대)에 대한 사용자 환경을 사용자 지정할 수 있습니다. 이렇게 하려면 JavaScript로 작성한 앱에서 Windows 런타임 제스처 이벤트를 처리합니다.

대부분의 앱은 제스처(회전, 확대/축소 및 끌기)를 처리하고 원시 포인터 데이터를 제스처 감지에 전달하는 것 외에는 이 데이터로 거의 작업하지 않습니다. 이 샘플에서는 조작 제스처 처리를 지원하기 위해 이 원시 포인터 데이터를 사용합니다. 이 샘플은 앱의 조작 모델을 확장하고 빠른 시작: 포인터에 설명된 기본 포인터 이벤트를 기반으로 합니다.

Windows 8.1용 업데이트: Windows 8.1에는 다양한 업데이트와 포인터 입력 API의 개선 사항이 포함되어 있습니다. 자세한 내용은 Windows 8.1의 API 변경 사항을 참조하세요.

JavaScript를 사용하여 앱을 처음 개발하는 경우: 다음 항목을 검토하여 여기서 설명하는 기술에 대해 알아보세요.

JavaScript를 사용하여 첫 번째 앱 만들기

JavaScript로 작성한 앱용 로드맵

이벤트에 대한 자세한 내용은 빠른 시작: HTML 컨트롤 추가 및 이벤트 처리를 참조하세요.

앱 기능 전체 프로세스:

앱 기능 전체 프로세스 시리즈의 일부로 이 기능을 자세히 살펴보세요.

사용자 조작 전체 프로세스(HTML)

사용자 조작 사용자 지정 전체 프로세스(HTML)

사용자 환경 지침:

플랫폼 컨트롤 라이브러리(HTMLXAML)는 표준 조작, 애니메이션 물리적 효과 및 시각적 피드백을 비롯하여 사용자 조작 환경 전체를 제공합니다. 사용자 지정 조작 지원이 필요 없는 경우에는 이러한 기본 제공 컨트롤을 사용하세요.

플랫폼 컨트롤이 충분하지 않으면 다음 사용자 조작 지침을 통해 입력 모드 전체에서 멋진 몰입형 조작 환경을 일관되게 제공합니다. 이러한 지침은 주로 터치식 입력을 중심으로 다루지만 터치 패드, 마우스, 키보드 및 스타일러스 입력에도 관련이 있습니다.

샘플: 이 기능의 작동 방식을 보려면 앱 샘플을 참조하세요.

사용자 조작 사용자 지정 전체 프로세스 샘플

입력: DOM 포인터 이벤트 처리 샘플

입력: 조작 및 제스처(JavaScript) 샘플

목표: 터치, 마우스, 펜/스타일러스 조작 및 Windows 런타임 제스처 이벤트의 입력을 사용하여 조작 제스처를 수신 대기하고 처리하는 방법을 알아봅니다.

사전 요구 사항

빠른 시작: 포인터, 빠른 시작: DOM 제스처 및 조작빠른 시작: 정적 제스처를 참조하세요.

사용자가 JavaScript용 Windows 라이브러리 템플릿을 사용하고 JavaScript로 작성된 기본 앱을 만들 수 있다고 가정합니다.

이 자습서를 완료하려면 다음을 수행해야 합니다.

제스처 이벤트란?

제스처는 입력 장치에서 입력 장치에 의해 수행되는 물리적 동작 또는 모션입니다. 여기에는 터치 화면의 손가락 하나 이상, 펜/스타일러스 디지타이저, 마우스 등이 포함될 수 있습니다. 이러한 자연스러운 조작은 시스템과 앱의 요소 작업에 매핑됩니다. 자세한 내용은 제스처, 조작 및 조작 방식을 참조하세요.

다음 표에서는 이 빠른 시작에 설명된 조작 제스처를 식별합니다. 탭, 길게 누르기 등의 정적 제스처 지원에 대해서는 빠른 시작: 정적 제스처를 참조하세요.

제스처설명
밀기밀기 제스처

거리 임계값 너머로 움직이는 하나 이상의 접촉입니다.

이동을 사용하지 않고 처음 접촉 지점이 요소의 경계 내에 있는 경우 밀면 요소가 이동합니다.

이동을 사용하고 처음 접촉 지점이 요소의 경계 내에 있으며 미는 방향이 이동 축에 수직인 경우 밀면 요소가 이동합니다. 그렇지 않으면 이동이 시작됩니다.

  • 입력 상태: 하나 이상의 접촉 지점이 감지됨
  • 동작: 끌기/밀기가 거리 임계값을 지남
  • 종료 상태: 마지막 접촉 지점에서 손을 떼거나 접촉을 끝냄
살짝 밀기살짝 밀기 제스처

거리 임계값 내에서 움직이는 하나 이상의 접촉입니다(짧은 밀기 제스처). 살짝 밀기는 속도 또는 시간 기반의 제스처가 아닙니다.

앱이 이동을 사용하지 않고 처음 접촉 지점이 요소의 경계 내에 있는 경우 살짝 밀면 요소가 이동합니다.

앱이 이동을 사용하고 처음 접촉 지점이 요소의 경계 내에 있으며 살짝 미는 방향이 이동 축에 수직인 경우 살짝 밀면 요소가 선택됩니다. 그렇지 않을 경우 살짝 밀면 이동이 시작됩니다.

  • 입력 상태: 하나 이상의 접촉 지점이 감지됨
  • 동작: 끌기/밀기가 거리 임계값을 지나지 않음
  • 종료 상태: 마지막 접촉 지점에서 손을 떼거나 접촉을 끝냄
손가락 모으기 및 확대손가락 모으기 및 확대 제스처

각각 확대 또는 축소하기 위해 모이거나 갈라지는 둘 이상의 접촉입니다.

일반적으로 개체의 크기를 조정하거나, 보기를 확대 또는 축소하거나, 시맨틱 줌에 사용됩니다.

  • 입력 상태: 개체의 경계 직사각형 내에서 둘 이상의 접촉 지점이 감지됨
  • 동작: 접촉 지점을 끌거나 밀어서 모으기 또는 갈라지기
  • 종료 상태: 두 개 미만의 접촉 지점이 감지됨
돌리기회전 제스처

두 개 이상의 손가락으로 돌리면 개체가 회전합니다. 장치 자체를 돌리면 전체 화면이 회전합니다.

회전 중심(또는 점)을 기준으로 원형 동작으로 움직이는 둘 이상의 접촉입니다.

일반적으로 개체를 회전하는 데 사용됩니다.

  • 입력 상태: 개체의 경계 직사각형 내에서 둘 이상의 접촉 지점이 감지됨
  • 동작: 하나 이상의 접촉 지점을 원형 동작으로 끌기/밀기
  • 종료 상태: 두 개 미만의 접촉 지점이 감지됨

이러한 제스처와 이 제스처가 Windows 8 터치 언어와 어떤 관련이 있는지에 대한 자세한 내용은 터치 조작 디자인을 참조하세요.

 

중요  고유한 조작 지원을 구현하는 경우 사용자들은 앱의 UI 요소를 직접 조작하는 직관적인 환경을 기대한다는 것에 유의하세요. 플랫폼 컨트롤 라이브러리(HTMLXAML)에서 항목이 일관되고 검색 가능하도록 사용자 지정 조작을 모델링하는 것이 좋습니다. 이러한 라이브러리의 컨트롤은 표준 조작, 애니메이션 효과를 준 물리적 효과, 시각적 피드백 및 접근성을 비롯하여 사용자 조작 환경 전체를 제공합니다. 요구 사항이 명확하게 잘 정의되어 있으며 기본 제스처가 시나리오를 지원하지 않는 경우에만 사용자 지정 조작을 만드세요.

 

UI 만들기

이 예제에서는 기본 UI 요소에서 단일 조작 제스처인 돌리기를 사용하도록 설정하여 회전하는 방법을 보여 줍니다. 정사각형(target)은 포인터 입력과 감지를 위한 대상 개체 역할을 합니다. 이 포인터 데이터는 회전 제스처에서 데이터를 처리하는 GestureRecognizer 개체로 전달되어 표준 관성 동작으로 회전 조작을 만듭니다.

앱은 다음과 같은 사용자 조작 기능을 제공합니다.

  • 돌리기: 포인터 접촉이 끝나면 관성을 사용하여 개체를 회전합니다. 이 조작 동작은 시각적 피드백에 대한 지침 및 Windows 터치 언어 권장 사항을 준수합니다. 이러한 권장 사항에 따르면 회전 제스처는 UI 요소의 회전으로 제한되어야 합니다. 참고  이 예제를 간단히 수정하여 끌기 및 확대/축소를 지원할 수 있습니다. 이에 대해서는 이 빠른 시간의 뒷부분에서 설명합니다.  

다음은 HTML 예문입니다.

<html>
<head>
    <meta charset="utf-8" />
    <title>Manipulation Gestures</title>
    
    <!-- WinJS references -->
    <link rel="stylesheet" href="//Microsoft.WinJS.2.0/css/ui-light.css" />
    <script src="//Microsoft.WinJS.2.0/js/base.js"></script>
    <script src="//Microsoft.WinJS.2.0/js/ui.js"></script>

    <!-- BasicGesture references -->
    <link href="/css/default.css" rel="stylesheet" />
    <script src="/js/InputProcessor.js"></script>
    <script src="/js/ManipulationManager.js"></script>
    <script src="/js/default.js"></script>
</head>
<body>
    <div class="Container" id="Container">
        <div id="targetTitle">Manipulation gestures (rotation)</div>
        <div class="TargetContainer" id="targetContainer">
            <div id="target" draggable="false"></div>
        </div>
        <div id="targetFooter">&nbsp;</div>
    </div>
</body>
</html>

다음은 CSS 스타일시트 예문입니다.

참고  이동 또는 확대/축소 조작을 수행하는 동안에는 포인터 이벤트가 종료되지 않습니다. CSS 속성 msTouchAction, overflow-ms-content-zooming을 통해 영역에서 이동 및 확대/축소를 사용하지 않도록 설정할 수 있습니다.

 

html,body {
    overflow: hidden;
    position: relative;
    height: 100%;
}

div #Container {
/*
This element permits no default touch behaviors.
A manipulation-blocking element is defined as an element that explicitly 
blocks direct manipulation via declarative markup, and instead fires gesture 
events such as MSGestureStart, MSGestureChange, and MSGestureEnd.
*/
    touch-action: none;
    display: -ms-grid;
    -ms-grid-rows: 200px 1fr 50px;
    -ms-grid-columns: 1fr;
    overflow: hidden;
    position: absolute;
    padding: 0px;
    margin: 0px;
    border-width: 0px;
    border-collapse: collapse;
}

div #targetTitle {
    touch-action: none;
    -ms-grid-row: 1;
    -ms-grid-column: 1;
    background-color: black;
    color: white;
    padding: 0px;
    margin: 0px;
    border-width: 0px;
    border-collapse: collapse;
    font-family: 'Segoe UI';
    font-size: large;
}
div #targetContainer {
    touch-action: none;
    -ms-grid-row: 2;
    -ms-grid-column: 1;
    background-color: white;
    padding: 0px;
    margin: 0px;
    border-width: 0px;
    border-collapse: collapse;
}
div #targetFooter {
    touch-action: none;
    -ms-grid-row: 3;
    -ms-grid-column: 1;
    background-color: black;
    color: white;
    padding: 0px;
    margin: 0px;
    border-width: 0px;
    border-collapse: collapse;
    font-family: 'Segoe UI';
    font-size: large;
}

div #target {
    -ms-transform-origin: 0px 0px;
    position: absolute;
    width: 300px;
    height: 300px;
    background-color: black;
    padding: 0px;
    margin: 0px;
    border-width: 0px;
    border-collapse: collapse;
}

앱 초기화

대상, 해당 컨테이너 및 앱이 시작될 때 대상에 대한 조작 처리를 설정합니다.

여기에서는 컨테이너 내에서 대상 요소 및 기타 UI 개체를 초기화하고 조작에 대해 처리기를 구성합니다.

/// <summary> 
/// Initializes the target and manipulation handling.
/// </summary>
function initialize() {
    var container = document.getElementById("targetContainer");
    var target = document.getElementById("target");
    var title = document.getElementById("targetTitle");
    var footer = document.getElementById("targetFooter");
    // Set the height of the target container for initial positioning of the target.
    var containerHeight = window.innerHeight - title.clientHeight - footer.clientHeight;
    container.style.height = containerHeight + "px";
    // Set the initial position of the target.
    target.style.msTransform = (new MSCSSMatrix()).
        translate((container.clientWidth - parseInt(target.clientWidth)) / 2.0,
        (containerHeight - parseInt(target.clientHeight)) / 2.0);
    // Configure manipulation handling.
    var manipulable = new Manipulator.ManipulationManager();
    // The configuration function can support all manipulations.
    // For this example, we limit manipulation support to rotation with inertia.
    manipulable.configure(false,
                          true, // Rotation.
                          false,
                          true, // Inertia.
                          1,
                          0,
                          {
                              x: (container.clientWidth - parseInt(target.clientWidth)) / 2.0,
                              y: (containerHeight - parseInt(target.clientHeight)) / 2.0
                          });
    manipulable.setElement(target);
    manipulable.setParent(container);
    // Handler for transforms related to the manipulation.
    manipulable.registerMoveHandler({
        x: (container.clientWidth / 2.0),
        y: (containerHeight / 2.0)
    }, Manipulator.ManipulationManager.FixPivot.MoveHandler);
}

포인터 입력 처리 및 제스처 인식기 구성

이 예제에서는 기본 래퍼 클래스(InputProcessor)를 사용하여 포인터 입력을 이용하는 GestureRecognizer 개체와 함께 포인터 이벤트 처리기를 정의합니다.

대부분의 경우 선택한 언어 프레임워크에서 포인터 이벤트 처리기의 이벤트 인수를 통해 포인터 정보를 가져오는 것이 좋습니다.

이벤트 인수가 앱에 필요한 포인터 정보를 표시하지 않으면 getCurrentPointgetIntermediatePoints 메서드 또는 currentPointintermediatePoints 속성을 통해 이벤트 인수의 확장된 포인터 데이터에 액세스할 수 있습니다. 포인터 데이터의 컨텍스트를 지정할 수 있으므로 getCurrentPointgetIntermediatePoints 메서드를 사용하는 것이 좋습니다.

  이 예제에는 제스처 인식기와 연관된 개체가 하나만 있습니다. 직소 퍼즐과 같이 조작할 수 있는 개체가 앱에 많이 포함되어 있으면 포인터 입력이 대상 개체에서 감지되는 경우에만 제스처 인식기를 동적으로 만들어 보세요. 제스처 인식기는 조작이 완료되면 삭제할 수 있습니다. 이와 관련된 예는 입력: 인스턴스화 가능한 제스처 샘플을 참조하세요. 제스처 인식기를 만들고 삭제하는 오버헤드가 발생하지 않도록 하려면 초기화 시 작은 규모의 제스처 인식기를 만들고 필요에 따라 동적으로 할당하세요.

 

제스처 인식기(_gestureRecognizer)는 모든 포인터 및 제스처 이벤트를 수신 대기하고 처리합니다.

/// <summary> 
/// InputProcessor is a thin wrapper for pointer event handling and gesture detection.
/// Defines an InputProcessor class that takes all pointer event data and feeds it to
/// a GestureRecognizer for processing of the manipulation gestures 
/// as configured in ManipulationManager.js.
/// </summary>
(function () {
    "use strict";
    WinJS.Namespace.define("Manipulator", {
        InputProcessor: WinJS.Class.define(function () {
            // Constructor.
            this._gestureRecognizer = new Windows.UI.Input.GestureRecognizer();
            this._downPoint = null;
            this._lastState = null;
        }, {
            // Instance members.
            element: {
                /// <summary> 
                /// The manipulable element.
                /// </summary>
                get: function () {
                    if (!this._element) {
                        return null;
                    }
                    return this._element;
                },
                set: function (value) {
                    this._element = value;
                    this._setupElement();
                }
            },
            parent: {
                /// <summary> 
                /// The container that defines the coordinate space used
                /// for transformations during manipulation of the target.
                /// </summary>
                get: function () {
                    if (!this._parent) {
                        return null;
                    }
                    return this._parent;
                },
                set: function (value) {
                    this._parent = value;
                }
            },
            getRecognizer: function () {
                /// <summary>
                /// The gesture recognition object.
                /// </summary>
                return this._gestureRecognizer;
            },
            getDown: function () {
                /// <summary>
                /// The pointer data for the pointerdown event.
                /// </summary>
                return this._downPoint;
            },
            _setupElement: function () {
                /// <summary> 
                /// Declare the event listeners for the pointer events on the target.
                /// </summary>
                var that = this;
                this._element.addEventListener("pointerdown",
                    function (evt) { Manipulator.InputProcessor._handleDown(that, evt); },
                    false);
                this._element.addEventListener("pointermove",
                    function (evt) { Manipulator.InputProcessor._handleMove(that, evt); },
                    false);
                this._element.addEventListener("pointerup",
                    function (evt) { Manipulator.InputProcessor._handleUp(that, evt); },
                    false);
                this._element.addEventListener("pointercancel",
                    function (evt) { Manipulator.InputProcessor._handleCancel(that, evt); },
                    false);
                this._element.addEventListener("wheel",
                    function (evt) { Manipulator.InputProcessor._handleMouse(that, evt); },
                    false);
            }
        }, {
            // Static members.
            _handleDown: function (that, evt) {
                /// <summary> 
                /// Handler for the pointerdown event.
                /// </summary>
                /// <param name="that" type="Object">
                /// The InputProcessor object handling this event.
                /// </param>
                /// <param name="evt" type="Event">
                /// The event object.
                /// </param>
                var pp = evt.getCurrentPoint(that._parent);
                that._element.setPointerCapture(pp.pointerId);
                that._gestureRecognizer.processDownEvent(pp);

                // Prevent propagation of this event to additional event handlers.
                evt.stopImmediatePropagation();

                // Capture the pointer location for this event.
                that._downPoint = { x: pp.position.x, y: pp.position.y };
            },
            _handleMove: function (that, evt) {
                /// <summary> 
                /// Handler for the pointermove event.
                /// </summary>
                /// <param name="that" type="Object">
                /// The InputProcessor object handling this event.
                /// </param>
                /// <param name="evt" type="Event">
                /// The event object.
                /// </param>
                var pps = evt.getIntermediatePoints(that._parent);
                that._gestureRecognizer.processMoveEvents(pps);

                // Prevent propagation of this event to additional event handlers.
                evt.stopImmediatePropagation();
            },
            _handleUp: function (that, evt) {
                /// <summary> 
                /// Handler for the pointerup event.
                /// </summary>
                /// <param name="that" type="Object">
                /// The InputProcessor object handling this event.
                /// </param>
                /// <param name="evt" type="Event">
                /// The event object.
                /// </param>
                var pp = evt.getCurrentPoint(that._parent);
                that._gestureRecognizer.processUpEvent(pp);

                // Prevent propagation of this event to additional event handlers.
                evt.stopImmediatePropagation();
            },
            _handleCancel: function (that, evt) {
                /// <summary> 
                /// Handler for the pointercancel event.
                /// </summary>
                /// <param name="that" type="Object">
                /// The InputProcessor object handling this event.
                /// </param>
                /// <param name="evt" type="Event">
                /// The event object.
                /// </param>
                that._gestureRecognizer.completeGesture();

                // Prevent propagation of this event to additional event handlers.
                evt.stopImmediatePropagation();
            },
            _handleMouse: function (that, evt) {
                /// <summary> 
                /// Handler for the mouse wheel event.
                /// </summary>
                /// <param name="that" type="Object">
                /// The InputProcessor object handling this event.
                /// </param>
                /// <param name="evt" type="Event">
                /// The event object.
                /// </param>
                var pp = evt.getCurrentPoint(that._parent);
                that._gestureRecognizer.processMouseWheelEvent(pp, evt.shiftKey, evt.ctrlKey);

                // Prevent propagation of this event to additional event handlers.
                evt.stopImmediatePropagation();
                evt.preventDefault();
            }
        })
    });
})();

조작을 처리합니다.

여기에서는 조작 관리자 클래스(ManipulationManager)를 사용하여 GestureRecognizer 개체에 대한 조작 동작 및 제약 조건을 정의합니다. 이 개체는 이전 단계에서 설명한 InputProcessor(_inputProcessor)에 정의되어 있습니다.

/// <summary> 
/// ManipulationManager is the manipulation processing engine for the 
/// GestureRecognizer object defined in InputProcessor.js.
/// Different components and behaviors of manipulation (rotate, translate, zoom, 
/// and inertia) can be enabled, disabled, and customized as required.
/// </summary>
(function () {
    "use strict";
    WinJS.Namespace.define("Manipulator", {
        ManipulationManager: WinJS.Class.define(function () {
            // Constructor.
            // Create an input processor.
            this._inputProcessor = new Manipulator.InputProcessor();
            // Initialize the manipulation movement and end handlers.
            this._endHandler = null;
            this._moveHandler = null;
            // Create the transform matrices used for manipulating
            // and resetting the target.
            this._currentTransform = new MSCSSMatrix();
            this._initialTransform = new MSCSSMatrix();
            // Initialize the transform matrices values.
            this._initialTransformParams = {
                translation: { x: 0, y: 0 },
                rotation: 0,
                scale: 1
            };
            this._currentTransformParams = {
                translation: { x: 0, y: 0 },
                rotation: 0,
                scale: 1
            };
        }, {
            // Instance members.
            configure: function (scale, rotate, translate, inertia,
                                initialScale, initialRotate, initialTranslate) {
                /// <summary> 
                /// Define the behaviors of the ManipulationManager object.
                /// </summary>
                /// <param name="scale" type="Boolean">
                /// True if scaling is enabled.
                /// </param>
                /// <param name="rotate" type="Boolean">
                /// True if rotation is enabled.
                /// </param>
                /// <param name="translate" type="Boolean">
                /// True if translation is enabled.
                /// </param>
                /// <param name="inertia" type="Boolean">
                /// True if inertia is enabled.
                /// </param>
                /// <param name="initialScale" type="Number">
                /// The initial scale factor.
                /// </param>
                /// <param name="initialRotate" type="Number">
                /// The initial rotation value.
                /// </param>
                /// <param name="initialTranslate" type="Object">
                /// The initial translation values (x,y).
                /// </param>

                // Get the GestureRecognizer associated with this manipulation manager.
                var gr = this._inputProcessor.getRecognizer();
                // Set the manipulations supported by the GestureRecognizer if the
                // interaction is not already being processed.
                if (!gr.isActive) {
                    var settings = 0;
                    if (scale) {
                        settings |= Windows.UI.Input.GestureSettings.manipulationScale;
                        if (inertia) {
                            settings |= Windows.UI.Input.GestureSettings.manipulationScaleInertia;
                        }
                    }
                    if (rotate) {
                        settings |= Windows.UI.Input.GestureSettings.manipulationRotate;
                        if (inertia) {
                            settings |= Windows.UI.Input.GestureSettings.manipulationRotateInertia;
                        }
                    }
                    if (translate) {
                        settings |= Windows.UI.Input.GestureSettings.manipulationTranslateX |
                            Windows.UI.Input.GestureSettings.manipulationTranslateY;
                        if (inertia) {
                            settings |= Windows.UI.Input.GestureSettings.manipulationTranslateInertia;
                        }
                    }

                    // Cache a reference to the current object.
                    var that = this;

                    // If any manipulation is supported, declare the manipulation event listeners.
                    if (scale || rotate || translate) {
                        gr.addEventListener('manipulationstarted',
                            function (evt) { Manipulator.ManipulationManager._manipulationStarted(that, evt); },
                            false);
                        gr.addEventListener('manipulationupdated',
                            function (evt) { Manipulator.ManipulationManager._manipulationUpdated(that, evt); },
                            false);
                        gr.addEventListener('manipulationended',
                            function (evt) { Manipulator.ManipulationManager._manipulationEnded(that, evt); },
                            false);
                    }

                    gr.gestureSettings = settings;

                    // Initialize the transform matrices.
                    this._currentTransformParams.scale = initialScale;
                    this._currentTransformParams.rotation = initialRotate;
                    this._currentTransformParams.translation = initialTranslate;

                    this._initialTransformParams.scale = initialScale;
                    this._initialTransformParams.rotation = initialRotate;
                    this._initialTransformParams.translation = initialTranslate;

                    // Set the transformation values.
                    if (initialRotate) {
                        this._initialTransform = this._initialTransform.rotate(initialRotate);
                    }
                    else {
                        this._currentTransformParams.rotation = 0;
                        this._initialTransformParams.rotation = 0;
                    }
                    if (initialTranslate) {
                        this._initialTransform = this._initialTransform.translate(initialTranslate.x, initialTranslate.y);
                    }
                    else {
                        this._currentTransformParams.translation = { x: 0, y: 0 };
                        this._initialTransformParams.translation = { x: 0, y: 0 };
                    }
                    if (initialScale) {
                        this._initialTransform = this._initialTransform.scale(initialScale);
                    }
                    else {
                        this._currentTransformParams.scale = 1;
                        this._initialTransformParams.scale = 1;
                    }

                    this._currentTransform = this._initialTransform;
                }
            },
            setElement: function (elm) {
                /// <summary> 
                /// Set the manipulable object.
                /// </summary>
                /// <param name="elm" type="Object">
                /// The object that supports manipulation.
                /// </param>
                this._inputProcessor.element = elm;
                // Set the transform origin for rotation and scale manipulations.
                this._inputProcessor.element.style.msTransformOrigin = "0 0";
            },
            setParent: function (elm) {
                /// <summary> 
                /// Set the parent of the manipulable object.
                /// </summary>
                /// <param name="elm" type="Object">
                /// The parent of the object that supports manipulation.
                /// </param>
                this._inputProcessor.parent = elm;
            },
            registerEndHandler: function (handler) {
                /// <summary> 
                /// Register handler to be called after the manipulation is complete.
                /// </summary>
                /// <param name="handler" type="Function">
                /// The manipulationended event handler.
                /// </param>
                this._endHandler = handler;
            },
            registerMoveHandler: function (arg, handler) {
                /// <summary> 
                /// Register handler to be called when manipulation is under way.
                /// </summary>
                /// <param name="args">
                /// Arguments passed to the move handler function.
                /// </param>
                /// <param name="handler" type="Function">
                /// The manipulationupdated event handler.
                /// </param>
                this._moveHandlerArg = arg;
                this._moveHandler = handler;
            },
            resetAllTransforms: function () {
                /// <summary> 
                /// Reset the ManipulationManager object to its initial state.
                /// </summary>

                // Check that the element has been registered before before attempting to reset.
                if (this._inputProcessor.element) {
                    // Reapply the initial transform
                    this._inputProcessor.element.style.transform = this._initialTransform.toString();
                    this._currentTransform = this._initialTransform;

                    // Reset the current transform parameters to their initial values.
                    this._currentTransformParams.translation = this._initialTransformParams.translation;
                    this._currentTransformParams.rotation = this._initialTransformParams.rotation;
                    this._currentTransformParams.scale = this._initialTransformParams.scale;
                }
            },

            _applyMotion: function (pivot, translation, rotation, scaling) {
                /// <summary> 
                /// Apply the manipulation transform to the target.
                /// </summary>
                /// <param name="pivot" type="Object">
                /// The X,Y values for the rotation and scaling pivot point.
                /// </param>
                /// <param name="translation" type="Object">
                /// The X,Y values for the translation delta.
                /// </param>
                /// <param name="rotation" type="Number">
                /// The angle of rotation.
                /// </param>
                /// <param name="scaling" type="Number">
                /// The scaling factor.
                /// </param>

                // Create the transform, apply parameters, and multiply by the current transform matrix.
                var transform = new MSCSSMatrix().translate(pivot.x, pivot.y).
                    translate(translation.x, translation.y).
                    rotate(rotation).
                    scale(scaling).
                    translate(-pivot.x, -pivot.y).multiply(this._currentTransform);

                this._inputProcessor.element.style.transform = transform.toString();
                this._currentTransform = transform;
            },

            _updateTransformParams: function (delta) {
                /// <summary> 
                /// Update the current transformation parameters based on the new delta.
                /// </summary>
                /// <param name="that" type="Object">
                /// The change in rotation, scaling, and translation.
                /// </param>
                this._currentTransformParams.translation.x = this._currentTransformParams.translation.x + delta.translation.x;
                this._currentTransformParams.translation.y = this._currentTransformParams.translation.y + delta.translation.y;
                this._currentTransformParams.rotation = this._currentTransformParams.rotation + delta.rotation;
                this._currentTransformParams.scale = this._currentTransformParams.scale * delta.scale;
            }
        }, {
            // Static members.
            _manipulationStarted: function (that, evt) {
                /// <summary> 
                /// The manipulationstarted event handler.
                /// </summary>
                /// <param name="that" type="Object">
                /// ManipulationManager object on which the event was performed.
                /// </param>
                /// <param name="evt" type="Event">
                /// The event data.
                /// </param>
                Manipulator.ManipulationManager._manipulationHelper(that, evt);
            },
            _manipulationUpdated: function (that, evt) {
                /// <summary> 
                /// The manipulationupdated event handler.
                /// </summary>
                /// <param name="that" type="Object">
                /// ManipulationManager object on which the event was performed.
                /// </param>
                /// <param name="evt" type="Event">
                /// The event data.
                /// </param>
                Manipulator.ManipulationManager._manipulationHelper(that, evt);
            },
            _manipulationEnded: function (that, evt) {
                /// <summary> 
                /// The manipulationended event handler.
                /// </summary>
                /// <param name="that" type="Object">
                /// ManipulationManager object on which the event was performed.
                /// </param>
                /// <param name="evt" type="Event">
                /// The event data.
                /// </param>
                // Pass the event to the manipulation helper function.
                Manipulator.ManipulationManager._manipulationHelper(that, evt);

                // Call the manipulationended handler, if registered.
                if (that._endHandler) {
                    that._endHandler();
                }
            },
            _manipulationHelper: function (that, evt) {
                /// <summary> 
                /// Helper function for calculating and applying the transformation parameter deltas.
                /// </summary>
                /// <param name="that" type="Object">
                /// ManipulationManager object on which the event was performed.
                /// </param>
                /// <param name="evt" type="Event">
                /// The event data.
                /// </param>

                if (evt.delta) {
                    // Rotation/scaling pivot point.
                    var pivot = { x: evt.position.x, y: evt.position.y };

                    // Translation values.
                    var translation = { x: evt.delta.translation.x, y: evt.delta.translation.y };

                    // Rotation angle.
                    var rotation = evt.delta.rotation;

                    // Scale factor.
                    var scale = evt.delta.scale;

                    // Group the transformation parameter deltas.
                    var delta = {
                        pivot: pivot,
                        translation: translation,
                        rotation: rotation,
                        scale: scale
                    };

                    // Apply the manipulation movement constraints.
                    if (that._moveHandler) {
                        delta = that._moveHandler(that._moveHandlerArg, delta, that._currentTransformParams, that._currentTransform);
                    }

                    // Update the transformation parameters with fresh deltas.
                    that._updateTransformParams(delta);

                    // Apply the transformation.
                    that._applyMotion(delta.pivot, delta.translation, delta.rotation, delta.scale);
                }
            },
            FixPivot: WinJS.Class.define(function () {
            /// <summary>
            /// Constrain the center of manipulation (or pivot point) to a set of X,Y coordinates,  
            /// instead of the centroid of the pointers associated with the manipulation.
            /// <param name="pivot" type="Object">
            /// The pivot coordinates for the ManipulationManager object.
            /// </param>
            /// <param name="delta" type="Object">
            /// The transformation parameter deltas (pivot, delta, rotation, scale).
            /// </param>
            /// </summary>
            }, {
            }, {
                MoveHandler: function (pivot, delta) {
                    delta.pivot = pivot;
                    return delta;
                }
            }),
        })
    });
})();

좀 더 복잡한 샘플 링크는 이 페이지 하단에 있는 관련 항목을 참조하세요.

전체 예제

조작 제스처 전체 코드를 참조하세요.

요약 및 다음 단계

이 빠른 시작에서는 JavaScript를 사용하는 Windows 스토어 앱에서 조작 제스처 이벤트를 처리하는 방법에 대해 알아보았습니다.

조작 제스처는 밀어서 이동 또는 다시 정렬/이동, 확대/축소 및 회전 등의 복잡한 조작을 지원 및 관리하려는 경우 유용합니다.

제스처 처리의 보다 복잡한 예제는 입력: 인스턴스화 가능한 제스처 샘플을 참조하세요.

참고  이 샘플은 사용자 지정 조작과 관련된 Windows 터치 언어 지침을 준수하지 않습니다. 일부 정적 제스처는 설명을 위해 다시 정의되었습니다.

 

정적 조작(예: 밀기, 살짝 밀기, 돌리기, 손가락 모으기 및 확대)을 관리하는 방법에 대한 자세한 내용은 빠른 시작: 정적 제스처를 참조하세요.

Windows 8 터치 언어에 대한 자세한 내용은 터치 조작 디자인을 참조하세요.

관련 항목

개발자

사용자 조작에 응답

Windows 스토어 앱 개발(JavaScript 및 HTML)

빠른 시작: 포인터

빠른 시작: DOM 제스처 및 조작

빠른 시작: 정적 제스처

디자이너

터치 조작 디자인