Share via


快速入門:DOM 手勢與操作 (HTML)

[ 本文的目標對象是撰寫 Windows 執行階段 App 的 Windows 8.x 和 Windows Phone 8.x 開發人員。如果您正在開發適用於 Windows 10 的 App,請參閱 最新文件 ]

您可以透過基本文件物件模型 (DOM) 手勢事件處理,為 Windows 觸控語言中描述的某些基本手勢自訂使用者體驗 (例如滑動、旋轉及調整大小)。

Windows 8.1 的更新: Windows 8.1 對指標輸入 API 引入了數種更新及改進。如需詳細資訊,請參閱 Windows 8.1 的 API 變更

如果您是使用 JavaScript 開發應用程式的新手: 請仔細閱讀這些主題以熟悉這裡討論的技術。

使用 JavaScript 建立您的第一個應用程式

使用 JavaScript 建立應用程式的藍圖

請參閱快速入門:新增 HTML 控制項和處理事件以了解事件

應用程式功能,從開始到完成:

應用程式功能,從開始到完成系列深入探索此功能。

使用者互動,從開始到完成 (HTML)

使用者互動自訂,從開始到完成 (HTML)

使用者經驗指導方針:

平台控制項程式庫 (HTMLXAML) 提供完整的使用者互動體驗,包括標準互動、動畫物理效果與視覺化回饋。 如果您不需要自訂的互動支援,請使用這些內建控制項。

如果平台控制項不足,下列使用者互動指導方針能讓您在各種輸入模式下提供令人讚賞的沈浸式互動體驗。這些指導方針主要著重在觸控輸入,不過與觸控板、滑鼠、鍵盤和手寫筆輸入仍有相關。

範例:應用程式範例中,查看此功能的執行方式。

使用者互動自訂,從開始到完成範例

HTML 捲動、移動瀏覽和縮放範例

輸入:DOM 指標事件處理範例

輸入:可具現化手勢範例

目標: 了解如何使用觸控、滑鼠、畫筆/手寫筆互動和 DOM 手勢事件的輸入,接聽及處理平移、旋轉和縮放的基本手勢。

先決條件

請參閱快速入門:指標

我們假設您可以利用 JavaScript,以適用於 JavaScript 的 Windows Library 範本建立基本的應用程式。

若要完成這個教學課程,您需要:

完成所需的時間: 30 分鐘.

什麼是手勢事件?

手勢是在輸入裝置上或由輸入裝置 (觸控介面上的單指或多指、畫筆/手寫筆數位板及滑鼠等) 所執行的實際動作。這些自然的互動都會對應到系統和您應用程式中元素的作業。 如需詳細資訊,請參閱手勢、操作以及互動

Windows 倚賴一組基本手勢來操作 UI 和進行互動。

手勢說明
點選點選手勢

偵測到單一接觸點並立即舉起。

點選元素會叫用它的主要動作。

按住不放按住不放手勢

偵測到單一接觸點且並未移動。

按住不放會在不進行任何動作下,顯示詳細的資訊或教學視覺物件 (例如工具提示或操作功能表)。

滑動滑動手勢

偵測到一或多個接觸點,再往同一個方向移動。

滑動主要用於移動瀏覽互動,但也可以用來移動、繪圖以及書寫。

撥動撥動手勢

偵測到一或多個接觸點,再往同一個方向短距離移動。

撥動以選取、命令和移動。

轉動轉動手勢

偵測到二或多個接觸點,往順時鐘或逆時鐘方向弧形旋轉。

轉動以旋轉。

捏合捏合手勢

偵測到二或多個接觸點,再朝靠攏的方向移動。

捏合以縮小。

伸展伸展手勢

偵測到二或多個接觸點,再朝分開的方向移動。

伸展以放大。

如需這些手勢及它們與 Windows 觸控語言之相關性的詳細資訊,請參閱觸控互動設計

 

您可以使用手勢偵測擴充您的應用程式互動模型,並以快速入門:處理指標輸入中所述的基本指標事件為基礎。 事實上,您的應用程式最可能使用手勢事件 (例如處理點選動作、使用滑動來移動瀏覽或移動,以及使用捏合或伸展來縮放) 和使用原始指標資料來支援手勢偵測與處理。

您的應用程式可以同時處理多個手勢 (如縮放和旋轉)、群組指標接觸點以對準特定元素 (如將所有接觸點與初始或主要接觸點的目標關聯),以及識別特定手勢或指標接觸點對準的特定元素。

重要  如果您實作自己的互動支援,請牢記使用者所期待的是與應用程式 UI 元素直接互動的直覺式體驗。 建議您在平台控制項程式庫 (HTMLXAML) 模型化您的自訂互動,以保持一致和可探索的 UI 體驗。這些程式庫中的控制項提供完整的使用者互動體驗,包含標準互動、動畫物理效果、視覺化回饋及協助工具。只有在需求明確且定義清楚,而且沒有基本的互動可以支援您的情況時,才建立自訂互動。

 

建立 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 屬性 msTouchActionoverflow 以及 -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;
}

處理指標向下事件

在指標向下事件中,我們會藉由呼叫 addPointer 方法來取得已選取的 RGB 色彩,並將指標與手勢辨識器建立關聯。我們會追蹤順序和 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)

快速入門:指標

快速入門:靜態手勢

快速入門:操作手勢

設計人員

觸控互動設計