Windows 市集應用程式實作手冊 - 使用 JavaScript : Lab 09

實驗 9 : 觸控輸入和混合式應用程式

概述

Windows 8 支援各種不同的人機界面裝置之輸入,包括滑鼠和鍵盤,但是,觸控輸入一直是它的首選。內建於作業系統裡的觸控式 APIs 讓開發人員能夠在 Windows 市集應用程式內建置豐富的、直覺的、和反應迅速的觸控界面。這對平板電腦尤其重要,因為它很可能不會附加滑鼠。

在本實驗裡,您將為一個已經存在的照片編輯應用程式(命名為 Contoso 照片)添加觸控支援,以便充份利用 Windows 8 的觸控功能。您將加入簡單的手勢支援,例如單點擊和雙點擊,您也將為拖曳( dragging )和移動瀏覽( panning )建置捏和縮放支援。除此之外,您也將在 C++ 裡,寫一 Windows 執行階段( Windows Runtime )元件,並將它從 JavaScript 裡呼叫出來,以便幫助執行影像處理任務。聽起來很吸引人?那麼,就讓我們一頭栽進觸控的世界來探索如何建置好的、偉大的、 Modern 樣式的觸控功能應用程式。

目標

本實驗將引導您如何:

  • 回應簡單的手勢,例如點擊。

  • 用一隻手指來實作拖曳( dragging )和移動瀏覽。

  • 用兩隻手指來實作捏和縮放(和不縮放)。

  • 在無觸控裝置上,使用滑鼠滾輪來實作縮放。

  • 從 JavaScript 裡呼叫以 C++ 寫成的 Windows 執行階段( Windows Runtime ) 元件。

系統需求

要完成本實驗,您必須擁有下列東西:

  • Windows 8

  • Microsoft Visual Studio 2012 (任何 SKU )

設定

為了讓您的電腦能進行本實驗,您必須先實作下列步驟:

  1. 安裝 Windows 8。

  2. 安裝 Visual Studio 2012 。

練習

本動手做實驗室包含下列練習:

  1. 簡單的手勢

  2. 移動瀏覽和縮放

  3. 混合式應用程式

  4. 指標輸入

完成本實驗,估計需要費時: 30 到 40 分鐘。

練習 1 : 簡單的手勢

在這個練習裡,您將啟動 Contoso 照片( Contoso Photo )這個已存在的應用程式,它已經擁有許多功能,但是沒有觸控支援。然後,為它加入點擊手勢支援。

任務 1 – 建置起始應用程式

首先,讓我們從起始材料中檢索出起始應用程式並測試一下。

  1. 從起始材料中,複製命名為 ContosoPhoto 的方案到您的本地硬碟。
  2. 在 Visual Studio 中,開啟 ContosoPhoto 。
  3. 按壓 F5 來啟動此應用程式於偵錯器,您應該看見如圖 1 的起始螢幕。

    圖 1
    Contoso 照片的起始螢幕
  4. 點擊起始訊息:「點擊這兒來開啟相片」( Tap here to open a photo )。發生什麼事?
  5. 返回 Visual Studio 並停止偵錯。

任務 2 – 從起始螢幕裡處理 MSGesture 點擊事件

「點擊這兒來開啟相片」( Tap here to open a photo )這個短訊包含在 default.html 所宣告的 DIV 標籤裡。讓我們加入程式碼,以便在 DIV 被點擊時能夠察覺、清除它、並進到主螢幕。

