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

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

기본 DOM(문서 개체 모델) 제스처 이벤트 처리를 통해 Windows 터치 언어에 설명된 일부 기본 제스처(예제: 밀기, 회전 및 크기 조정)에 대한 사용자 환경을 사용자 지정할 수 있습니다.

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

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

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

JavaScript로 작성한 앱용 로드맵

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

앱 기능 전체 프로세스:

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

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

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

사용자 환경 지침:

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

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

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

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

HTML 스크롤, 이동 및 확대/축소 샘플

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

입력: 인스턴스화 가능한 제스처 샘플

목표: 터치, 마우스, 펜/스타일러스 조작 및 DOM 제스처 이벤트의 입력을 사용하여 변환, 회전 및 크기 조정에 대한 기본 제스처를 수신 대기하고 처리하는 방법을 알아봅니다.

사전 요구 사항

빠른 시작: 포인터를 참조하세요.

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

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

완료 시간: 30 분.

제스처 이벤트란?

제스처는 입력 장치에서 또는 입력 장치(터치 표면의 하나 이상 손가락, 펜/스타일러스 디지타이저, 마우스 등)에 의해 수행되는 실제 동작 또는 움직임입니다. 이러한 자연스러운 조작은 시스템과 앱의 요소 작업에 매핑됩니다. 자세한 내용은 제스처, 조작 및 조작 방식을 참조하세요.

Windows는 UI와 연결하고 UI를 조작하기 위해 기본적인 제스처 모음에 많이 의존합니다.

제스처설명
탭하기탭하기 제스처

접촉이 한 번 감지되면 즉시 손가락을 뗍니다.

요소를 탭하면 기본 동작이 호출됩니다.

길게 누르기길게 누르기 제스처

접촉이 한 번 감지되고 이동하지 않습니다.

길게 누르기는 어떤 동작을 수행하는 게 아니라 세부 정보 또는 교육용 화면 효과(예제: 도구 설명, 상황에 맞는 메뉴)를 표시합니다.

밀기밀기 제스처

접촉이 한 번 이상 감지되면 같은 방향으로 움직입니다.

밀기는 주로 이동 조작에 사용하지만 이동, 그리기 또는 쓰기에 사용할 수도 있습니다.

살짝 밀기살짝 밀기 제스처

접촉이 한 번 이상 감지되면 같은 방향으로 짧게 움직입니다.

살짝 밀어 선택, 명령하고 이동합니다.

회전회전 제스처

접촉이 두 번 이상 감지되면 시계 방향으로 또는 시계 반대 방향으로 호를 그리며 회전합니다.

돌려서 회전합니다.

손가락 모으기축소 제스처

접촉이 두 번 이상 감지되면 모으면서 움직입니다.

손가락을 모아 축소합니다.

손가락 늘이기확대 제스처

접촉이 두 번 이상 감지되면 늘리면서 움직입니다.

손가락을 늘여 확대합니다.

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

 

제스처 감지를 사용하여 앱의 조작 모델을 확장하고, 빠른 시작: 포인터 입력 처리에 설명된 기본 포인터 이벤트를 바탕으로 빌드할 수 있습니다. 실제로 앱은 대부분 제스처 이벤트(예제: 탭 처리, 밀기로 이동, 손가락을 모으거나 확대하여 확대/축소)를 사용하고, 원시 포인터 데이터를 사용하여 제스처 감지 및 처리를 지원합니다.

앱은 여러 제스처(예제: 확대/축소 및 회전)를 동시에 처리하고, 특정 요소를 대상으로 하도록 포인터 접촉을 그룹화하고(예제: 모든 접촉과 초기 또는 기본 접촉 대상 연결), 특정 제스처 또는 포인터 접촉을 통해 대상으로 지정되는 특정 요소를 식별할 수 있습니다.

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

 

UI 만들기

다음 예에서는 직사각형(target)을 포인터 입력 및 제스처 감지와 처리를 위한 대상 개체로 사용합니다.

직사각형은 기본 색상 믹서로 작동합니다. 대상의 색상은 RGB 색상 선택(빨강, 녹색 또는 파랑) 및 회전 제스처를 통해 보고된 대상의 회전 각도를 기준으로 변경됩니다. 회전 각도로 빨강, 녹색 또는 파랑 값을 계산합니다.

