如何抹除筆跡筆觸 (HTML)
[ 本文的目標對象是撰寫 Windows 執行階段 App 的 Windows 8.x 和 Windows Phone 8.x 開發人員。如果您正在開發適用於 Windows 10 的 App,請參閱 最新文件 ]
了解如何在使用 JavaScript 的 Windows 市集應用程式中抹除筆跡筆觸。
Windows 8.1 的更新: Windows 8.1 對指標輸入 API 引入了數種更新及改進。如需詳細資訊,請參閱 Windows 8.1 的 API 變更。
因為支援筆跡的應用程式需要先分辨不同的輸入,是繪製筆跡筆觸的輸入,或抹除筆跡筆觸的輸入,所以抹除通常會實作為強制回應狀態。
目前有不同的方式可以進行抹除的動作:應用程式列命令、畫筆橡皮擦觸控端或筆身按鈕 (含筒狀滾輪按鈕)。因為筆身按鈕可以減少使用者不停地在繪圖平面和應用程式列之間來回移動,所以十分實用。因此,您的應用程式應該支援所有選項以及裝置功能。
讓我們在這裡顯示一張畫布,根據使用者指定的輸入狀態,其中的繪圖平面可以接受筆跡筆觸以及抹除筆跡筆觸。
InkManipulationMode 會指定使用者繪製的任何新筆觸行為。在 erasing 模式中,新的筆觸會顯示在繪圖平面上但不會轉譯,當使用者提起畫筆時便消失。當使用者畫出一條抹除筆觸連到現有的筆觸後,現有的筆觸會被抹除。這是 Windows 8 筆跡平台支援的預設行為。
您必須知道的事
技術
先決條件
這是改編自快速入門:擷取筆跡資料。
我們假設您可以利用 JavaScript,以適用於 JavaScript 的 Windows Library 範本來建立基本的 Windows 市集應用程式。
- 如需建立第一個 Windows 市集應用程式的指示,請參閱使用 JavaScript 建立您的第一個 Windows 市集應用程式。
- 如需使用 WinJS 物件和控制項的相關資訊,請參閱快速入門:新增 WinJS 控制項與樣式。
指示
步驟 1: 建立筆跡管理員
把將處理及操作從指標輸入取得之筆跡相關資料的 InkManager 物件初始化。
// Create an ink manager.
// InkManager is documented at https://go.microsoft.com/fwlink/?LinkID=260648.
var inkManager = new Windows.UI.Input.Inking.InkManager();
步驟 2: 定義起始筆觸設定
每一個 InkManipulationMode 都有獨特的筆觸設定,指出使用者目前是使用什麼模式。根據預設,本範例是從 inking 模式開始並使用指定的筆觸設定。
// Initial stroke property values.
var strokeColor;
var strokeWidth;
步驟 3: 進行模式的選擇
提供一種方法,讓您的使用者可以選擇 InkManipulationMode。
首先,我們會宣告一組模式按鈕。
<body>
<div id="applicationTitle">Ink sample</div>
<div>
<canvas id="inkCanvas"></canvas>
<div>
<button id="load">Load</button>
<button id="save">Save</button>
<button id="draw">Draw</button>
<button id="select">Select</button>
<button id="selectall">Select all</button>
<button id="erase">Erase</button>
<button id="eraseAll">Erase all</button>
<button id="recognize" value="selected">Handwriting recognition</button>
</div>
</div>
<div id="modeMessage"></div>
<div id="deviceMessage"></div>
<div id="statusMessage"></div>
</body>
然後,宣告事件接聽程式。
get("erase").addEventListener("click", eraseStrokes, false);
最後,我們會在按一下事件處理常式中,為每一個模式按鈕定義筆觸設定。
// Set up draw mode.
function drawStrokes() {
inkManager.mode = Windows.UI.Input.Inking.InkManipulationMode.inking;
strokeColor = "black";
strokeWidth = 2;
modeMessage.innerText = inkManager.mode;
}
// Set up selection mode.
function selectStrokes() {
inkManager.mode = Windows.UI.Input.Inking.InkManipulationMode.selecting;
strokeColor = "red";
strokeWidth = 1;
modeMessage.innerText = inkManager.mode;
}
// Set up erase mode.
function eraseStrokes() {
inkManager.mode = Windows.UI.Input.Inking.InkManipulationMode.erasing;
strokeColor = "gold";
strokeWidth = 1;
modeMessage.innerText = inkManager.mode;
}
步驟 4: 抹除所有筆觸
提供可以一次抹除所有筆觸的選項。
// Select all strokes handler.
function eraseAllStrokes() {
inkManager.mode = Windows.UI.Input.Inking.InkManipulationMode.inking;
strokeColor = "black";
strokeWidth = 2;
modeMessage.innerText = "Erase all strokes.";
// Iterate through each stroke.
inkManager.getStrokes().forEach(
function (stroke) {
stroke.selected = 1;
}
);
inkManager.deleteSelected();
renderAllStrokes();
}
步驟 5: 刪除之後,重新轉譯筆觸
renderAllStrokes
函式會使用 inkManager
透過 bezierCurveTo 方法處理每個筆觸線段。重新繪製每一個筆觸時,都會使用適當的筆觸設定。(如果抹除,就會從 InkManager 筆觸集合刪除筆觸,不會再顯示在畫布上。)
// Render all strokes using bezier curves instead of line segments.
function renderAllStrokes()
{
statusMessage.innerText += "Render strokes as bezier curves."
// Clear the drawing surface of existing strokes.
inkContext.clearRect(0, 0, inkCanvas.width, inkCanvas.height);
// Iterate through each stroke.
inkManager.getStrokes().forEach(
function (stroke)
{
inkContext.beginPath();
if (stroke.selected) {
inkContext.lineWidth = stroke.drawingAttributes.size.width * 2;
inkContext.strokeStyle = "green";
} else {
inkContext.lineWidth = stroke.drawingAttributes.size.width;
inkContext.strokeStyle = "black";
}
// Enumerate through each line segment of the stroke.
var first = true;
stroke.getRenderingSegments().forEach(
function (segment)
{
// Move to the starting screen location of the stroke.
if (first)
{
inkContext.moveTo(segment.position.x, segment.position.y);
first = false;
}
// Calculate the bezier curve for the segment.
else
{
inkContext.bezierCurveTo(segment.bezierControlPoint1.x,
segment.bezierControlPoint1.y,
segment.bezierControlPoint2.x,
segment.bezierControlPoint2.y,
segment.position.x, segment.position.y);
}
}
);
// Draw the stroke.
inkContext.stroke();
inkContext.closePath();
}
);
}
完整範例
//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//// PARTICULAR PURPOSE.
////
//// Copyright (c) Microsoft Corporation. All rights reserved
// Windows Store app that demonstrates the use of the Windows.UI.Input.Inking APIs.
// Ink functionality is documented at https://go.microsoft.com/fwlink/?LinkID=260649.
// User interaction functionality is documented at https://go.microsoft.com/fwlink/?LinkID=260650.
// Ink APIs are documented at https://go.microsoft.com/fwlink/?LinkID=260652.
// Pointer APIs are documented at https://go.microsoft.com/fwlink/?LinkID=260653.
(function ()
{
"use strict";
//
// Global variables
//
// UI object references.
var inkCanvas;
var inkContext;
var modeMessage;
var deviceMessage
var statusMessage;
// Create an ink manager.
// InkManager is documented at https://go.microsoft.com/fwlink/?LinkID=260648.
var inkManager = new Windows.UI.Input.Inking.InkManager();
// Initial pointer values.
var pointerId = -1;
var pointerDeviceType = null;
// Initial stroke property values.
var strokeColor;
var strokeWidth;
//
// End global variables
//
// Obtain reference to the specified element.
function get(elementId)
{
return document.getElementById(elementId);
}
function initialize()
{
// Set up the UI.
inkCanvas = get("inkCanvas");
inkContext = inkCanvas.getContext("2d");
inkContext.lineCap = "round";
inkContext.lineKJoin = "round";
inkCanvas.width = window.innerWidth - 10;
inkCanvas.height = window.innerHeight * 0.5;
deviceMessage = get("deviceMessage");
deviceMessage.innerText = "Undefined";
modeMessage = get("modeMessage");
modeMessage.innerText = inkManager.mode;
statusMessage = get("statusMessage");
statusMessage.innerText = "No pointer input detected."
// Set initial ink mode.
drawStrokes();
// Set default recognition language.
if (!setRecognizerByName("Microsoft English (US) Handwriting Recognizer")) {
statusMessage.innerText += "\nRecognition: Failed to find English (US) recognizer.";
}
else {
statusMessage.innerText += "\nRecognition: English (US) recognizer.";
}
// Set up the handlers for input processing.
inkCanvas.addEventListener("pointerdown", onPointerDown, false);
inkCanvas.addEventListener("pointermove", onPointerMove, false);
inkCanvas.addEventListener("pointerup", onPointerUp, false);
get("save").addEventListener("click", saveStrokes, false);
get("load").addEventListener("click", loadStrokes, false);
get("draw").addEventListener("click", drawStrokes, false);
get("select").addEventListener("click", selectStrokes, false);
get("selectall").addEventListener("click", selectAllStrokes, false);
get("erase").addEventListener("click", eraseStrokes, false);
get("eraseAll").addEventListener("click", eraseAllStrokes, false);
get("recognize").addEventListener("click", recognizeStrokes, false);
}
document.addEventListener("DOMContentLoaded", initialize, false);
function getPointerDeviceType(pId)
{
var pointerDeviceType;
var pointerPoint = Windows.UI.Input.PointerPoint.getCurrentPoint(pId);
switch (pointerPoint.pointerDevice.pointerDeviceType)
{
case Windows.Devices.Input.PointerDeviceType.touch:
pointerDeviceType = "Touch";
break;
case Windows.Devices.Input.PointerDeviceType.pen:
pointerDeviceType = "Pen";
break;
case Windows.Devices.Input.PointerDeviceType.mouse:
pointerDeviceType = "Mouse";
break;
default:
pointerDeviceType = "Undefined";
}
deviceMessage.innerText = pointerDeviceType;
return pointerDeviceType;
}
// Occurs when the pointer (touch, pen, mouse) is detected by the canvas.
// Each stroke begins with onPointerDown.
function onPointerDown(evt)
{
// Get the device type for the pointer input.
pointerDeviceType = getPointerDeviceType(evt.pointerId);
// Process pen and mouse (with left button) only. Reserve touch for manipulations.
if ((pointerDeviceType === "Pen") || ((pointerDeviceType === "Mouse") && (evt.button === 0)))
{
statusMessage.innerText = pointerDeviceType + " pointer down: Start stroke. "
// Process one pointer at a time.
if (pointerId === -1)
{
var current = evt.currentPoint;
// Start drawing the stroke.
inkContext.beginPath();
inkContext.lineWidth = strokeWidth;
inkContext.strokeStyle = strokeColor;
inkContext.moveTo(current.position.x, current.position.y);
// Add current pointer to the ink manager (begin stroke).
inkManager.processPointerDown(current);
// The pointer id is used to restrict input processing to the current stroke.
pointerId = evt.pointerId;
}
}
else
{
// Process touch input.
}
}
// Mouse: Occurs when the pointer moves.
// Pen/Touch: Occurs at a steady rate (approx. 100 messages/second) whether the pointer moves or not.
function onPointerMove(evt)
{
// Process pen and mouse (with left button) only. Reserve touch for manipulations.
if ((pointerDeviceType === "Pen") || ((pointerDeviceType === "Mouse") && (evt.button === -1)))
{
statusMessage.innerText = pointerDeviceType + " pointer move: Draw stroke as lines. "
// The pointer Id is used to restrict input processing to the current stroke.
// pointerId is updated in onPointerDown().
if (evt.pointerId === pointerId)
{
var current = evt.currentPoint;
// Draw stroke in real time.
inkContext.lineTo(current.rawPosition.x, current.rawPosition.y);
inkContext.stroke();
// Add current pointer to the ink manager (update stroke).
inkManager.processPointerUpdate(current);
}
}
else
{
// Process touch input.
}
}
// Occurs when the pointer (touch, pen, mouse) is lifted from the canvas.
// Each stroke ends with onPointerUp.
function onPointerUp(evt)
{
// Process pen and mouse (with left button) only. Reserve touch for manipulations.
if ((pointerDeviceType === "Pen") || ((pointerDeviceType === "Mouse") && (evt.button === 0)))
{
statusMessage.innerText = pointerDeviceType + " pointer up: Finish stroke. "
if (evt.pointerId === pointerId) {
// Add current pointer to the ink manager (end stroke).
inkManager.processPointerUp(evt.currentPoint);
// End live drawing.
inkContext.closePath();
// Render strokes using bezier curves.
renderAllStrokes();
// Reset pointer Id.
pointerId = -1;
}
}
else
{
// Process touch input.
}
}
// Render all strokes using bezier curves instead of line segments.
function renderAllStrokes()
{
statusMessage.innerText += "Render strokes as bezier curves."
// Clear the drawing surface of existing strokes.
inkContext.clearRect(0, 0, inkCanvas.width, inkCanvas.height);
// Iterate through each stroke.
inkManager.getStrokes().forEach(
function (stroke)
{
inkContext.beginPath();
if (stroke.selected) {
inkContext.lineWidth = stroke.drawingAttributes.size.width * 2;
inkContext.strokeStyle = "green";
} else {
inkContext.lineWidth = stroke.drawingAttributes.size.width;
inkContext.strokeStyle = "black";
}
// Enumerate through each line segment of the stroke.
var first = true;
stroke.getRenderingSegments().forEach(
function (segment)
{
// Move to the starting screen location of the stroke.
if (first)
{
inkContext.moveTo(segment.position.x, segment.position.y);
first = false;
}
// Calculate the bezier curve for the segment.
else
{
inkContext.bezierCurveTo(segment.bezierControlPoint1.x,
segment.bezierControlPoint1.y,
segment.bezierControlPoint2.x,
segment.bezierControlPoint2.y,
segment.position.x, segment.position.y);
}
}
);
// Draw the stroke.
inkContext.stroke();
inkContext.closePath();
}
);
}
// Set up draw mode.
function drawStrokes() {
inkManager.mode = Windows.UI.Input.Inking.InkManipulationMode.inking;
strokeColor = "black";
strokeWidth = 2;
modeMessage.innerText = inkManager.mode;
}
// Set up selection mode.
function selectStrokes() {
inkManager.mode = Windows.UI.Input.Inking.InkManipulationMode.selecting;
strokeColor = "red";
strokeWidth = 1;
modeMessage.innerText = inkManager.mode;
}
// Set up erase mode.
function eraseStrokes() {
inkManager.mode = Windows.UI.Input.Inking.InkManipulationMode.erasing;
strokeColor = "gold";
strokeWidth = 1;
modeMessage.innerText = inkManager.mode;
}
// Select all strokes handler.
function selectAllStrokes() {
inkManager.mode = Windows.UI.Input.Inking.InkManipulationMode.selecting;
strokeColor = "red";
strokeWidth = 1;
modeMessage.innerText = "Select all strokes.";
// Iterate through each stroke.
inkManager.getStrokes().forEach(
function (stroke) {
stroke.selected = 1;
}
);
renderAllStrokes();
}
// Select all strokes handler.
function eraseAllStrokes() {
inkManager.mode = Windows.UI.Input.Inking.InkManipulationMode.inking;
strokeColor = "black";
strokeWidth = 2;
modeMessage.innerText = "Erase all strokes.";
// Iterate through each stroke.
inkManager.getStrokes().forEach(
function (stroke) {
stroke.selected = 1;
}
);
inkManager.deleteSelected();
renderAllStrokes();
}
// Save all strokes owned by inkManager.
function saveStrokes()
{
// Ensure that strokes exist before calling saveAsync.
if (inkManager.getStrokes().size > 0)
{
// Set up the file save screen.
var savePicker = Windows.Storage.Pickers.FileSavePicker();
savePicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.picturesLibrary;
savePicker.fileTypeChoices.insert("GIF with embedded ISF", [".gif"]);
savePicker.defaultFileExtension = ".gif";
// Set up the stream.
var saveStream = null;
// Asynchronously save the ink data to the stream.
savePicker.pickSaveFileAsync().done(
function (file)
{
if (null !== file)
{
file.openAsync(Windows.Storage.FileAccessMode.readWrite).then(
function (stream)
{
saveStream = stream;
return inkManager.saveAsync(saveStream);
}
).then(
function ()
{
return saveStream.flushAsync();
},
function (e) {
// Override the standard saveAsync error with our own.
throw new Error("saveAsync");
}
).done(
function ()
{
statusMessage.innerText = "Strokes saved as GIF with embedded ISF (.gif).";
saveStream.close();
},
function (e) {
statusMessage.innerText = "Save: " + e.toString();
// Close the stream if open.
if (saveStream) {
saveStream.close();
}
}
);
}
}
);
}
else
{
statusMessage.innerText = "No strokes to save.";
}
}
// Load strokes into an inkManager.
function loadStrokes()
{
// Set up the file open screen.
var openPicker = Windows.Storage.Pickers.FileOpenPicker();
openPicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.picturesLibrary;
openPicker.fileTypeFilter.replaceAll([".gif"]);
// Set up the stream.
var loadStream = null;
// Asynchronously load the ink data from the stream.
openPicker.pickSingleFileAsync().done(
function (file)
{
if (null != file)
{
file.openAsync(Windows.Storage.FileAccessMode.read).then(
function (stream) {
loadStream = stream;
return inkManager.loadAsync(loadStream);
}).done(
function()
{
var strokes = inkManager.getStrokes().length;
if (strokes === 0)
{
statusMessage.innerText = "No strokes in file.";
}
else
{
statusMessage.innerText = strokes + " strokes loaded.";
}
renderAllStrokes();
loadStream.close();
},
function (e)
{
statusMessage.innerText = "Load failed.";
if (loadStream)
{
// Close the stream if open.
loadStream.close();
}
});
}
});
}
/// <summary>
/// Finds a specific recognizer, and sets the inkManager's default to that recognizer.
/// Returns true if successful.
/// </summary>
/// <param name="recognizerName">The name of the handwriting recognizer.</param>
function setRecognizerByName(recognizerName) {
try {
// recognizers is a normal JavaScript array
var recognizers = inkManager.getRecognizers();
for (var i = 0, len = recognizers.length; i < len; i++) {
if (recognizerName === recognizers[i].name) {
inkManager.setDefaultRecognizer(recognizers[i]);
return true;
}
}
}
catch (e) {
displayError("setRecognizerByName: " + e.toString());
}
return false;
}
/// <summary>
/// Invoked when the "Handwriting recognition" button is pressed.
/// The ink manager processes all strokes through recognizeAsync, which returns
/// the number of words detected and a set of recognition results for each word.
/// </summary>
/// <param name="evt">The event object.</param>
function recognizeStrokes(evt) {
// Ensure ink strokes exist before calling recognizeAsync.
if (inkManager.getStrokes().length > 0) {
// recognizeAsync fails if other recognition tasks are in progress.
try {
// The ink manager is used to store the recognition results.
// recognizeAsync has 3 modes: all | selected | recent.
// For this example, we process all strokes.
// recognizeAsync is documented at https://go.microsoft.com/fwlink/?LinkID=265172.
inkManager.recognizeAsync(Windows.UI.Input.Inking.InkRecognitionTarget.all).done
(
// The recognitionResult object returned by recognizeAsync exposes the
// bounding rect, strokes, and text candidates for each word.
// In this example, we simply display the word count and recognition results.
function (results) {
// recognizeAsync does not automatically update existing recognition results in the ink manager.
// updateRecognitionResults is documented at https://go.microsoft.com/fwlink/?LinkID=265175.
inkManager.updateRecognitionResults(results);
var x = inkManager.getRecognizers();
// Display the number of words returned in results.
statusMessage.innerText = "Words recognized: " + results.length.toString();
// Iterate through each word and display the ranked list of possible matches.
for (var i = 0; i < results.length; i++) {
statusMessage.innerText += "\nWord" + (i+1).toString() + ":";
var alts = results[i].getTextCandidates();
for (var j = 0; j < alts.length; j++) {
statusMessage.innerText += " " + alts[j].toString();
}
}
},
function (e) {
displayError("InkManager::recognizeAsync: " + e.toString());
}
);
}
catch (e) {
displayError("recognize: " + e.toString());
}
}
else {
statusMessage.innerText = "No strokes to recognize.";
}
}
// Returns true if any strokes inside the ink manager are selected; false otherwise.
function anySelected() {
var strokes = inkManager.getStrokes();
var len = strokes.length;
for (var i = 0; i < len; i++) {
if (strokes[i].selected) {
return true;
}
}
return false;
}
})();
相關主題
概念
參考
範例 (DOM)
範例 (Windows 市集應用程式 API)