備註: JavaScript 開發人員都很熟悉 DOM 事件,例如點擊( click )、按下滑鼠( mousedown )、和釋放滑鼠( mouseup )等一些使用滑鼠來表示的動作。 Windows 8 可以從 DOM 裡觸發額外的事件來做指標輸入,也就是說,使用滑鼠、手寫筆、或手指來做輸入。舉例來說,當手指觸控螢幕時,就會觸發 MSPointerDown 事件;當移動手指橫越螢幕時,就觸發 MSPointerMove 事件;當抬起手指離開螢幕時, MSPointerUp 事件就觸發。如果您使用手寫筆或滑鼠來做輸入,也會觸發相同的事件。 只是情況會變得更好。為了讓手勢處理變得很簡單, Windows 8 提供了 MSGesture 類別。如果您將 DOM 元素裡的 MSPointerDown 事件轉交給 MSGesture 事件,那些 DOM 元素就會改而觸發 MSGestureTap 、 MSGestureDoubleTap 、和 MSGestureChange 等事件。察覺一個 DOM 元素(例如,一個 DIV 或一張圖像)何時被點擊很容易,其簡單程度猶如登錄該元素所觸發的 MSGestureTap 事件之處理常式。 點擊是使用一隻手指來執行的簡單手勢,但是,使用滑鼠或手寫筆來點擊某一元素照樣可以觸發 MSGestureTap 事件。因此,當您處理這些事件時,您等於得到滑鼠和手寫筆的免費支援。此外, MSGestureTap 事件不等同於按下滑鼠( mousedown )事件,因為只有在指標裝置與一個元素在半秒鐘裡的接觸並馬上離開, MSGestureTap 事件才會觸發。

  1. 在專案的 js 資料夾裡,開啟 default.js ,加入下列陳述式於「文件」頂部的其他 var :

    JavaScript
    var _gesture = new MSGesture( );

  2. 現在加入下列陳述式到 app.onactivated 處理常式後面,在叫用 WinJS.UI.processAll 之後:

    JavaScript
    // Register handlers for MSPointerDown events for gesture support
    document.querySelector("#message").addEventListener("MSPointerDown", onAssignPointer);
    document.querySelector("#main").addEventListener("MSPointerDown", onAssignPointer);
    _mainCanvas.addEventListener("MSPointerDown", onAssignPointer);
    // Register handlers for MSGesture events for gesture recognition
    document.querySelector("#message").addEventListener("MSGestureTap", onOpeningMessageTapped);

  3. 加入下列事件處理常式到 default.js :

    JavaScript
    function onAssignPointer(e) {
    try {
    _gesture.target = e.target;
    } catch (err) { }
    if (e.target == _gesture.target) {
    _gesture.addPointer(e.pointerId);
    }
    }

    function onOpeningMessageTapped(e) {
    onOpenButtonClicked(e);
    }

  4. 按壓 F5 來啟動此應用程式。

  5. 點擊「點擊這兒來開啟相片」( Tap here to open a photo )。

  6. 在隨後出現的 FileOpenPicker 裡,選擇一圖像文件並點擊開啟( Open )按鈕。(起始材料內包含有命名為 Amy(zh-tw,MSDN.10).jpg 的圖片檔案,它應該是本練習的完美教材。)

  7. 確認您所選擇的影像出現在螢幕上,如圖 2 所示。

    圖 2
    載入圖像後的 Contoso 照片

  8. 返回 Visual Studio 並停止偵錯。

任務 3 – 在影像裡處理 MSGestureTap 事件

Contoso 照片已經包含有從相片刪除紅眼症的邏輯;該邏輯就包含在 default.js 裡命名為 removeRedEye 的函式內。讓我們允許使用者運用手指、手寫筆、或滑鼠點擊它們來修正紅眼症。

  1. 在 default.js 內,加入下列陳述式於 app.onactivated 處理常式後面。

    JavaScript
    _mainCanvas.addEventListener("MSGestureTap", onImageTapped);

  2. 現在,加入下列事件處理常式到 default.js :

    JavaScript
    function onImageTapped(e) {
    if (_bitmap !== null) {
    var x, y;

    // Get the click coordinates
    x = e.offsetX;
    y = e.offsetY;

    // Translate click coordinates into image coordinates
    x = Math.round(x * _hiddenCanvas.width / _mainCanvas.width);
    y = Math.round(y * _hiddenCanvas.height / _mainCanvas.height);

    // Fix red eye in region under cursor
    removeRedEye(x, y);

    // Enable the Save button
    _saveButton.removeAttribute("disabled");
    }
    }

  3. 按壓 F5 來啟動應用程式。

  4. 載入含有紅眼症的相片,例如,起始材料所提供的 Amy(zh-tw,MSDN.10).jpg 檔案。

  5. 點擊每一個紅眼症並驗證紅眼已消失。

    備註:如果紅眼症沒有消失,其可能的原因是眼睛比較小而您的手指相對來說比較大。如果嘗試失敗,請試著在眼睛四週點擊數次,或使用滑鼠或手寫筆對眼睛的紅色部份做精確的點擊。
    還有,雖然這不是本實驗練習的一部份,不過, Contoso 照片含有**儲存( Save )**按鈕的應用程式列,您可以點擊它來儲存已編輯的照片。試試看!

  6. 返回 Visual Studio 並停止偵錯。

