クイック スタート: 静的ジェスチャ (HTML)
[ この記事は、Windows ランタイム アプリを作成する Windows 8.x および Windows Phone 8.x 開発者を対象としています。Windows 10 向けの開発を行っている場合は、「最新のドキュメント」をご覧ください]
基本的な Windows ランタイム ジェスチャ イベントを使って、「Windows のタッチ言語」で記述されている静的ジェスチャ (タップ、ダブルタップ、長押し、右タップなど) のユーザー エクスペリエンスをカスタマイズできます。
ほとんどのアプリはジェスチャ (タップ、パン、ズームなど) を処理し、生のポインター データはジェスチャ検出へ渡す場合を除いてほとんど使いません。このサンプルでは、静的ジェスチャの処理のサポートで生のポインター データを使ってアプリの操作モデルを拡張し、「クイック スタート: ポインター」に記載されている基本的なポインター イベントをベースに構築します。
Windows 8.1 の変更点: Windows 8.1 では、ポインター入力 API に対するさまざまな変更や改良が行われています。詳しくは、「Windows 8.1 の API の変更点」をご覧ください。
JavaScript を使ってアプリを開発するのが初めての場合: 以下のトピックに目を通して、ここで説明されているテクノロジをよく理解できるようにしてください。
イベントについては、「クイック スタート: HTML コントロールの追加とイベントの処理」をご覧ください。
アプリの機能の概要:
この機能について詳しくは、「アプリの機能の概要」シリーズをご覧ください。
ユーザー エクスペリエンス ガイドライン:
プラットフォーム コントロール ライブラリ (HTML と XAML) は、標準的な対話式操作、アニメーション化された物理的効果、視覚的フィードバックなど、完全なユーザー操作エクスペリエンスを提供しています。 操作のサポートをカスタマイズする必要がない場合は、これらのビルトイン コントロールを使います。
プラットフォーム コントロールでは十分に対応できない場合は、以下のユーザー操作ガイドラインに従うと、どの入力モードでも一貫性のある、魅力的でイマーシブな対話式操作エクスペリエンスを実現できます。これらのガイドラインは、主にタッチ入力を対象として説明していますが、タッチパッド、マウス、キーボード、スタイラスでの入力にも当てはまります。
- 一般的なユーザー操作のガイドライン
- クロススライドのガイドライン
- 光学式ズームとサイズ変更のガイドライン
- パンのガイドライン
- 回転のガイドライン
- セマンティック ズームのガイドライン
- テキストと画像の選択のガイドライン
- ターゲットの設定のガイドライン
- 視覚的なフィードバックのガイドライン
サンプル: アプリのサンプルで、この機能の動作を実際に確かめることができます。
入力: DOM ポインター イベント処理のサンプルに関するページ
入力: 操作とジェスチャ (JavaScript) のサンプルに関するページ
入力: Windows 8 のジェスチャのサンプルに関するページ
目標: タッチ、マウス、ペン/スタイラスによる対話式操作からの入力と Windows ランタイム ジェスチャ イベントを使って静的ジェスチャをリッスンし、処理する方法について理解する。
必要条件
「クイック スタート: ポインター」と「クイック スタート: DOM ジェスチャおよび操作」をご覧ください。
JavaScript 用 Windows ライブラリのテンプレートが使われた JavaScript を使った基本的なアプリの作成経験が必要です。
このチュートリアルを行うには、次の作業を行う必要があります。
- Microsoft Visual Studio をインストールします。
- 開発者用ライセンスを取得します。詳しくは、「Visual Studio 2013 を使った開発」をご覧ください。
- JavaScript を使って初めての Windows ストア アプリを作成します。
- WinJS のオブジェクトとコントロールについて詳しくは、「クイックスタート: WinJS コントロールとスタイルの追加」をご覧ください。
完了までの時間: 30 分.
ジェスチャ イベントとは
ジェスチャは、入力デバイス (タッチ画面上の 1 本以上の指、ペン/スタイラス デジタイザー、マウスなど) 上や、入力デバイスにより実行される物理的な動作や動きです。これらの自然な対話式操作は、システムとアプリの両方の要素に対する操作にマップされます。 詳しくは、「ジェスチャ、操作、対話的操作」をご覧ください。
次の表に、このクイック スタートで説明する静的ジェスチャについて定義します。
ジェスチャ | 説明 | |
---|---|---|
タップ/ダブルタップ |
すぐに離される (終了する) 1 か所の接触です。 要素をタップするとプライマリ操作が呼び出されます。 ダブルタップはすばやく 2 回タップするジェスチャであり、アプリで必要に応じて処理できます。
| |
長押し/右タップ |
時間のしきい値を超えるまで動かない 1 か所の接触です。 長押しにより、詳しい情報や説明を伝える視覚効果 (ヒントやコンテキスト メニューなど) が表示されます。操作はコミットされません。 右タップは、長押しジェスチャと密接に関係するジェスチャです。右タップ イベントは、長押しが離されたときに発生します。
| |
これらのジェスチャと、Windows のタッチ言語との関係について詳しくは、「タッチ操作の設計」をご覧ください。 |
重要 独自の対話式操作サポートを実装する場合は、ユーザーはアプリの UI 要素を直接操作できる直感的なエクスペリエンスを期待しているということを心に留めておいてください。 プラットフォーム コントロール ライブラリ (HTML と XAML) でのカスタムの対話式操作をモデル化し、一貫性と見つけやすさを維持することをお勧めします。これらのライブラリのコントロールでは、標準的な対話式操作、アニメーション化された物理的効果、視覚的フィードバック、アクセシビリティなど、完全なユーザー操作エクスペリエンスが提供されます。はっきりとした明確に定義されている要件があり、基本的な対話式操作ではシナリオがサポートされない場合のみ、カスタムの対話式操作を作ってください。
UI を作る
ここで示す例は、基本的な QA アプリです。四角形 (inputBox
) が、ポインター入力と静的ジェスチャを検出して処理するためのターゲット オブジェクトとして機能します。質問、ヒント、答えは、いずれもこのオブジェクト内に表示されます。
このアプリには、次のユーザー操作機能があります。
- ダブルタップ: 質問とアプリ タイマーを開始および停止します。
- タップ: 次の質問に進みます。
- 長押し: 現在の質問に対する一連のヒントを表示します。押したまま数秒経過するごとに新しいヒントが表示されます。この操作の動作は、長押しジェスチャは情報 UI の表示に限定して使うというタッチ言語の推奨事項と「視覚的なフィードバックのガイドライン」に準拠しています。
- 右タップ (長押しを離す動作): 指を離したときに、答えを表示するかどうかたずねるポップアップを表示します。これも、「視覚的なフィードバックのガイドライン」と Windows のタッチ言語のショートカット メニューに関する推奨事項に従っています。注 ジェスチャの処理コードを中心に説明するために、XML ファイルからの質問と答えのデータの読み取りと、一部の UI およびアプリ機能は完全には実装しません。
この例の HTML を次に示します。
<html>
<head>
<meta charset="utf-8" />
<title>js</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>
<script src="/js/inputprocessor.js"></script>
<script src="/js/datamanager.js"></script>
<script src="/js/cluemanager.js"></script>
</head>
<body>
<div class="TargetContainer" id="targetContainer">
<div id="inputBox">
<div id="instructions">Tap gray box below: Double tap to start questions, tap for next question, press and hold to show clues.</div>
<div id="questions"> </div>
<div id="answers">
<label for="answer">Answer:</label>
<input type="text" id="answer" maxlength="30" size="30" style="z-index:1" />
<button id="submit">Submit</button>
<button id="stumped">Stumped</button>
</div>
<div id="clues">
</div>
<div id="timerBox"></div>
</div>
<div id="eventLog"></div>
<div id="answerFloater">
<p>Show answer?</p>
<button id="yes">Yes</button>
<button id="no">No</button>
</div>
</div>
</body>
</html>
この例のカスケード スタイル シート (CSS) を次に示します。
注 パンまたはズームの操作中はポインター イベントが発生しません。領域のパンとズームは、CSS プロパティ msTouchAction、overflow、-ms-content-zooming を使って無効にできます。
body {
/*
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.
*/
overflow: hidden;
position: absolute;
font-family: 'Segoe UI';
font-size: small;
touch-action: none;
background-color: black;
}
div #targetContainer {
position: relative;
height: fill-available;
width: fill-available;
}
div #inputBox {
position: relative;
width: 640px;
height: 640px;
color: black;
overflow: hidden;
background-color: darkgrey;
margin: 0px;
padding: 0px;
border-width: 1px;
border-color: white;
border-style: solid;
}
div #instructions {
position: relative;
width: 100%;
height: fit-content;
color: black;
background-color: white;
visibility: visible;
}
div #questions {
position: relative;
width: 100%;
height: fit-content;
color: white;
background-color: black;
visibility: visible;
}
div #answers {
position: relative;
width: 100%;
height: fit-content;
color: white;
background-color: black;
visibility: visible;
}
div #clues {
position: relative;
width: 100%;
height: 100%;
background-color: DimGray;
}
div #timerBox {
background-color: red;
color: black;
position: absolute;
width: 100%;
bottom: 0px;
height: 20px;
text-align: center;
}
div #answerFloater {
position: absolute;
visibility: hidden;
top: 0px;
left: 0px;
background-color: blue;
}
div #eventLog {
font-size: xx-small;
position: absolute;
left: 0px;
top: 0px;
width: 640px;
height: 50px;
overflow: auto;
overflow-style: auto;
}
アプリを初期化する
質問と答えのオブジェクトを初期化します。
ここでは、グローバル変数を宣言し、UI オブジェクトへの参照を取得します。
var _applicationData;
var _localSettings;
var _data;
var _inputBox;
var _instructions;
var _answers;
var _questions;
var _clues;
var _eventLog;
var _floater;
function initialize() {
// Get our UI objects.
_inputBox = document.getElementById("inputBox");
_instructions = document.getElementById("instructions");
_questions = document.getElementById("questions");
_answers = document.getElementById("answers");
_clues = document.getElementById("clues");
_eventLog = document.getElementById("eventLog");
_floater = document.getElementById("answerFloater");
// Configure the target.
setTarget();
}
次に、質問と答えの UI を配置し、対話式操作オブジェクトを設定して、XML ファイルから質問と答えのデータを処理します。この例の詳しい XML データは、このトピックの最後に紹介している完全なリストで確認できます。
// Configure the interaction target.
function setTarget() {
// Set the position of the input target.
var inputLeft = (window.innerWidth - _inputBox.clientWidth) / 2.0;
var inputTop = (window.innerHeight - _inputBox.clientHeight) / 2.0;
var transform = (new MSCSSMatrix()).translate(inputLeft, inputTop);
_inputBox.style.msTransform = transform;
// Set the position of the event log.
transform = (new MSCSSMatrix()).translate(inputLeft, inputTop + _inputBox.clientHeight);
_eventLog.style.msTransform = transform;
// Associate interaction target with our input manager.
// Scope input to clue area only.
_clues.inputProcessor = new QandA.InputProcessor(_clues);
}
ジェスチャ認識エンジンを構成する
ここでは、対話式操作の処理を設定します。
ほとんどの場合は、選んだ言語フレームワークのポインター イベント ハンドラーのイベント引数を介してポインター情報を取得することをお勧めします。
アプリに必要なポインターの詳細をイベント引数が公開していない場合は、getCurrentPoint と getIntermediatePoints メソッドか、currentPoint と intermediatePoints プロパティを通じて、イベント引数の拡張ポインター データにアクセスできます。ポインター データのコンテキストを指定できるので、getCurrentPoint と getIntermediatePoints メソッドを使うことをお勧めします。
ヒント この例では、ジェスチャ認識エンジンに 1 つのオブジェクトだけが関連付けられています。多数の操作対象オブジェクトがあるアプリ (ジグソー パズルなど) の場合は、ターゲット オブジェクトでポインター入力が検出されたときにだけジェスチャ認識エンジンを動的に作ることを検討してください。ジェスチャ認識エンジンは、操作が完了したら破棄できます (その例については、入力: インスタンス化できるジェスチャのサンプルに関するページをご覧ください)。ジェスチャ認識エンジンの作成と破棄のオーバーヘッドを避けるには、初期化時にジェスチャ認識エンジンの小規模なプールを作り、必要に応じてそれらを動的に割り当てます。
入力プロセッサ オブジェクトには、すべてのポインター イベントとジェスチャ イベントをリッスンして処理するジェスチャ認識エンジン (gr
) が含まれています。質問と答えの UI は、ジェスチャ認識エンジンのイベント ハンドラーによって管理されます。
// Handle gesture recognition for this sample.
(function () {
"use strict";
var InputProcessor = WinJS.Class.define(
// Constructor
function InputProcessor_ctor(target) {
this._questionsStarted = false;
this._tapCount = 0;
// Create a clue manager.
this._clueManager = new QandA.ClueManager();
// Load xml data from file into local app settings.
var _dataObject = new QandA.DataManager();
_data = _dataObject.getData();
this._questionTotal = _data.selectNodes("questions/question").length;
this._doubleTap = false;
this._startTime;
this._intervalTimerId;
// Initialize the gesture recognizer.
this.gr = new Windows.UI.Input.GestureRecognizer();
// Turn off visual feedback for gestures.
// Visual feedback for pointer input is still displayed.
this.gr.showGestureFeedback = false;
// Configure gesture recognizer to process the following:
// double tap - start questions and timer.
// tap - move to next question.
// right tap - show answer.
// hold and hold with mouse - start clues.
this.gr.gestureSettings =
Windows.UI.Input.GestureSettings.tap |
Windows.UI.Input.GestureSettings.doubleTap |
Windows.UI.Input.GestureSettings.rightTap |
Windows.UI.Input.GestureSettings.hold |
Windows.UI.Input.GestureSettings.holdWithMouse;
//
// Set event listeners.
//
// Get our context.
var that = this;
// Register event listeners for these gestures.
this.gr.addEventListener('tapped', tappedHandler);
this.gr.addEventListener("holding", holdingHandler);
this.gr.addEventListener("righttapped", rightTappedHandler);
// The following functions are registered to handle DOM pointer events
//
// Basic pointer handling to highlight input area.
target.addEventListener("pointerover", function onPointerOver(eventInfo) {
eventInfo.stopImmediatePropagation = true;
_eventLog.innerText += "pointer over || ";
eventInfo.target.style.backgroundColor = "DarkGray";
}, false);
// Basic pointer handling to highlight input area.
target.addEventListener("pointerout", function onPointerOut(eventInfo) {
eventInfo.stopImmediatePropagation = true;
_eventLog.innerText += "pointer out || ";
eventInfo.target.style.backgroundColor = "DimGray";
}, false);
// Handle the pointer move event.
// The holding gesture is routed through this event.
// If pointer move is not handled, holding will not fire.
target.addEventListener("pointermove", function onPointerMove(eventInfo) {
eventInfo.stopImmediatePropagation = true;
// Get intermediate PointerPoints
var pps = eventInfo.intermediatePoints;
// Pass the array of PointerPoints to the gesture recognizer.
that.gr.processMoveEvents(pps);
}, false);
// Handle the pointer down event.
target.addEventListener("pointerdown", function onPointerDown(eventInfo) {
eventInfo.stopImmediatePropagation = true;
_eventLog.innerText += "pointer down || ";
// Hide the floater if visible.
_floater.style.visibility = "hidden";
// Get the PointerPoint for the pointer event.
var pp = eventInfo.currentPoint;
// Get whether this pointer down event is within
// the time threshold for a double tap.
that._doubleTap = that.gr.canBeDoubleTap(pp);
// Pass the PointerPoint to the gesture recognizer.
that.gr.processDownEvent(pp);
}, false);
// Handle the pointer up event.
target.addEventListener("pointerup", function onPointerUp(eventInfo) {
eventInfo.stopImmediatePropagation = true;
_eventLog.innerText += "pointer up || ";
// Get the current PointerPoint
var pp = eventInfo.currentPoint;
// Pass the PointerPoint to the gesture recognizer.
that.gr.processUpEvent(pp);
}, false);
// The following functions are registered to handle gesture events.
//
// This handler processes taps and double taps.
// Potential double taps are identified in the pointer down handler.
function tappedHandler(evt) {
// Single tap and questions started: Display next question.
if (!that._doubleTap && that._questionsStarted) {
_eventLog.innerText += "tapped || ";
_instructions.innerText = "Double tap to stop questions.";
_clues.innerText = "";
that._tapCount++;
that._clueManager.tapCount = that.tapCount;
if (that._tapCount > that._questionTotal) {
_questions.innerText = "No more questions.";
} else {
var xpath = "questions/question[" + (that._tapCount % (that._questionTotal + 1)) + "]/q";
// Read data from a simple setting
_questions.innerText = _data.selectSingleNode(xpath).innerText;
}
}
// Single tap and questions not started: Don't do much.
else if (!that._doubleTap && !that._questionsStarted) {
_eventLog.innerText += "tapped || ";
_instructions.innerText = "Double tap to start questions.";
}
// Double tap and questions not started: Display first question.
else if (that._doubleTap && !that._questionsStarted) {
_eventLog.innerText += "double-tapped || ";
// Return if last question displayed.
if (that._tapCount > that._questionTotal) {
_questions.innerText = "No more questions.";
return;
}
// Start questions.
that._questionsStarted = true;
_instructions.innerText = "Starting questions (double tap to stop questions).";
// Question number is based on tap count.
that._tapCount++;
// Select question from XML data object.
var xpath = "questions/question[" + (that._tapCount % (that._questionTotal + 1)) + "]/q";
_questions.innerText = _data.selectSingleNode(xpath).innerText;
// Display a basic timer once questions started.
that._startTime = new Date().getTime();
that._intervalTimerId = setInterval(displayTimer, 100);
}
// Double tap and questions started: Stop questions and timer.
else if (that._doubleTap && that._questionsStarted) {
_eventLog.innerText += "double-tapped || ";
_instructions.innerText = "Questions stopped (double tap to start questions).";
that._questionsStarted = false;
clearInterval(that._intervalTimerId);
}
};
// For this app, we display a basic timer once questions start.
// In a more robust app, could be used for achievements.
function displayTimer() {
var x = new Date().getTime();
timerBox.innerText = (x - that._startTime) / 1000;
}
// This handler processes right taps.
// For all pointer devices a right tap is fired on
// the release of a press and hold gesture.
// For mouse devices, righttapped is also fired on a right button click.
// For pen devices,
function rightTappedHandler(evt) {
if (!that._questionsStarted) {
return;
}
var transform = (new MSCSSMatrix()).
translate(
(window.innerWidth - _inputBox.clientWidth) / 2.0 + evt.position.x,
(window.innerHeight - _inputBox.clientHeight) / 2.0 + evt.position.y);
_floater.style.visibility = "visible";
_floater.style.msTransform = transform;
eventLog.innerText = "right-tap || ";
}
// The pointer move event must also be handled because the
// holding gesture is routed through this event.
// If pointer move is not handled, holding will not fire.
// A holding event is fired approximately one second after
// a pointer down if no subsequent movement is detected.
function holdingHandler(evt) {
if (!that._questionsStarted)
return;
if (evt.holdingState == Windows.UI.Input.HoldingState.started) {
_eventLog.innerText += "holding || ";
// Create a clue manager.
that._clueManager.tapCount = that._tapCount;
// Start displaying clues.
that._clueManager.displayClues();
} else if (evt.holdingState == Windows.UI.Input.HoldingState.completed) {
that._clueManager.destroy();
_eventLog.innerText += "holding completed || ";
} else {
_eventLog.innerText += "holding canceled || ";
}
}
},
{},
{});
WinJS.Namespace.define("QandA", {
InputProcessor: InputProcessor
});
})();
最後に、長押しジェスチャの間に現在の質問に基づいて一連のヒントを表示するヒント マネージャーを構成します。
// Handle data for this sample.
(function () {
"use strict";
var ClueManager = WinJS.Class.define(
// Constructor
function ClueManager_ctor() {
this._clueTimerId = null;
},
{
displayClues: function () {
var clue;
var clueCount = 0;
var clueCollection = _data.selectNodes("questions/question[" + this.tapCount + "]/clues/clue");
this._clueTimerId = setInterval(function () {
clueCount++;
if (clueCount > clueCollection.length) {
_clues.innerText += "\nNo more clues.";
clearInterval(_clueTimerId);
return;
}
if (clueCount == 1)
clue = clueCollection.first();
_clues.innerText += "\n" + clue.current.innerText;
clue.moveNext();
}, 2000);
},
destroy: function () {
clearInterval(this._clueTimerId);
},
tapCount: {
get: function () {
return this._tapCount;
},
set: function (tapCount) {
this._tapCount = tapCount;
}
}
},
{});
WinJS.Namespace.define("QandA", {
ClueManager: ClueManager
});
})();
さらに複雑なサンプルへのリンクについては、このページの最後にある関連トピックをご覧ください。
完全な例
「静的ジェスチャのコード一式」をご覧ください。
要約と次のステップ
このクイック スタートでは、JavaScript を使った Windows ストア アプリでの静的ジェスチャ イベントの処理について説明しました。
ポインター イベントと組み合わせて使われる基本的なジェスチャ認識は、タップ、ダブルタップ、長押し、右タップなどの単純な対話式操作の管理に便利です。
さらに複雑なジェスチャ処理の例については、入力: インスタンス化できるジェスチャのサンプルに関するページをご覧ください。
注 このサンプルは、カスタムの対話式操作に関しては Windows のタッチ言語のガイダンスに準拠していません。説明を目的として一部の静的ジェスチャは再定義されています。
フル カスタマイズされたユーザー操作エクスペリエンスを提供するためのより複雑な操作 (スライド、スワイプ、回転、ピンチ、ストレッチ) の処理については、「クイック スタート: 操作ジェスチャ」をご覧ください。
Windows のタッチ言語について詳しくは、「タッチ操作の設計」をご覧ください。
関連トピック
開発者向け
Windows ストア アプリの開発 (JavaScript と HTML)
デザイナー向け