대상 개체 내에서 대상에 적용되는 현재 변형 매트릭스와 함께 포인터와 제스처 이벤트별로 세부 정보를 표시합니다.

다음은 HTML 예문입니다.

<html>
<head>
    <meta charset="utf-8" />
    <title>PointerInput</title>

    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.2.0/css/ui-dark.css" rel="stylesheet" />
    <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/default.js"></script>
</head>
<body>
    <div class="TargetContainer" id="targetContainer">
        <div id="colorMixer">
            <input type="radio" name="color" value="R" title="Red" id="red" class="Red" /><label for="red" id="labelRed">Red</label>
            <input type="radio" name="color" value="G" title="Green" id="green" class="Green" /><label for="green" id="labelGreen">Green</label>
            <input type="radio" name="color" value="B" title="Blue" id="blue" class="Blue" /><label for="blue" id="labelBlue">Blue</label>
            <div id="targetLog"></div>
            <div id="eventLog"></div>
        </div>
    </div>
</body>
</html>

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

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

 

body {
    overflow: hidden;
    position: relative;
}

div #targetContainer {
/*
Set the width and height properties of the target container to fill the viewport. 
You can set these properties to 100%, but we use 100vw (viewport width) and 100vh (viewport height).
See https://go.microsoft.com/fwlink/?LinkID=301480 for more detail on CSS units supported by Internet Explorer.
*/
    height: 100vw;
    width: 100vh;
    overflow: hidden;
    position: absolute;
}

