Skip to content

本文由 简悦 SimpRead 转码, 原文地址 mp.weixin.qq.com

随着技术的发展,前端技术与硬件设备的交互模式正变得日益丰富多样。从基础的点击、滑动操作,进化到高级的传感器数据捕获及设备远程操控,前端技术正持续跨越传统的交互壁垒,为用户塑造出更为便捷且智能的使用体验。本文就来对前端与硬件交互多种方式进行全面盘点。

接收设备的输入

在现代 Web 开发中,处理用户输入事件是构建交互式应用的关键部分。这些输入事件可以来自多种设备,包括键盘、鼠标(或触控板)、触摸屏以及游戏控制器等。

键盘事件

键盘事件是最常见的用户输入方式之一,包括按键按下(keydown)、按键释放(keyup)等事件。

  • keydown:当按键被按下时触发。

  • keyup:当按键被释放时触发。

监听键盘事件的基本方法是使用 addEventListener 方法:

document.addEventListener('keydown', function(event) {      // 使用event.key来获取按键的值      console.log('Key down:', event.key);  });

Pointer 事件

Pointer 事件是一个统一的指针输入模型,旨在处理所有类型的指针输入,包括鼠标、触控笔和触摸。Pointer 事件包括 pointerdown、pointerup、pointermove 等。

  • pointerdown:当指针按下时触发。

  • pointerup:当指针释放时触发。

  • pointermove:当指针移动时触发。

监听 Pointer 事件的例子:

document.addEventListener('keydown', function(event) {      // 使用event.key来获取按键的值      console.log('Key down:', event.key);  });

还可以直接使用鼠标事件,常见的鼠标事件包括:

  • click:单击鼠标左键时触发。

  • dblclick:双击鼠标左键时触发。

  • mousedown:鼠标按钮被按下时触发。

  • mouseup:鼠标按钮被松开时触发。

  • mousemove:鼠标在元素上移动时触发。

  • mouseenter:鼠标进入元素时触发。

  • mouseleave:鼠标离开元素时触发。

  • contextmenu:用户右键点击时触发。

  • wheel:鼠标滚轮滚动时触发。

  • auxclick:辅助键(如中键或右键)被点击时触发。

例子:

document.addEventListener('pointerdown', function(event) {    console.log('Pointer down at:', event.clientX, event.clientY);  });

mousedown 事件对象的 button 属性表示被按下的鼠标按钮。这个属性的值如下:

  • 0:表示主按钮(通常是左键)。

  • 1:表示中间按钮(通常是滚轮按钮,但不是所有鼠标都有)。

  • 2:表示次按钮(通常是右键)。

游戏控制器

Gamepad API 允许 Web 应用检测并响应来自游戏控制器的输入,包括按钮按压和轴移动。这对于开发需要精确控制的游戏或应用非常有用。

使用 Gamepad API,首先需要检查是否有连接的游戏控制器:

document.addEventListener('pointerdown', function(event) {    console.log('Pointer down at:', event.clientX, event.clientY);  });

一旦检测到游戏控制器,可以访问其 buttons 和 axes 属性来读取按钮状态和轴位置:

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();    }  });

获取音频和视频

在现代 Web 开发中,访问和处理音频与视频流是一项强大的功能,它允许开发者创建实时通信应用、视频录制工具、音频处理应用等。MediaDevices.getUserMedia() API 是实现这一功能的关键接口,它允许 Web 应用请求访问用户的音频和 / 或视频设备(如摄像头和麦克风),并获取实时媒体流。

获取实时音频和视频流

MediaDevices.getUserMedia() 是一个异步函数,它提示用户允许 Web 应用访问其音频和 / 或视频设备。一旦用户授权,该函数返回一个 Promise,该 Promise 解析为一个 MediaStream 对象,该对象包含了来自音频和 / 或视频设备的实时数据。

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();    }  });

注意,要使用摄像头或麦克风,需要申请权限。navigator.mediaDevices.getUserMedia() 的第一个参数是一个对象,用于指定详细信息和每种媒体类型的要求例如,如果要访问摄像头,则第一个参数应为 {video: true}。如需同时使用麦克风和摄像头,就要传递 {video: true, audio: true}。