練習 2 : 移動瀏覽和縮放

到目前為止,我們有一個很好的起跑。但是,如果使用者能在編輯照片的同時放大和縮小這些照片,那麼, Contoso 照片功能會顯得更有用。在這個練習裡,請您加入捏和縮放手勢支援,讓使用者可以隨心所欲來放大和縮小相片。您也將加入拖曳和移動瀏覽支援,讓使用者可以將縮放後的照片到處移動。

任務 1 –加入移動瀏覽支援

首先,讓我們使用 MSGestureChange 事件,它會在指標裝置移到 DOM 元素上面時觸發,以允許使用者拖曳和重新定位影像於螢幕畫面上。在剛開始的階段,這個功能看不太出來,但是在我們加入縮放支援(下一任務的練習重點)之後,它就會非常重要。

  1. 加入下列陳述式到 default.js 上方的其他 var :

    JavaScript
    var _dx = 0;
    var _dy = 0;

  2. 加入下列陳述式到 app.onactivated 處理常式後面:

    JavaScript
    _mainCanvas.addEventListener("MSGestureChange", onGestureChange);
    _mainCanvas.addEventListener("MSGestureEnd", onGestureEnd);

    備註:在 Windows 市集應用程式裡,當使用者用一根或兩根手指來操控 DOM 元素時,就會觸發 MSGestureChange 事件來接收處理此 DOM 元素的資訊:如果是使用一根手指來觸碰螢幕畫面, MSGestureChange 事件處理常式就會移動此 DOM 元素,如果是兩隻手指的碰觸並做張合的動作,它就調整其大小。相關的 MSGestureStart 和 MSGestureEnd 事件表示操控已啟動或已終止。

  3. 加入下列事件處理常式到 default.js :

    JavaScript
    function onGestureChange(e) {
    _dx += e.translationX;
    _dy += e.translationY;
    e.target.style.msTransform = "translate(" + _dx + "px," + _dy + "px)";
    }

    備註:設定 msTransform 屬性的陳述式會應用 CSS 轉換( CSS transform )來移動影像。有關所有 CSS 轉換的詳情,請參閱 http://www.w3.org/TR/css3-2d-transforms/ 。

  4. 找到在 onOpenButtonClicked 函式內部的 image.onload 處理常式,在「重新設定轉換和相關變數」( Reset the transform and related variables )註解後面加入這三行來重新設定轉換,當新的影像被載入時:

    JavaScript
    image.onload = function () {
    // Set the width and height of the canvas based on the image size
    var width = image.width;
    var height = image.height;
    var ratio = height / width;

    _mainCanvas.width = Math.min(800, width);
    _mainCanvas.height = _mainCanvas.width * ratio;

    // Reset the transform and related variables
    _dx = 0;
    _dy = 0;
    _mainCanvas.style.msTransform = "translate(0px,0px)";

    // Convert the image into an ImageData object by drawing it to
    // the hidden canvas and calling getImageData on that canvas
    _hiddenCanvas.width = image.width;
    _hiddenCanvas.height = image.height;
    _hiddenContext.drawImage(image, 0, 0, _hiddenCanvas.width, _hiddenCanvas.height);
    _bitmap = _hiddenContext.getImageData(0, 0, _hiddenCanvas.width, _hiddenCanvas.height);

    // Transfer (and scale) the image from the hidden canvas to the main canvas
    _mainContext.drawImage(_hiddenCanvas, 0, 0, _mainCanvas.width, _mainCanvas.height);

    // Disable the Save button
    _saveButton.setAttribute("disabled", "");
    }

  5. 按壓 F5 來啟動此應用程式。

  6. 載入圖像文件。

  7. 使用一根手指(或滑鼠或手寫筆,如果您比較喜歡的話)來拖曳照片,並驗證照片跟在您的手指後面到處跑。

  8. 現在,將照片刷到螢幕畫面的邊緣。觀察一下,在您的手指不碰觸螢幕畫面後,照片仍然繼續移動。這種效應被稱為慣性( inertia )。 MSGesture 事件免費提供此功能。

  9. 返回 Visual Studio 並停止偵錯。

任務 2 – 處理 MSGestureDoubleTap 事件