div #colorMixer {
/*
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;
    -ms-transform-origin: 0px 0px;
    position: absolute;
    background-color: black;
    border-color: white;
    border-width: thick;
    border-style: solid;
}

div #colorSelector {
    position: relative;
}

div #eventLog {
    -ms-overflow-style:scrollbar;
}

input.Red {
    background-color: rgb(255,0,0);
}

input.Green {
    background-color: rgb(0,255,0);
}

input.Blue {
    background-color: rgb(0,0,255);
}

포인터 및 제스처 이벤트 수신

다음 코드에서는 색상 믹서 및 색상 선택기를 설정하고, 다양한 이벤트 수신기를 선언합니다.

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

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

먼저 전역 변수를 선언하고, 데이터 개체(colorInfo)를 정의하여 대상 상태를 추적하고, 색상 믹서(target) 및 RGB 색상 선택기 모두를 초기화합니다.

var _width = 640;
var _height = 640;

var _pointerInfo;
var _targetLog;

var _selectedColor;
var _colorRed, _colorGreen, _colorBlue;

// Color-specific data object.
//   value: The color value (r, g, or b)
//   rotation: The rotation value used to calculate color value.
//   matrix: The transform matrix of the target.
function colorInfo(value, rotation, matrix) {
    this.value = value;
    this.rotation = rotation;
    this.matrix = matrix;
}

function initialize() {
    // Configure the target.
    setTarget();

    // Initialize color tracking.
    setColors();
}

그런 다음 색상 믹서를 설정하고, 제스처 인식기(msGesture)를 개체와 연결하고, 다양한 이벤트 수신기를 선언합니다.

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

 

// Configure the interaction target.
function setTarget() {
    //  Set up the target position, size, and transform.
    colorMixer.style.width = _width + "px";
    colorMixer.style.height = _height + "px";
    colorMixer.style.msTransform = (new MSCSSMatrix()).
        translate((window.innerWidth - parseInt(colorMixer.style.width)) / 2.0,
        (window.innerHeight - parseInt(colorMixer.style.height)) / 2.0);

    // Create gesture recognizer.
    var msGesture = new MSGesture();
    msGesture.target = colorMixer;
    colorMixer.gesture = msGesture;
    // Expando property for handling multiple pointer devices.
    colorMixer.gesture.pointerType = null;

    // Expando property to track pointers.
    colorMixer.pointers = [];

    // Declare event handlers.
    colorMixer.addEventListener("pointerdown", onPointerDown, false);
    colorMixer.addEventListener("pointerup", onPointerUp, false);
    colorMixer.addEventListener("pointercancel", onPointerCancel, false);
    colorMixer.addEventListener("lostpointercapture", onLostPointerCapture, false);
    colorMixer.addEventListener("MSGestureChange", onMSGestureChange, false);
    colorMixer.addEventListener("MSGestureTap", onMSGestureTap, false);
    colorMixer.addEventListener("MSGestureEnd", onMSGestureEnd, false);
    colorMixer.addEventListener("MSGestureHold", onMSGestureHold, false);
}

마지막으로 RGB 색상 선택기(이벤트 수신기 포함)와 colorInfo 개체를 초기화합니다.

// Initialize values and event listeners for color tracking.
function setColors() {
    var m = new MSCSSMatrix(colorMixer.style.msTransform);
    _colorRed = new colorInfo(0, 0, m);
    _colorGreen = new colorInfo(0, 0, m);
    _colorBlue = new colorInfo(0, 0, m);

    document.getElementById("red").addEventListener("click", onColorChange, false);
    document.getElementById("green").addEventListener("click", onColorChange, false);
    document.getElementById("blue").addEventListener("click", onColorChange, false);
}

// Re-draw target based on transform matrix associated with color selection.
function onColorChange(e) {
    switch (e.target.id) {
        case "red":
            colorMixer.style.msTransform = _colorRed.matrix;
            break;
        case "green":
            colorMixer.style.msTransform = _colorGreen.matrix;
            break;
        case "blue":
            colorMixer.style.msTransform = _colorBlue.matrix;
            break;
    }
    _selectedColor = e.target.id;

    eventLog.innerText = "Color change";
    targetLog.innerText = colorMixer.style.msTransform;
}

포인터 다운 이벤트 처리

포인터 다운 이벤트에서는 선택한 RGB 색상을 가져와 addPointer 메서드를 호출하여 포인터를 제스처 인식기와 연결합니다. 순서와 pointerType을 추적하여 필요할 경우 포인터 및 제스처 인식기를 다시 연결합니다.

선택된 색상이 없으면 포인터 이벤트를 무시합니다.

// Pointer down handler: Attach the pointer to a gesture object.
function onPointerDown(e) {
    // Do not attach pointer if no color selected.
    if (_selectedColor === undefined)
        return;
    _selectedColor = getSelectedColor();

    // Process pointer.
    if (e.target === this) {
        this.style.borderStyle = "double";
        //  Attach first contact and track device.
        if (this.gesture.pointerType === null) {
            this.gesture.addPointer(e.pointerId);
            this.gesture.pointerType = e.pointerType;
        }
            // Attach subsequent contacts from same device.
        else if (e.pointerType === this.gesture.pointerType) {
            this.gesture.addPointer(e.pointerId);
        }
            // New gesture recognizer for new pointer type.
        else {
            var msGesture = new MSGesture();
            msGesture.target = e.target;
            e.target.gesture = msGesture;
            e.target.gesture.pointerType = e.pointerType;
            e.target.gesture.addPointer(e.pointerId);
        }
    }
    eventLog.innerText = "Pointer down";
}

// Get the current color.
function getSelectedColor() {
    var colorSelection = document.getElementsByName("color");
    for (var i = 0; i < colorSelection.length; i++) {
        if (colorSelection[i].checked)
            return colorSelection[i].id;
    }
}

제스처 이벤트 처리

다음 코드에서는 변환(밀기 또는 살짝 밀기), 회전 및 크기 조정(모으기 또는 늘이기) 제스처를 처리합니다.

// Gesture change handler: Process gestures for translation, rotation, and scaling.
// For this example, we don't track pointer movements.
function onMSGestureChange(e) {
    // Get the target associated with the gesture event.
    var elt = e.gestureObject.target;
    // Get the matrix transform for the target.
    var matrix = new MSCSSMatrix(elt.style.msTransform);

    // Process gestures for translation, rotation, and scaling.
    e.target.style.msTransform = matrix.
        translate(e.offsetX, e.offsetY).
        translate(e.translationX, e.translationY).
        rotate(e.rotation * 180 / Math.PI).
        scale(e.scale).
        translate(-e.offsetX, -e.offsetY);

    // Mix the colors based on rotation value.
    switch (_selectedColor) {
        case "red":
            _colorRed.rotation += ((e.rotation * 180 / Math.PI));
            _colorRed.rotation = _colorRed.rotation % 360;
            targetLog.innerText = _colorRed.rotation.toString();
            if (_colorRed.rotation >= 0)
                _colorRed.value = parseInt(Math.abs(_colorRed.rotation) * (256 / 360));
            else
                _colorRed.value = parseInt((360 - Math.abs(_colorRed.rotation)) * (256 / 360));
            document.getElementById("labelRed").innerText = _colorRed.value.toString();
            _colorRed.matrix = matrix;
            break;
        case "green":
            _colorGreen.rotation += ((e.rotation * 180 / Math.PI));
            _colorGreen.rotation = _colorGreen.rotation % 360;
            targetLog.innerText = _colorGreen.rotation.toString();
            if (_colorGreen.rotation >= 0)
                _colorGreen.value = parseInt(Math.abs(_colorGreen.rotation) * (256 / 360));
            else
                _colorGreen.value = parseInt((360 - Math.abs(_colorGreen.rotation)) * (256 / 360));
            document.getElementById("labelGreen").innerText = _colorGreen.value.toString();
            _colorGreen.matrix = matrix;
            break;
        case "blue":
            _colorBlue.rotation += ((e.rotation * 180 / Math.PI));
            _colorBlue.rotation = _colorBlue.rotation % 360;
            if (_colorBlue.rotation >= 0)
                _colorBlue.value = parseInt(Math.abs(_colorBlue.rotation) * (256 / 360));
            else
                _colorBlue.value = parseInt((360 - Math.abs(_colorBlue.rotation)) * (256 / 360));
            document.getElementById("labelBlue").innerText = _colorBlue.value.toString();
            _colorBlue.matrix = matrix;
            break;
    }
    e.target.style.backgroundColor = "rgb(" + _colorRed.value + ", " + _colorGreen.value + ", " + _colorBlue.value + ")";
    targetLog.innerText = e.target.style.msTransform;
    eventLog.innerText = "Gesture change";
}

필요할 경우 다른 이벤트 처리

이 예제에서는 여기서 처리된 다른 이벤트를 보고만 하지만 보다 강력한 앱은 추가 기능을 제공합니다.

// Tap gesture handler: Display event.
// The touch language described in Touch interaction design (https://go.microsoft.com/fwlink/?LinkID=268162),
// specifies that the tap gesture should invoke an elements primary action (such as launching an application 
// or executing a command). 
// The primary action in this sample (color mixing) is performed through the rotation gesture.
function onMSGestureTap(e) {
    eventLog.innerText = "Gesture tap";
}

// Gesture end handler: Display event.
function onMSGestureEnd(e) {
    if (e.target === this) {
        this.style.borderStyle = "solid";
    }
    eventLog.innerText = "Gesture end";
}

// Hold gesture handler: Display event.
function onMSGestureHold(e) {
    eventLog.innerText = "Gesture hold";
}

// Pointer up handler: Display event.
function onPointerUp(e) {
    eventLog.innerText = "Pointer up";
}

// Pointer cancel handler: Display event.
function onPointerCancel(e) {
    eventLog.innerText = "Pointer canceled";
}

// Pointer capture lost handler: Display event.
function onLostPointerCapture(e) {
    eventLog.innerText = "Pointer capture lost";
}

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

전체 예제

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

요약 및 다음 단계

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

기본 제스처 인식은 포인터 이벤트와 결합되어 변환(밀기 또는 살짝 밀기), 회전 및 크기 조정(손가락 모으기 또는 확대)과 같은 단순한 조작을 관리하는 데 유용합니다.

보다 정교한 조작을 처리하고 완전히 사용자 지정된 사용자 조작 환경을 제공하려면 빠른 시작: 정적 제스처빠른 시작: 조작 제스처를 참조하세요.

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

관련 항목

개발자

사용자 조작에 응답

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

빠른 시작: 포인터

빠른 시작: 정적 제스처

빠른 시작: 조작 제스처

디자이너

터치 조작 디자인