浏览器在调用 navigator.mediaDevices.getUserMedia() 时显示权限对话框, 让用户能够选择授予或拒绝对其摄像头 / 麦克风的访问权限。

控制摄像头

在访问用户摄像头前,需要获取用户的授权。这里还是调用 navigator.mediaDevices.getUserMedia() 方法来实现,它会返回一个 Promise 对象。当用户同意或拒绝访问摄像头时,Promise 对象会相应地 resolve 或 reject。

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);

成功获取到视频流后,可以将其赋值给 HTML 中的元素的 srcObject 属性,并调用 play() 方法播放视频流。

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);

可以通过 MediaStream API 提供的 getVideoTracks() 方法可以获取到视频轨道,并通过 enabled 属性来打开或关闭摄像头。

if (gamepad.buttons[0].pressed) {      console.log('Button 0 pressed');  }    console.log('Axis 0:', gamepad.axes[0]); // 通常表示左右移动  console.log('Axis 1:', gamepad.axes[1]); // 通常表示上下移动

可以使用 MediaRecorder API 来录制视频并保存为文件。

if (gamepad.buttons[0].pressed) {      console.log('Button 0 pressed');  }    console.log('Axis 0:', gamepad.axes[0]); // 通常表示左右移动  console.log('Axis 1:', gamepad.axes[1]); // 通常表示上下移动

可以使用 Canvas API 对当前摄像头画面进行截图。

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);    });

使用设备进行打印

当调用 window.print() 方法时,浏览器会显示一个打印对话框,允许用户配置打印设置(如页面布局、纸张大小、打印份数等)。

使用设备进行身份验证

Web Authentication API 是一个现代、开放标准的 API,旨在通过公钥加密技术来简化用户认证过程,同时增强安全性。它支持多种身份验证方式,包括使用蓝牙、NFC(近场通信)、USB 漫游设备(如 U2F 或 FIDO2 身份验证器)以及平台身份验证器(如内置在笔记本电脑或智能手机中的指纹识别器或屏幕锁定机制)。Authentication 它利用公钥密码学,在用户的设备上生成密钥对(公钥和私钥),其中公钥存储在服务器上,私钥则安全地保存在用户设备上。

注意:在 Web Authentication API 中,“挑战”(challenge)指的是一个由服务器生成的唯一、随机的数据字符串。它有助于防止重放攻击、增强安全性,并确保请求的真实性和完整性。

注册过程

  1. 生成挑战:服务器生成一个唯一的挑战(通常是一个随机字节数组),并将其发送给客户端。

  2. 调用 API:客户端使用 navigator.credentials.create() 方法开始注册过程。该方法接受一个包含多个选项的对象作为参数,包括应用 ID(通常是域名的哈希值)、用户 ID(如用户名或邮箱地址)、公钥参数(指定使用的公钥算法和类型)以及挑战等。

  3. 用户交互:用户被提示选择或插入一个身份验证器,并按照设备要求进行身份验证(如输入 PIN 码、指纹识别等)。

  4. 生成并返回凭据:身份验证器生成一个公钥凭据,并将其与用户的私钥一起存储。公钥和必要的元数据(如认证器类型、公钥算法等)被封装在一个响应对象中,并返回给客户端。

  5. 存储凭据:客户端将响应对象发送回服务器,服务器提取公钥并存储在数据库中,与用户 ID 相关联。

认证过程

  1. 生成挑战:与注册过程类似,服务器生成一个新的挑战。

  2. 调用 API:客户端使用 navigator.credentials.get() 方法开始认证过程。该方法接受一个包含挑战、应用 ID 以及允许重用的凭据列表(可选)的对象作为参数。

  3. 用户交互:用户再次被提示选择或插入身份验证器,并按照设备要求进行身份验证。

  4. 生成并返回断言:身份验证器使用私钥签署挑战,并将签名连同凭据 ID 封装在一个断言对象中,返回给客户端。

  5. 验证断言:客户端将断言对象发送回服务器,服务器使用存储的公钥验证签名,以确认用户身份。

访问设备上的文件