內建慣性的缺點是,如果您刷照片的力道太大的話,照片會飛離螢幕畫面!不過,在我們進一步說明之前,且讓我們使用 MSGestureDoubleTap 事件來將照片重新定位在螢幕畫面的中間。

  1. 在 default.js 裡,加入下列陳述式到 app.onactivated 後面:

    JavaScript
    document.querySelector("#main").addEventListener("MSGestureDoubleTap", onDoubleTap);

    備註:您正在包含此影像的 DIV 上處理這個事件,因此,您可以在螢幕畫面的任何地方做雙點擊,不一定要在照片上面。

  2. 加入下列事件處理常式到 default.js :

    JavaScript
    function onDoubleTap(e) {
    _dx = 0;
    _dy = 0;
    _mainCanvas.style.msTransform = "translate(0px,0px)";
    }

  3. 啟動此應用程式,載入相片,用力將它刷離螢幕畫面。

  4. 雙點擊螢幕畫面,驗證照片已返回螢幕畫面的中央。

  5. 返回 Visual Studio 並停止偵錯。

任務 3 –加入捏和縮放支援

如果不使用滑鼠或手寫筆,目前要精準修正紅眼症還是相當困難,因為手指是不精確的指標裝置。但是,我們可以讓使用者以捏和縮放手勢來放大和縮小照片,進而修正這個問題。由於您已經寫作了這些事件的處理常式,所以,在 Windows 8 裡做這件事算是很容易。

  1. 在 default.js 裡,加入下列陳述式於「文件」頂部附近、宣告 _dx 和 _dy 變數的陳述式後面。

    JavaScript
    var _scale = 1;

  2. 找到您在任務 1 所加入的 onGestureChange 函式,修改它如下所顯示:

    JavaScript
    function onGestureChange(e) {
    _dx += e.translationX;
    _dy += e.translationY;
    _scale = _scale * e.scale;
    _scale = Math.min(_scale, 4);
    _scale = Math.max(_scale, 1);
    e.target.style.msTransform = "scale(" + _scale + ") translate(" + _dx + "px," + _dy + "px)";
    }

    備註:當一根手指移動通過 DOM 元素或當兩隻手指頭移動在 DOM 元素上面時,就會觸發 MSGestureChange 事件。換言之,當只有一根手指是碰觸螢幕畫面時,包含非零值的 e.translationX 和 e.translationY 就會確切計算出,離上一次 MSGestureChange 事件,手指移動了多少距離;如果是兩隻手指頭碰觸螢幕畫面,則包含值為 1.0 的 e.scale 會確切計算出,離上一次 MSGestureChange 事件,兩根手指彼此之間移動的相對距離比。

  3. 找到您在任務 2 所加入的 onDoubleTap 函式,修改它如下所示:

    JavaScript
    function onDoubleTap(e) {
    // Reset the transform and related variables
    _dx = 0;
    _dy = 0;
    _scale = 1;
    _mainCanvas.style.msTransform = "scale(1) translate(0px,0px)";
    }

  4. 最後,找到 image.onload 處理常式裡的 “_dy = 0” 陳述式,修改它底下的那兩個陳述式,以便在使用者開啟新的影像時,重新設定其轉換。

    JavaScript
    image.onload = function ( ) {
    // Set the width and height of the canvas based on the image size
    var width = image.width;
    var height = image.height;
    var ratio = height / width;

    _mainCanvas.width = Math.min(800, width);
    _mainCanvas.height = _mainCanvas.width * ratio;

    // Reset the transform and related variables
    _dx = 0;
    _dy = 0;
    _scale = 1;
    _mainCanvas.style.msTransform = "scale(1) translate(0px,0px)";

    // Convert the image into an ImageData object by drawing it to
    // the hidden canvas and calling getImageData on that canvas
    _hiddenCanvas.width = image.width;
    _hiddenCanvas.height = image.height;
    _hiddenContext.drawImage(image, 0, 0, _hiddenCanvas.width, _hiddenCanvas.height);
    _bitmap = _hiddenContext.getImageData(0, 0, _hiddenCanvas.width, _hiddenCanvas.height);

    // Transfer (and scale) the image from the hidden canvas to the main canvas
    _mainContext.drawImage(_hiddenCanvas, 0, 0, _mainCanvas.width, _mainCanvas.height);

    // Disable the Save button
    _saveButton.setAttribute("disabled", "");
    }

  5. 按壓 F5 來啟動此應用程式。

  6. 載入相片,然後,用兩根手指碰觸螢幕畫面並往外張開來放大它們。

  7. 使用一根手指來重新定位照片,以便使紅眼睛出現在螢幕畫面的中央。

  8. 點擊照片裡的紅眼睛來修正它(圖 3 )。

    圖 3
    紅眼已被刪除的縮放照片

  9. 擺放兩根手指頭於螢幕畫面上,將它們向內夾來縮小照片。

    備註:依系統預設,以 CSS 轉換來執行的縮放係從 DOM 元素的左上角開始發生,造成該元素對捏和縮放手勢做出不自然的回應。在 Contoso 照片裡,影像大小的調整則由中央往外縮放,因為有 “-ms-transform-origin: 50%” 樣式應用於 default.css 內的影像。

  10. 返回 Visual Studio 並停止偵錯。

