前端與硬件設(shè)備交互探秘,最全總結(jié)!
隨著技術(shù)的發(fā)展,前端技術(shù)與硬件設(shè)備的交互模式正變得日益豐富多樣。從基礎(chǔ)的點擊、滑動操作,進化到高級的傳感器數(shù)據(jù)捕獲及設(shè)備遠程操控,前端技術(shù)正持續(xù)跨越傳統(tǒng)的交互壁壘,為用戶塑造出更為便捷且智能的使用體驗。本文就來對前端與硬件交互多種方式進行全面盤點。
接收設(shè)備的輸入
在現(xiàn)代Web開發(fā)中,處理用戶輸入事件是構(gòu)建交互式應(yīng)用的關(guān)鍵部分。這些輸入事件可以來自多種設(shè)備,包括鍵盤、鼠標(或觸控板)、觸摸屏以及游戲控制器等。
鍵盤事件
鍵盤事件是最常見的用戶輸入方式之一,包括按鍵按下(keydown)、按鍵釋放(keyup)等事件。
- keydown:當按鍵被按下時觸發(fā)。
- keyup:當按鍵被釋放時觸發(fā)。
監(jiān)聽鍵盤事件的基本方法是使用addEventListener方法:
document.addEventListener('keydown', function(event) {
// 使用event.key來獲取按鍵的值
console.log('Key down:', event.key);
});
Pointer事件
Pointer 事件是一個統(tǒng)一的指針輸入模型,旨在處理所有類型的指針輸入,包括鼠標、觸控筆和觸摸。Pointer事件包括pointerdown、pointerup、pointermove等。
- pointerdown:當指針按下時觸發(fā)。
- pointerup:當指針釋放時觸發(fā)。
- pointermove:當指針移動時觸發(fā)。
監(jiān)聽Pointer事件的例子:
document.addEventListener('pointerdown', function(event) {
console.log('Pointer down at:', event.clientX, event.clientY);
});
還可以直接使用鼠標事件,常見的鼠標事件包括:
- click:單擊鼠標左鍵時觸發(fā)。
- dblclick:雙擊鼠標左鍵時觸發(fā)。
- mousedown:鼠標按鈕被按下時觸發(fā)。
- mouseup:鼠標按鈕被松開時觸發(fā)。
- mousemove:鼠標在元素上移動時觸發(fā)。
- mouseenter:鼠標進入元素時觸發(fā)。
- mouseleave:鼠標離開元素時觸發(fā)。
- contextmenu:用戶右鍵點擊時觸發(fā)。
- wheel:鼠標滾輪滾動時觸發(fā)。
- auxclick:輔助鍵(如中鍵或右鍵)被點擊時觸發(fā)。
例子:
document.addEventListener('mousedown', function(event) {
// 使用event.button來判斷哪個鼠標按鈕被按下
let buttonPressed;
switch (event.button) {
case 0:
buttonPressed = 'Left button';
break;
case 1:
buttonPressed = 'Middle button';
break;
case 2:
buttonPressed = 'Right button';
break;
default:
buttonPressed = 'Unknown button';
}
console.log(buttonPressed);
// 如果按下了右鍵,則阻止默認的上下文菜單
if (event.button === 2) {
event.preventDefault();
}
});
mousedown事件對象的button屬性表示被按下的鼠標按鈕。這個屬性的值如下:
- 0:表示主按鈕(通常是左鍵)。
- 1:表示中間按鈕(通常是滾輪按鈕,但不是所有鼠標都有)。
- 2:表示次按鈕(通常是右鍵)。
游戲控制器
Gamepad API 允許 Web 應(yīng)用檢測并響應(yīng)來自游戲控制器的輸入,包括按鈕按壓和軸移動。這對于開發(fā)需要精確控制的游戲或應(yīng)用非常有用。
使用 Gamepad API,首先需要檢查是否有連接的游戲控制器:
function checkGamepads() {
const gamepads = navigator.getGamepads();
for (let i = 0; i < gamepads.length; i++) {
if (gamepads[i]) {
console.log('Gamepad connected:', gamepads[i]);
// 處理游戲控制器輸入
}
}
}
// 定期檢查是否有游戲控制器連接
setInterval(checkGamepads, 100);
一旦檢測到游戲控制器,可以訪問其buttons和axes屬性來讀取按鈕狀態(tài)和軸位置:
if (gamepad.buttons[0].pressed) {
console.log('Button 0 pressed');
}
console.log('Axis 0:', gamepad.axes[0]); // 通常表示左右移動
console.log('Axis 1:', gamepad.axes[1]); // 通常表示上下移動
獲取音頻和視頻
在現(xiàn)代Web開發(fā)中,訪問和處理音頻與視頻流是一項強大的功能,它允許開發(fā)者創(chuàng)建實時通信應(yīng)用、視頻錄制工具、音頻處理應(yīng)用等。MediaDevices.getUserMedia() API 是實現(xiàn)這一功能的關(guān)鍵接口,它允許Web應(yīng)用請求訪問用戶的音頻和/或視頻設(shè)備(如攝像頭和麥克風(fēng)),并獲取實時媒體流。
獲取實時音頻和視頻流
MediaDevices.getUserMedia() 是一個異步函數(shù),它提示用戶允許Web應(yīng)用訪問其音頻和/或視頻設(shè)備。一旦用戶授權(quán),該函數(shù)返回一個 Promise,該 Promise 解析為一個 MediaStream 對象,該對象包含了來自音頻和/或視頻設(shè)備的實時數(shù)據(jù)。
navigator.mediaDevices.getUserMedia({ audio: true, video: true })
.then(function(stream) {
// 使用視頻流,例如將其顯示在video元素上
var video = document.querySelector('video');
video.srcObject = stream;
video.play();
})
.catch(function(err) {
console.error("Error accessing media devices.", err);
});
注意,要使用攝像頭或麥克風(fēng),需要申請權(quán)限。navigator.mediaDevices.getUserMedia() 的第一個參數(shù)是一個對象,用于指定詳細信息和每種媒體類型的要求例如,如果要訪問攝像頭,則第一個參數(shù)應(yīng)為 {video: true}。如需同時使用麥克風(fēng)和攝像頭,就要傳遞 {video: true, audio: true}。
瀏覽器在調(diào)用 navigator.mediaDevices.getUserMedia() 時顯示權(quán)限對話框, 讓用戶能夠選擇授予或拒絕對其攝像頭/麥克風(fēng)的訪問權(quán)限。
控制攝像頭
在訪問用戶攝像頭前,需要獲取用戶的授權(quán)。這里還是調(diào)用navigator.mediaDevices.getUserMedia()方法來實現(xiàn),它會返回一個Promise對象。當用戶同意或拒絕訪問攝像頭時,Promise對象會相應(yīng)地resolve或reject。
const constraints = { video: true, audio: true };
try {
const stream = await navigator.mediaDevices.getUserMedia(constraints);
// 成功獲取到視頻流,可以在這里進行后續(xù)處理
} catch (err) {
// 用戶拒絕或其他錯誤,可以在這里處理錯誤
console.error("Error accessing media devices.", err);
}
成功獲取到視頻流后,可以將其賦值給HTML中的元素的srcObject屬性,并調(diào)用play()方法播放視頻流。
<video id="video" width="640" height="480" autoplay></video>
<script>
const video = document.querySelector('#video');
navigator.mediaDevices.getUserMedia({ video: true, audio: false })
.then(function(stream) {
video.srcObject = stream;
video.play();
})
.catch(function(err) {
console.error("Error accessing media devices.", err);
});
</script>
可以通過MediaStream API提供的getVideoTracks()方法可以獲取到視頻軌道,并通過enabled屬性來打開或關(guān)閉攝像頭。
const tracks = stream.getVideoTracks();
tracks[0].enabled = false; // 關(guān)閉第一條視頻軌道(即攝像頭)
可以使用MediaRecorder API來錄制視頻并保存為文件。
const mediaRecorder = new MediaRecorder(stream);
let chunks = [];
mediaRecorder.ondataavailable = function(event) {
chunks.push(event.data);
};
mediaRecorder.onstop = function() {
const blob = new Blob(chunks, { type: 'video/mp4' });
const videoURL = window.URL.createObjectURL(blob);
// 這里可以將videoURL賦值給某個<video>元素的src屬性來播放錄制的視頻
// 或者將blob對象上傳到服務(wù)器
chunks = []; // 清空chunks數(shù)組,以便下次錄制
};
// 開始錄制
mediaRecorder.start();
// 停止錄制
// mediaRecorder.stop(); // 可以在需要的時候調(diào)用
可以使用Canvas API對當前攝像頭畫面進行截圖。
const canvas = document.createElement('canvas');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
// 將canvas內(nèi)容轉(zhuǎn)換為圖片URL并顯示或下載
const imageURL = canvas.toDataURL('image/png');
// 可以在這里將imageURL賦值給某個<img>元素的src屬性來顯示圖片,或者使用a標簽的download屬性來下載圖片
使用設(shè)備進行打印
當調(diào)用 window.print() 方法時,瀏覽器會顯示一個打印對話框,允許用戶配置打印設(shè)置(如頁面布局、紙張大小、打印份數(shù)等)。
使用設(shè)備進行身份驗證
Web Authentication API 是一個現(xiàn)代、開放標準的 API,旨在通過公鑰加密技術(shù)來簡化用戶認證過程,同時增強安全性。它支持多種身份驗證方式,包括使用藍牙、NFC(近場通信)、USB漫游設(shè)備(如U2F或FIDO2身份驗證器)以及平臺身份驗證器(如內(nèi)置在筆記本電腦或智能手機中的指紋識別器或屏幕鎖定機制)。Authentication 它利用公鑰密碼學(xué),在用戶的設(shè)備上生成密鑰對(公鑰和私鑰),其中公鑰存儲在服務(wù)器上,私鑰則安全地保存在用戶設(shè)備上。
注意:在 Web Authentication API 中,“挑戰(zhàn)”(challenge)指的是一個由服務(wù)器生成的唯一、隨機的數(shù)據(jù)字符串。它有助于防止重放攻擊、增強安全性,并確保請求的真實性和完整性。
注冊過程
- 生成挑戰(zhàn):服務(wù)器生成一個唯一的挑戰(zhàn)(通常是一個隨機字節(jié)數(shù)組),并將其發(fā)送給客戶端。
- 調(diào)用API:客戶端使用navigator.credentials.create()方法開始注冊過程。該方法接受一個包含多個選項的對象作為參數(shù),包括應(yīng)用ID(通常是域名的哈希值)、用戶ID(如用戶名或郵箱地址)、公鑰參數(shù)(指定使用的公鑰算法和類型)以及挑戰(zhàn)等。
- 用戶交互:用戶被提示選擇或插入一個身份驗證器,并按照設(shè)備要求進行身份驗證(如輸入PIN碼、指紋識別等)。
- 生成并返回憑據(jù):身份驗證器生成一個公鑰憑據(jù),并將其與用戶的私鑰一起存儲。公鑰和必要的元數(shù)據(jù)(如認證器類型、公鑰算法等)被封裝在一個響應(yīng)對象中,并返回給客戶端。
- 存儲憑據(jù):客戶端將響應(yīng)對象發(fā)送回服務(wù)器,服務(wù)器提取公鑰并存儲在數(shù)據(jù)庫中,與用戶ID相關(guān)聯(lián)。
認證過程
- 生成挑戰(zhàn):與注冊過程類似,服務(wù)器生成一個新的挑戰(zhàn)。
- 調(diào)用API:客戶端使用navigator.credentials.get()方法開始認證過程。該方法接受一個包含挑戰(zhàn)、應(yīng)用ID以及允許重用的憑據(jù)列表(可選)的對象作為參數(shù)。
- 用戶交互:用戶再次被提示選擇或插入身份驗證器,并按照設(shè)備要求進行身份驗證。
- 生成并返回斷言:身份驗證器使用私鑰簽署挑戰(zhàn),并將簽名連同憑據(jù)ID封裝在一個斷言對象中,返回給客戶端。
- 驗證斷言:客戶端將斷言對象發(fā)送回服務(wù)器,服務(wù)器使用存儲的公鑰驗證簽名,以確認用戶身份。
訪問設(shè)備上的文件
在Web開發(fā)中,處理本地文件的需求很常見。隨著Web技術(shù)的發(fā)展,現(xiàn)代瀏覽器提供了幾種API來處理用戶文件,其中包括File System Access API和較老的File API。
File System Access API
File System Access API 提供了 showOpenFilePicker 和 showSaveFilePicker 方法,分別用于打開文件選擇對話框和保存文件對話框。
async function readFileAndSave() {
try {
// 打開文件選擇對話框
const [fileHandle] = await window.showOpenFilePicker();
// 讀取文件內(nèi)容
const file = await fileHandle.getFile();
const contents = await file.text();
console.log('文件內(nèi)容:', contents);
// 打開保存文件對話框
const saveOptions = {
suggestedName: 'saved-file.txt',
types: [
{
description: 'Text files',
accept: {
'text/plain': ['.txt'],
},
},
],
};
const saveHandle = await window.showSaveFilePicker(saveOptions);
// 寫入文件內(nèi)容
const writableStream = await saveHandle.createWritable();
await writableStream.write(contents);
await writableureStream.close();
console.log('文件已保存');
} catch (error) {
console.error('發(fā)生錯誤:', error);
}
}
File API
如果 File System Access API 不可用,可以使用 File API 來實現(xiàn)類似的功能。File API 提供了 FileReader 對象,用于讀取文件內(nèi)容。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>讀取文件</title>
</head>
<body>
<input type="file" id="fileInput" multiple>
<pre id="fileContent"></pre>
<script>
document.getElementById('fileInput').addEventListener('change', function(event) {
const files = event.target.files;
const fileContentElement = document.getElementById('fileContent');
fileContentElement.textContent = '';
for (const file of files) {
const reader = new FileReader();
reader.onload = function(e) {
fileContentElement.textContent += e.target.result + '\n';
};
reader.readAsText(file);
}
});
</script>
</body>
</html>
在這個例子中,創(chuàng)建了一個文件輸入框,用戶可以通過它選擇本地文件。當用戶選擇文件后,使用 FileReader 對象來讀取文件內(nèi)容,并將其顯示在頁面上。
訪問設(shè)備上的傳感器
通用傳感器 API(Generic Sensor API)是一種允許 Web 應(yīng)用訪問設(shè)備上的傳感器數(shù)據(jù)的標準。這些傳感器包括移動傳感器(如加速度計和陀螺儀)以及環(huán)境傳感器(如環(huán)境光傳感器和磁力計)。這些API使得網(wǎng)頁和應(yīng)用能夠利用設(shè)備的硬件傳感器來提供更豐富的用戶體驗。
通用傳感器 API 的主要組件如下:
- Sensor API:提供了一個通用的接口來訪問各種類型的傳感器數(shù)據(jù)。
- Motion Sensors:包括加速度計(Accelerometer)和陀螺儀(Gyroscope)。
- Orientation Sensors:通常與設(shè)備的方向有關(guān),可以提供關(guān)于設(shè)備方向的詳細信息。
- Environmental Sensors:包括環(huán)境光傳感器和磁力計。
使用加速度計:
if ('Accelerometer' in window) {
const accelerometer = new Accelerometer();
accelerometer.addEventListener('reading', () => {
console.log('Acceleration X:', accelerometer.x);
console.log('Acceleration Y:', accelerometer.y);
console.log('Acceleration Z:', accelerometer.z);
});
accelerometer.start();
// 停止傳感器
// accelerometer.stop();
} else {
console.log('Accelerometer is not supported on this device.');
}
使用環(huán)境光傳感器:
if ('AmbientLightSensor' in window) {
const ambientLightSensor = new AmbientLightSensor();
ambientLightSensor.addEventListener('reading', () => {
console.log('Ambient Light Level:', ambientLightSensor.illuminance);
});
ambientLightSensor.start();
} else {
console.log('AmbientLightSensor is not supported on this device.');
}
DeviceMotion和DeviceOrientation
如果通用傳感器API不可用,可以使用DeviceMotion和DeviceOrientation事件來訪問設(shè)備的加速度計、陀螺儀和羅盤數(shù)據(jù)。
使用DeviceMotion事件獲取加速度計數(shù)據(jù):
window.addEventListener('devicemotion', (event) => {
console.log('Acceleration X:', event.acceleration.x);
console.log('Acceleration Y:', event.acceleration.y);
console.log('Acceleration Z:', event.acceleration.z);
console.log('Acceleration Including Gravity X:', event.accelerationIncludingGravity.x);
console.log('Acceleration Including Gravity Y:', event.accelerationIncludingGravity.y);
console.log('Acceleration Including Gravity Z:', event.accelerationIncludingGravity.z);
});
使用DeviceOrientation事件獲取陀螺儀和羅盤數(shù)據(jù):
window.addEventListener('deviceorientation', (event) => {
console.log('Alpha:', event.alpha); // 繞Z軸旋轉(zhuǎn)(羅盤方向)
console.log('Beta:', event.beta); // 繞X軸旋轉(zhuǎn)(前后傾斜)
console.log('Gamma:', event.gamma); // 繞Y軸旋轉(zhuǎn)(左右傾斜)
});
訪問 GPS 坐標
在 web 上可以使用 Geolocation API 獲取設(shè)備的當前地理位置(通常是經(jīng)緯度坐標)。
首先,網(wǎng)頁需要請求用戶的權(quán)限來獲取其地理位置。這是因為地理位置信息屬于用戶的隱私數(shù)據(jù),必須得到用戶的明確同意。一旦用戶授予權(quán)限,可以使用navigator.geolocation.getCurrentPosition()方法來獲取當前位置。這個方法接受兩個回調(diào)函數(shù)作為參數(shù):成功回調(diào)和失敗回調(diào)。
- 成功回調(diào):當成功獲取到位置信息時調(diào)用,接收一個位置對象作為參數(shù)。
- 失敗回調(diào):當獲取位置信息失敗時調(diào)用,接收一個錯誤對象作為參數(shù)。
// 請求用戶位置
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(successCallback, errorCallback);
} else {
console.log("Geolocation is not supported by this browser.");
}
// 成功回調(diào)
function successCallback(position) {
const latitude = position.coords.latitude;
const longitude = position.coords.longitude;
console.log(`Latitude: ${latitude}, Longitude: ${longitude}`);
}
// 失敗回調(diào)
function errorCallback(error) {
switch(error.code) {
case error.PERMISSION_DENIED:
console.log("User denied the request for Geolocation.");
break;
case error.POSITION_UNAVAILABLE:
console.log("Location information is unavailable.");
break;
case error.TIMEOUT:
console.log("The request to get user location timed out.");
break;
case error.UNKNOWN_ERROR:
console.log("An unknown error occurred.");
break;
}
}
檢查設(shè)備電池電量
使用Battery API確實可以獲取關(guān)于電池電量的信息,并且在電池電量或充電狀態(tài)發(fā)生變化時收到通知。
獲取電量信息
- 檢測瀏覽器支持:
- 在使用Battery API之前,開發(fā)者需要先檢測用戶所使用的瀏覽器是否支持該API。
- 可以通過if ('getBattery' in navigator)來判斷瀏覽器是否支持Battery API。
- 獲取電池對象:
- 如果瀏覽器支持Battery API,可以通過調(diào)用navigator.getBattery()方法來獲取一個Promise對象。
- 這個Promise對象在成功時會傳遞一個BatteryManager對象,該對象包含了電池的各種信息。
- 讀取電池信息:
- battery.level:表示當前電池的電量,范圍從0到1,可以通過乘以100來轉(zhuǎn)換為百分比形式。
- battery.charging:一個布爾值,表示設(shè)備當前是否在充電。
- battery.chargingTime:表示設(shè)備預(yù)計還需要多長時間充滿,單位為秒(如果設(shè)備已經(jīng)在充電且即將充滿,則可能為0或接近0)。
- battery.dischargingTime:表示設(shè)備預(yù)計還能使用多長時間,單位為秒(如果設(shè)備正在放電且電量即將耗盡,則可能為一個較大的值或Infinity)。
- 一旦獲取到BatteryManager對象,開發(fā)者就可以通過該對象的屬性和事件來獲取電池信息。
- 主要的屬性包括:
電池、電量狀態(tài)通知
- 監(jiān)聽電池狀態(tài)變化:
- 為了更加精準地捕捉和響應(yīng)用戶設(shè)備的電池狀態(tài)動態(tài),開發(fā)者需要實時監(jiān)聽電池狀態(tài)的變化。
- 可以通過為BatteryManager對象注冊事件監(jiān)聽器來實現(xiàn)這一功能。
- 事件類型:
- levelchange:當設(shè)備電量發(fā)生變化時觸發(fā)。
- chargingchange:當設(shè)備的充電狀態(tài)發(fā)生變化時觸發(fā)(例如,從充電狀態(tài)變?yōu)槲闯潆姞顟B(tài),或反之)。
- chargingtimechange:當設(shè)備充滿電所需時間發(fā)生變化時觸發(fā)(這通常發(fā)生在充電速率改變時)。
- dischargingtimechange:當設(shè)備放空電所需時間發(fā)生變化時觸發(fā)(這通常發(fā)生在電池使用量或放電速率改變時)。
- 實現(xiàn)方式:
- 使用battery.addEventListener()方法來為上述事件類型注冊監(jiān)聽器。
- 在監(jiān)聽器的回調(diào)函數(shù)中,可以更新UI或執(zhí)行其他邏輯來響應(yīng)電池狀態(tài)的變化。
以下是一個簡單的例子:
if ('getBattery' in navigator) {
navigator.getBattery().then(function(battery) {
// 初始獲取電池信息
console.log('當前電池電量: ' + battery.level * 100 + '%');
console.log('充電狀態(tài): ' + (battery.charging ? '正在充電' : '未充電'));
console.log('充滿電所需時間: ' + battery.chargingTime + '秒');
console.log('防空電所需時間: ' + battery.dischargingTime + '秒');
// 監(jiān)聽電池狀態(tài)變化
battery.addEventListener('levelchange', function() {
console.log('電量已更新: 當前電量為' + (battery.level * 100) + '%');
});
battery.addEventListener('chargingchange', function() {
console.log('充電狀態(tài)已變更: ' + (battery.charging ? '正在充電' : '未在充電'));
});
battery.addEventListener('chargingtimechange', function() {
console.log('充滿電所需時間更新: 剩余' + battery.chargingTime + '秒');
});
battery.addEventListener('dischargingtimechange', function() {
console.log('放空電所需時間更新: 剩余' + battery.dischargingTime + '秒');
});
}).catch(function(error) {
// 處理獲取電池對象失敗的情況
console.error('獲取電池信息失敗:', error);
});
} else {
// 處理瀏覽器不支持Battery API的情況
console.log('當前瀏覽器不支持Battery API');
}
本地網(wǎng)絡(luò)中的多媒體播放控制
Remote Playback API
Remote Playback API 允許網(wǎng)頁應(yīng)用將音頻和視頻內(nèi)容發(fā)送到遠程播放設(shè)備(如智能電視、無線音箱等)進行播放。這對于構(gòu)建家庭娛樂系統(tǒng)或跨設(shè)備媒體共享功能特別有用。
工作原理:
- 檢測支持:首先,應(yīng)用需要檢測瀏覽器是否支持 Remote Playback API。
- 獲取可用設(shè)備:通過調(diào)用 navigator.mediaDevices.enumerateDevices() 并過濾出支持 Remote Playback 的設(shè)備,可以獲取到當前網(wǎng)絡(luò)中的可用設(shè)備列表。但請注意,Remote Playback API 并不直接提供設(shè)備枚舉功能;通常,設(shè)備發(fā)現(xiàn)是通過其他機制(如 UPnP、DLNA 等)或用戶選擇來實現(xiàn)的。
- 建立連接:一旦選擇了目標設(shè)備,應(yīng)用就可以通過 navigator.remotePlayback.requestRemotePlayback() 方法嘗試與該設(shè)備建立連接。
- 發(fā)送媒體:連接建立后,應(yīng)用可以開始將媒體內(nèi)容發(fā)送到遠程設(shè)備進行播放。
假設(shè)有一個網(wǎng)頁應(yīng)用,它允許用戶選擇并播放存儲在本地服務(wù)器上的視頻文件。該應(yīng)用可以使用 Remote Playback API 將所選視頻發(fā)送到用戶的智能電視進行播放。
// 偽代碼示例,具體實現(xiàn)可能因設(shè)備和瀏覽器而異
if ('remotePlayback' in navigator.mediaDevices) {
// 假設(shè)已經(jīng)通過某種方式選擇了目標設(shè)備
const remoteDevice = selectedDevice;
navigator.mediaDevices.requestRemotePlayback(remoteDevice)
.then(remote => {
// 成功建立連接,開始播放媒體
const mediaElement = document.querySelector('video'); // 假設(shè)頁面上有一個 <video> 元素
remote.loadVideo(URL.createObjectURL(mediaElement.currentSrc));
remote.play();
})
.catch(error => {
// 處理連接失敗的情況
console.error('Remote playback failed:', error);
});
} else {
console.log('Remote Playback API is not supported in this browser.');
}
Presentation API
Presentation API 則用于在第二屏幕(如輔助屏幕、HDMI 連接的顯示屏、無線連接的智能電視等)上呈現(xiàn)網(wǎng)頁內(nèi)容。它允許開發(fā)者將網(wǎng)頁的一部分或全部內(nèi)容投影到另一個屏幕上,這對于會議演示、家庭娛樂等場景非常有用。
工作原理:
- 檢測支持:首先,應(yīng)用需要檢測瀏覽器是否支持 Presentation API。
- 獲取可用屏幕:通過調(diào)用 navigator.presentation.getAvailability() 和 navigator.presentation.requestSession() 方法,應(yīng)用可以獲取到當前可用的第二屏幕列表,并請求與其中一個屏幕建立會話。
- 建立連接并發(fā)送內(nèi)容:一旦會話建立,應(yīng)用就可以開始將內(nèi)容發(fā)送到第二屏幕進行顯示。
假設(shè)有一個網(wǎng)頁應(yīng)用,它允許用戶將當前頁面或某個特定部分的內(nèi)容投影到連接在同一本地網(wǎng)絡(luò)上的智能電視上進行顯示。
if ('presentation' in navigator) {
navigator.presentation.getAvailability().then(availability => {
if (availability === 'available') {
// 請求與第二屏幕建立會話
navigator.presentation.requestSession().then(presentationSession => {
// 會話建立成功,開始發(fā)送內(nèi)容
// 假設(shè)我們有一個要投影的 <div> 元素
const contentToPresent = document.querySelector('#content-to-present');
// 創(chuàng)建一個新的 HTML 文檔,并將要投影的內(nèi)容添加到其中
const presentationDocument = presentationSession.urls[0]; // 獲取用于投影的 URL
const iframe = document.createElement('iframe');
iframe.src = presentationDocument;
iframe.onload = () => {
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
iframeDoc.open();
iframeDoc.write(`
<!DOCTYPE html>
<html>
<head><title>Presentation</title></head>
<body>${contentToPresent.innerHTML}</body>
</html>
`);
iframeDoc.close();
};
// 將 iframe 添加到當前頁面中(雖然實際上它不會在當前頁面顯示)
// 這是為了觸發(fā) onload 事件并加載投影內(nèi)容
document.body.appendChild(iframe);
// 稍后可以從 DOM 中移除 iframe,因為它不再需要
// document.body.removeChild(iframe);
}).catch(error => {
// 處理請求會話失敗的情況
console.error('Presentation session request failed:', error);
});
} else {
console.log('No second screens available.');
}
}).catch(error => {
// 處理獲取可用性失敗的情況
console.error('Presentation API availability check failed:', error);
});
} else {
console.log('Presentation API is not supported in this browser.');
}