在 Web 开发中,处理本地文件的需求很常见。随着 Web 技术的发展,现代浏览器提供了几种 API 来处理用户文件,其中包括 File System Access API 和较老的 File API。

File System Access API

File System Access API 提供了 showOpenFilePicker 和 showSaveFilePicker 方法,分别用于打开文件选择对话框和保存文件对话框。

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);    });

File API

如果 File System Access API 不可用,可以使用 File API 来实现类似的功能。File API 提供了 FileReader 对象,用于读取文件内容。

const constraints = { video: true, audio: true };  try {    const stream = await navigator.mediaDevices.getUserMedia(constraints);    // 成功获取到视频流,可以在这里进行后续处理  } catch (err) {    // 用户拒绝或其他错误,可以在这里处理错误    console.error("Error accessing media devices.", err);  }

在这个例子中,创建了一个文件输入框,用户可以通过它选择本地文件。当用户选择文件后,使用 FileReader 对象来读取文件内容,并将其显示在页面上。

访问设备上的传感器

通用传感器 API(Generic Sensor API)是一种允许 Web 应用访问设备上的传感器数据的标准。这些传感器包括移动传感器(如加速度计和陀螺仪)以及环境传感器(如环境光传感器和磁力计)。这些 API 使得网页和应用能够利用设备的硬件传感器来提供更丰富的用户体验。

通用传感器 API 的主要组件如下:

  • Sensor API:提供了一个通用的接口来访问各种类型的传感器数据。

  • Motion Sensors:包括加速度计(Accelerometer)和陀螺仪(Gyroscope)。

  • Orientation Sensors:通常与设备的方向有关,可以提供关于设备方向的详细信息。

  • Environmental Sensors:包括环境光传感器和磁力计。

使用加速度计:

const constraints = { video: true, audio: true };  try {    const stream = await navigator.mediaDevices.getUserMedia(constraints);    // 成功获取到视频流,可以在这里进行后续处理  } catch (err) {    // 用户拒绝或其他错误,可以在这里处理错误    console.error("Error accessing media devices.", err);  }

使用环境光传感器:

<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>

DeviceMotion 和 DeviceOrientation

如果通用传感器 API 不可用,可以使用 DeviceMotion 和 DeviceOrientation 事件来访问设备的加速度计、陀螺仪和罗盘数据。

使用 DeviceMotion 事件获取加速度计数据:

<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>

使用 DeviceOrientation 事件获取陀螺仪和罗盘数据:

const tracks = stream.getVideoTracks();  tracks[0].enabled = false; // 关闭第一条视频轨道(即摄像头)

访问 GPS 坐标

在 web 上可以使用 Geolocation API 获取设备的当前地理位置(通常是经纬度坐标)。

首先,网页需要请求用户的权限来获取其地理位置。这是因为地理位置信息属于用户的隐私数据,必须得到用户的明确同意。一旦用户授予权限,可以使用 navigator.geolocation.getCurrentPosition() 方法来获取当前位置。这个方法接受两个回调函数作为参数:成功回调和失败回调。

  • 成功回调:当成功获取到位置信息时调用,接收一个位置对象作为参数。

  • 失败回调:当获取位置信息失败时调用,接收一个错误对象作为参数。

const tracks = stream.getVideoTracks();  tracks[0].enabled = false; // 关闭第一条视频轨道(即摄像头)

检查设备电池电量

使用 Battery API 确实可以获取关于电池电量的信息,并且在电池电量或充电状态发生变化时收到通知。

获取电量信息

  1. 检测浏览器支持
  • 在使用 Battery API 之前,开发者需要先检测用户所使用的浏览器是否支持该 API。

  • 可以通过 if ('getBattery' in navigator) 来判断浏览器是否支持 Battery API。

  1. 获取电池对象
  • 如果浏览器支持 Battery API,可以通过调用 navigator.getBattery() 方法来获取一个 Promise 对象。

  • 这个 Promise 对象在成功时会传递一个 BatteryManager 对象,该对象包含了电池的各种信息。

  1. 读取电池信息
  • battery.level:表示当前电池的电量,范围从 0 到 1,可以通过乘以 100 来转换为百分比形式。

  • battery.charging:一个布尔值,表示设备当前是否在充电。

  • battery.chargingTime:表示设备预计还需要多长时间充满,单位为秒(如果设备已经在充电且即将充满,则可能为 0 或接近 0)。

  • battery.dischargingTime:表示设备预计还能使用多长时间,单位为秒(如果设备正在放电且电量即将耗尽,则可能为一个较大的值或 Infinity)。

  • 一旦获取到 BatteryManager 对象,开发者就可以通过该对象的属性和事件来获取电池信息。

  • 主要的属性包括:

电池、电量状态通知

  1. 监听电池状态变化
  • 为了更加精准地捕捉和响应用户设备的电池状态动态,开发者需要实时监听电池状态的变化。

  • 可以通过为 BatteryManager 对象注册事件监听器来实现这一功能。

  1. 事件类型
  • levelchange:当设备电量发生变化时触发。

  • chargingchange:当设备的充电状态发生变化时触发(例如,从充电状态变为未充电状态,或反之)。

  • chargingtimechange:当设备充满电所需时间发生变化时触发(这通常发生在充电速率改变时)。

  • dischargingtimechange:当设备放空电所需时间发生变化时触发(这通常发生在电池使用量或放电速率改变时)。

  1. 实现方式
  • 使用 battery.addEventListener() 方法来为上述事件类型注册监听器。

  • 在监听器的回调函数中,可以更新 UI 或执行其他逻辑来响应电池状态的变化。

以下是一个简单的例子:

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对象上传到服务器    chunks = []; // 清空chunks数组,以便下次录制  };    // 开始录制  mediaRecorder.start();    // 停止录制  // mediaRecorder.stop(); // 可以在需要的时候调用

本地网络中的多媒体播放控制

Remote Playback API

Remote Playback API 允许网页应用将音频和视频内容发送到远程播放设备(如智能电视、无线音箱等)进行播放。这对于构建家庭娱乐系统或跨设备媒体共享功能特别有用。

工作原理

  1. 检测支持:首先,应用需要检测浏览器是否支持 Remote Playback API。

  2. 获取可用设备:通过调用 navigator.mediaDevices.enumerateDevices() 并过滤出支持 Remote Playback 的设备,可以获取到当前网络中的可用设备列表。但请注意,Remote Playback API 并不直接提供设备枚举功能;通常,设备发现是通过其他机制(如 UPnP、DLNA 等)或用户选择来实现的。

  3. 建立连接:一旦选择了目标设备,应用就可以通过 navigator.remotePlayback.requestRemotePlayback() 方法尝试与该设备建立连接。

  4. 发送媒体:连接建立后,应用可以开始将媒体内容发送到远程设备进行播放。

假设有一个网页应用,它允许用户选择并播放存储在本地服务器上的视频文件。该应用可以使用 Remote Playback 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对象上传到服务器    chunks = []; // 清空chunks数组,以便下次录制  };    // 开始录制  mediaRecorder.start();    // 停止录制  // mediaRecorder.stop(); // 可以在需要的时候调用

Presentation API

Presentation API 则用于在第二屏幕(如辅助屏幕、HDMI 连接的显示屏、无线连接的智能电视等)上呈现网页内容。它允许开发者将网页的一部分或全部内容投影到另一个屏幕上,这对于会议演示、家庭娱乐等场景非常有用。

工作原理

  1. 检测支持:首先,应用需要检测浏览器是否支持 Presentation API。

  2. 获取可用屏幕:通过调用 navigator.presentation.getAvailability() 和 navigator.presentation.requestSession() 方法,应用可以获取到当前可用的第二屏幕列表,并请求与其中一个屏幕建立会话。

  3. 建立连接并发送内容:一旦会话建立,应用就可以开始将内容发送到第二屏幕进行显示。

假设有一个网页应用,它允许用户将当前页面或某个特定部分的内容投影到连接在同一本地网络上的智能电视上进行显示。

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内容转换为图片URL并显示或下载  const imageURL = canvas.toDataURL('image/png');  // 可以在这里将imageURL赋值给某个<img>元素的src属性来显示图片,或者使用a标签的download属性来下载图片