任務 4 – 加入滑鼠滾輪支援

使用 MSGestureChange 事件可以讓捏和縮放順利運作於觸控螢幕上,但是沒有觸控螢幕的人該怎麼辦?在這個任務裡,您將加入滑鼠滾輪支援,以便讓使用者可以用滑鼠來做放大和縮小的動作。

  1. 在 default.js 裡,加入下列陳述式於 app.onactivated 處理常式後面:

    JavaScript
    document.addEventListener("mousewheel", onMouseWheelChanged)

  2. 現在,加入下列事件處理常式到 default.js :

    JavaScript
    function onMouseWheelChanged(e) {
    if (_bitmap != null && e.ctrlKey) {
    if (e.wheelDelta > 0) {
    // Zoom in
    if (_scale < 4.0) {
    _scale += 0.1;
    _mainCanvas.style.msTransform = "scale(" + _scale + ") translate(" + _dx + "px," + _dy + "px)";
    }
    }
    else {
    // Zoom out
    if (_scale > 1.0) {
    _scale -= 0.1;
    _mainCanvas.style.msTransform = "scale(" + _scale + ") translate(" + _dx + "px," + _dy + "px)";
    }
    }
    }
    }

    備註:這個處理常式使用與 MSGestureChange 處理常式相同的轉換來調整照片的大小。但是,這個處理常式被呼叫的時機是按壓控制鍵( Ctrl key )加上滑鼠滾輪被向前或向後滾動時。

  3. 啟動應用程式和載入相片。

  4. 按住控制鍵( Ctrl key )的同時,向前或向後滾動滑鼠滾輪。驗證一下,照片已放大或縮小,其回應方式與捏和縮放手勢時完全相同。

  5. 確認一下:您也能以滑鼠拖曳照片來為它重新定位。

  6. 返回 Visual Studio 並停止偵錯。

練習 3 :混合式應用程式

您或許未曾想過這個問題,可是,在您於 JavaScript 使用 Windows 執行階段( Windows Runtime )分類時,您實際上正在使用以另一個語言( C++ )所寫成的元件。有趣的是您可以在 C++ 或 C# 裡建置 Windows 執行階段( Windows Runtime )元件,然後,從自己所選擇的語言來運用它們。在這個練習裡,您將看見如何在 C++ 裡實作 Windows執行階段( Windows Runtime )元件,然後,您將刪除目前在 JavaScript 所寫作的紅眼移除程式碼,並使用 C++ 元件來刪除相片裡的紅眼症。

任務 1 – 在 C++ 裡,寫作 Windows 執行階段( Windows Runtime )元件

建置混合式應用程式的第一步是建置一個可以從 JavaScript 被呼叫出來的元件。

  1. 在方案總管裡,用滑鼠右鍵單擊 ContosoPhoto 方案,並使用**加入- 新增專案( Add - New Project )**命令來加入命名為 PhotoEffects 的 Windows 執行階段( Windows Runtime )元件專案到此方案裡(圖 4 )。選擇 Visual C++ 為語言。

    圖 4
    加入新的 C++ 專案到方案裡

  2. 在方案總管裡,重新命名 Class1.h 為 RedEyeComponent.h 並重新命名 Class1.cpp 為 RedEyeComponent.cpp 。

  3. 開啟 RedEyeComponent.h ,以下列陳述式來替換它的內容:

    C++
    #pragma once
    #include <collection.h>
    namespace PhotoEffects
    {
    public ref class RedEyeComponent sealed
    {
    public:
    RedEyeComponent();
    Windows::Foundation::Collections::IVector<unsigned char>^ RemoveRedEye(Windows::Foundation::Collections::IVector<unsigned char>^);
    };
    }

  4. 開啟 RedEyeComponent.cpp ,以下列陳述式來替換它的內容:

    C++
    // RedEyeComponent.cpp
    #include "pch.h"
    #include "RedEyeComponent.h"
    using namespace PhotoEffects;
    using namespace Platform;
    using namespace Windows::Foundation::Collections;
    RedEyeComponent::RedEyeComponent()
    {
    }
    IVector<unsigned char>^ RedEyeComponent::RemoveRedEye(IVector<unsigned char>^ pixels)
    {
    // Loop through the pixels in the region, lowering red values that
    // exceed the sum of the blue and green values
    for (unsigned int i = 0; i < pixels->Size; i += 4)
    {
    unsigned char r = pixels->GetAt(i + 0);
    unsigned char g = pixels->GetAt(i + 1);
    unsigned char b = pixels->GetAt(i + 2);
    if (r > (g + b))
    {
    pixels->SetAt(i + 0, (unsigned char)((g + b) / 2));
    }
    }
    return pixels;
    }

  5. 用滑鼠右鍵單擊方案總管裡的 PhotoEffects 專案,使用**建置( Build )**命令來建置此專案。驗證一下:已正確無誤將專案建置起來。

任務 2 – 從 JavaScript 呼叫 Windows 執行階段( Windows Runtime )元件

現在,元件已被建置。要從 JavaScript 呼叫它,請在您的 JavaScript 專案裡創建一參考到 C++ 專案,然後,對待 C++ 元件如同對待任何其他元件。方法如下:

  1. 在方案總管裡,用滑鼠右鍵單擊 ContosoPhoto 專案裡的 References 資料夾,點擊加入參考( Add Reference )

  2. 點擊左邊窗格裡的**方案( Solution )**來展示方案裡的其他專案。然後,選擇緊接在 “PhotoEffects” 的對話框,如圖 5 所示,並點擊 OK 來加入專案參考。

    圖 5
    加入一參考到 C++ 專案

  3. 點擊此按鈕來啟動產品購買。

    JavaScript
    // Loop through the pixels in the region, lowering red values that
    // exceed the sum of the blue and green values
    for (var i = x1; i < x2; i++) {
    for (var j = y1; j < y2; j++) {
    var index = (i + j * _bitmap.width) << 2;
    var r = _bitmap.data[index + 0];
    var g = _bitmap.data[index + 1];
    var b = _bitmap.data[index + 2];
    if (r > (g + b)) {
    _bitmap.data[index + 0] = (g + b) / 2;
    }
    }
    }

  4. 以下列陳述式來替換您剛剛刪除的陳述式:

    JavaScript
    // Create a copy of the region
    var pixels = [];
    for (var i = x1; i < x2; i++) {
    for (var j = y1; j < y2; j++) {
    var index = (i + j * _bitmap.width) << 2;
    pixels.push(_bitmap.data[index + 0]);
    pixels.push(_bitmap.data[index + 1]);
    pixels.push(_bitmap.data[index + 2]);
    pixels.push(_bitmap.data[index + 3]);
    }
    }
    // Remove red eye from the region
    var effect = new PhotoEffects.RedEyeComponent();
    pixels = effect.removeRedEye(pixels);
    // Copy the modified pixels to the bitmap
    var k = 0;
    for (var i = x1; i < x2; i++) {
    for (var j = y1; j < y2; j++) {
    var index = (i + j * _bitmap.width) << 2;
    _bitmap.data[index + 0] = pixels[k++];
    _bitmap.data[index + 1] = pixels[k++];
    _bitmap.data[index + 2] = pixels[k++];
    _bitmap.data[index + 3] = pixels[k++];
    }
    }

  5. 按壓 F5 來啟動應用程式。

  6. 開啟相片,點擊紅眼來修正它。驗證一下:紅眼已消失,就像以前那樣。

  7. 返回 Visual Studio 並停止偵錯。

練習 4 :指標輸入

Windows 8 結合了所有的輸入法,因此,開發人員能夠寫一個 API 來同時處理觸控、滑鼠、和畫筆等輸入。這個 API 的核心就是指標事件(pointer events)。在 HTML 裡,這些事件以不同名稱的 DOM 事件出現,例如 MSPointerDown 、 MSPointerMove 、和 MSPointerUp 。指標事件的優點就在於它們提供了統一的、容易使用的模式來處理指標輸入(見後面說明)。除此之外,它們會根據碰觸螢幕畫面的指標來觸發該指標事件,因此,您可以使用它們來建置豐富、多元的觸控介面,而且每一次指標事件觸發,您都能判斷何種類型的指標促動了事件,以便對不同類型的指標裝置做出不同的回應。

在這個練習裡,您將加入Contoso照片支援來註解以手指、滑鼠、或畫筆或手寫筆所畫的圖像。您將使用指標事件來處理此輸入,在顯示照片的HTML5畫布上畫線來回應這些事件。

備註:這個練習僅供示範說明之用途。您畫的圖不會被保存,如果應用程式被暫停和終止的話(雖然這是您可以自己加入的練習之一)。當您儲存圖像時,它們不會被儲存,而且如果您點擊或雙點擊此圖像,它們就會消失。可是,不提指標事件,觸控輸入實驗就不完整!

任務 1 - 處理指標事件

第一個任務是修改 default.js 來處理指標事件。

  1. 開啟 default.js,加入下列 變數聲明到「文件」頂部的其他變數聲明:

    JavaScript
    var _annotating = false;
    var _redEyeButton;
    var _annotateButton;
    var _points = {};

  2. 加入下列陳述式到 app.onactivated 處理常式後面:

    JavaScript
    // Register handlers for pointer events
    _mainCanvas.addEventListener("MSPointerDown", onPointerDown);
    _mainCanvas.addEventListener("MSPointerMove", onPointerMove);
    _mainCanvas.addEventListener("MSPointerUp", onPointerUp);
    _mainCanvas.addEventListener("MSPointerOut", onPointerOut);

  3. 加入下列事件處理常式到 default.js,以便在指標移動橫越 HTML5 畫布時跟縱它們,並在每一次 MSPointerMove 事件觸發時,畫一條線來連接目前的指標位置到前一個指標位置:

    JavaScript
    function onPointerDown(e) {
    if (_annotating && e.currentPoint.isInContact) {
    _points[e.pointerId] = e.currentPoint;
    _mainContext.lineWidth = 8;
    _mainContext.lineCap = "round";
    _mainContext.strokeStyle = "Yellow";
    }
    }
    function onPointerMove(e) {
    if (_annotating && e.currentPoint.isInContact) {
    var point = e.currentPoint;
    if (_points[e.pointerId] !== undefined) {
    var prev = _points[e.pointerId];
    _mainContext.beginPath();
    _mainContext.moveTo(prev.position.x, prev.position.y);
    _mainContext.lineTo(point.position.x, point.position.y);
    _mainContext.stroke();
    }
    _points[e.pointerId] = point;
    }
    }
    function onPointerUp(e) {
    if (_annotating && _points[e.pointerId] !== undefined) {
    delete _points[e.pointerId];
    }
    }
    function onPointerOut(e) {
    if (_annotating && _points[e.pointerId] !== undefined) {
    delete _points[e.pointerId];
    }
    }

    備註:為避免用來畫線的指標事件與用來做移動瀏覽和縮放的 MSGesture 事件之間產生衝突,我們將只 _annotating 為真( true )時,才在畫布上畫線。在接下來的任務裡,您將加入按鈕,以便在 _annotating 為真( true )或為假( false )之間做切換。 另外,請注意:我們不儲存前一個指標的位置,以供畫一條線連接到目前的指標位置,取而代之,我們將儲存前一次的指標位置於使用指標 ID輸 入的陣列條目( array entries )。這一點很重要,假如有多個指標碰觸螢幕畫面的話。如此,才能防止以甲指標表示的目前位置與乙指標表示的前一個位置連接在一起。

  4. 加入 if 條件( if condition )到 onGestureChange 函式:

    JavaScript
    function onGestureChange(e) {
    if (!_annotating) {
    _dx += e.translationX;
    _dy += e.translationY;
    _scale = _scale * e.scale;
    _scale = Math.min(_scale, 4);
    _scale = Math.max(_scale, 1);
    e.target.style.msTransform = "scale(" + _scale + ") translate(" + _dx + "px," + _dy + "px)";
    }
    }

  5. 做類似的變更到 onImageTapped 函式:

    JavaScript
    function onImageTapped(e) {
    if (_bitmap !== null && !_annotating) {
    var x, y;
    // Get the click coordinates
    x = e.offsetX;
    y = e.offsetY;
    // Translate click coordinates into image coordinates
    x = Math.round(x * _hiddenCanvas.width / _mainCanvas.width);
    y = Math.round(y * _hiddenCanvas.height / _mainCanvas.height);
    // Fix red eye in region under cursor
    removeRedEye(x, y);
    // Enable the Save button
    _saveButton.removeAttribute("disabled");
    }
    }

任務 2 – 修改應用程式列

接下來,我們需要加入兩個按鈕到應用程式列,以便使用者能在紅眼模式和註解模式之間做切換。

  1. 在 default.html 裡,加入 “reEyeButton” 和 “annotateButton” 陳述式到 ID 是 “AppBar” 的 DIV 。

    HTML
    <div id="appbar" data-win-control="WinJS.UI.AppBar">
    <button id="openButton" data-win-control="WinJS.UI.AppBarCommand" data-win-options="{id:'open', label:'Open', icon:'&#xE160;', section: 'selection'}"></button>
    <button id="saveButton" data-win-control="WinJS.UI.AppBarCommand" data-win-options="{id:'save', label:'Save', icon:'&#xE105;', section: 'selection'}"></button>
    <button id="redEyeButton" data-win-control="WinJS.UI.AppBarCommand" data-win-options="{id:'open', label:'Red Eye', icon:'&#xE15E;', section: 'global'}"></button>
    <button id="annotateButton" data-win-control="WinJS.UI.AppBarCommand" data-win-options="{id:'save', label:'Annotate', icon:'&#xE104;', section: 'global'}"></button>
    </div>

  2. 開啟 default.js ,加入下列陳述式到 app.onactivated 處理常式,其位置就在初始化**開啟( Open )儲存( Save )**按鈕正後方的陳述式:

    JavaScript
    _redEyeButton = document.querySelector("#redEyeButton");
    _redEyeButton.addEventListener("click", onRedEyeButtonClicked);
    _redEyeButton.setAttribute("disabled", "");
    _annotateButton = document.querySelector("#annotateButton");
    _annotateButton.addEventListener("click", onAnnotateButtonClicked);

  3. 加入下列事件處理常式到 default.js :

    JavaScript
    function onRedEyeButtonClicked(e) {
    _annotating = false;
    _redEyeButton.setAttribute("disabled", "");
    _annotateButton.removeAttribute("disabled");
    }
    function onAnnotateButtonClicked(e) {
    _annotating = true;
    _redEyeButton.removeAttribute("disabled");
    _annotateButton.setAttribute("disabled", "");
    }

任務 3 – 測試結果

現在,讓我們測試結果並觀察運作中的指標事件。

  1. 請按壓 F5 來啟動應用程式。
  2. 開啟相片,然後,顯示應用程式列和點擊**註解( Annotate )**按鈕。
  3. 使用一根手指或滑鼠來畫照片。確認這些筆觸以黃色字出現,如圖 6 所示。

    圖 6
    註解一張照片
  4. 如果您是在觸控螢幕上運作,請用兩根或三根手指來畫畫。驗證一下:您可以同時使用多根手指來作畫。
  5. 使用滑鼠在照片作畫。您能同時使用滑鼠和手指來作畫嗎?試試看!
  6. 返回 Visual Studio 並停止偵錯。

摘要

好的、偉大的 Windows 市集應用程式以偉大的觸控界面為主訴求。以 MSPointer 和 MSGesture 事件為重心的 Windows 執行階段( Windows Runtime )觸控 API 讓建置好的觸控式 API 變得很簡單,您已在本實驗得到第一手的親身經驗。您也體驗到從 JavaScrip t去呼叫以另一種語言寫作的 Windows 執行階段( Windows Runtime )元件是多麼容易。

當作臨別贈言,請您注意「觸控」可以做的事遠超過我們在 Contoso 照片所做的事。舉例來說,除了包括縮放資訊來實作捏手指縮放之外, MSGestureChange 事件也包含碰觸畫面的兩隻手指頭旋轉比例的資訊。如果您允許使用者旋轉照片,只要在用來移動瀏覽和縮放的 CSS 轉換裡加入數行旋轉值程式碼就行了。

返回目錄頁