Storage Access API

Storage Access API 允许第三方上下文中跨站内容(如嵌入 <iframe> 标签中)获取对未分区 cookie (即以传统方式存储的 cookie,仅允许在第一方上下文(直接加载在浏览器选项卡中)中访问)的访问权限

通常出于隐私性考虑,默认用户代理会阻止第三方上下文中跨站内容对未分区 cookie 的访问,但一些场景下,开发者期望启用第三方内容对未分区 cookie 访问的功能

Document 接口的 requestStorageAccess() 方法用于请求文档访问第三方 Cookie 权限

方法返回一个 Promise

方法可能抛出 InvalidStateError 异常,若当前文档尚未进入 active 状态

方法可能抛出 NotAllowedError 异常,若当前文档未处于安全上下文状态,或受权限策略阻止,或当前文档或其顶层文档的 origin 为空,或受 <iframe> 标签沙箱策略的限制,或请求权限被拒绝(如之前已被拒绝,或当前未处于用户激活状态且未授予权限)

1
2
3
4
5
document.requestStorageAccess().then(() => {
console.log('granted')
}).catch(() => {
console.log('denied')
})

Document 接口的 hasStorageAccess() 方法用于检测文档访问第三方 Cookie 权限

方法返回一个 Promise 的 boolean,表示是否已允许文档访问第三方 Cookie

方法可能抛出 InvalidStateError 异常,若当前文档尚未进入 active 状态

1
2
3
4
5
6
7
document.hasStorageAccess().then((sym) => {
if (sym) {
console.log('has access')
} else {
console.log('not has access')
}
})

沙箱策略

该 API 在 <iframe> 标签中的调用受到 allow-storage-access-by-user-activation 沙箱策略的控制,需要将 sandbox 的属性指定为允许

(同时为使用该 API,也需要指定 allow-scriptsallow-same-origin 沙箱策略)

权限策略

该 API 调用受到 storage-access 权限策略的控制,可以通过 Permissions-Policy 响应头指定,或通过 <iframe> 标签的 allow 属性指定

默认值是 *,即允许任意源的浏览上下文使用该 API

权限 API

该 API 调用需要用户授予 storage-access 权限,可以调用 Permission.query() 方法检查用户是否已授予了该权限

类型

1
2
3
4
interface Document {
hasStorageAccess(): Promise<boolean>
requestStorageAccess(): Promise<undefined>
}

链接

Storage API

Storage API 提供了多个存储 API 的共享的管理方法,允许获取存储的信息并控制存储的清除策略

通常管理的存储包括包括 IndexedDB 、 Cache Storage 、 ServiceWorker 脚本 、 Origin Private File System

该 API 通过 StorageManager 接口提供相关方法,并通过 navigator.storage 对开发者暴露

获取存储信息

StorageManager 接口的 estimate() 方法用于获取用户代理存储的信息

方法返回一个 Promise 的对象,其 usage 属性返回一个数字,代表当前网页使用的存储的大小;quota 属性返回一个数字,代表当前用户代理能为网页提供的存储的大小

1
2
3
4
const storage = await navigator.storage.estimate()

console.log('usage', storage.usage)
console.log('quota', storage.quota)

设置存储清除策略

默认存储策略为 "best-effort",即用户代理会尽可能地保存存储数据,但若需要清除时不会告知用户

而存储策略 "persistent",即用户代理会尽可能地保存存储数据,优先清除存储策略设置为 "best-effort" 的存储,但若需要清除时会主动告知用户

StorageManager 接口的 persisted() 方法检测是否已将存储策略更改为 "persistent",返回一个 Promise 的 boolean

StorageManager 接口的 persist() 方法用于将存储策略更改为 "persistent",返回一个 Promise 的 boolean

1
2
3
4
5
6
7
const result = await navigator.storage.persist()

if (result) {
console.log('persisted')
} else {
console.log('not persisted')
}

权限 API

该 API 调用需要用户 persistent-storage 授予权限,可以调用 Permission.query() 方法检查用户是否已授予了该权限

类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
interface NavigatorStorage {
readonly storage: StorageManager
}

interface Navigator extends NavigatorStorage {}

interface WorkerNavigator extends NavigatorStorage {}

interface StorageManager {
persisted(): Promise<boolean>
persist(): Promise<boolean>

estimate(): Promise<StorageEstimate>
}

interface StorageEstimate {
usage: number
quota: number
}

链接

Permissions API

Permissions API 提供了编程式地检测当前浏览上下文 API 权限

可以用于确定对应的 API 是否已授予权限或被拒绝授予权限,或等待授权权限

同时 API 还会考虑其他因素,如若该 API 对 Secure Context 的要求、Permissions-Policy 的限制等待

该 API 通过 Permissions 接口提供相关功能,并通过 Navigator.permissionsWorkerNavigator.permissions 对外暴露 Permissions 实例

检测权限

调用 Permissions 接口的 query() 方法检测给定 API 的权限状态

方法传入一个 permissionDescriptor 的配置项,其唯一参数 name 反映与给定 API 相关的名称

方法返回一个 Promise 的 PermissionStatus,代表与给定 API 相关的权限状态

1
2
3
4
5
const status = await navigator.permissions.query({
name: 'geolocation',
})

console.log(status.state) // 'granted' 'denied' 'prompt'

PermissionStatusstate 属性返回一个 string,代表权限的状态,为 'granted' 'denied' 'prompt' 之一

PermissionStatusname 属性返回一个 string,代表权限的名称,与 query() 方法传入的 name 参数一致

PermissionStatuschange 事件在权限的状态改变时触发,返回一个 Event

权限名称列表

Permission name Permission description
accelerometer Sensor APIs
ambient-light-sensor Sensor APIs
background-sync Background Synchronization API
camera Media Capture and Streams API
clipboard-read Clipboard API
clipboard-write Clipboard API
geolocation Geolocation API
gyroscope Sensor APIs
local-fonts Local Font Access API
magnetometer Sensor APIs
microphone Media Capture and Streams API
midi Web MIDI API
notifications Notifications API
payment-handler Payment Handler API
persistent-storage Storage API
push Push API
speaker-selection Web Audio Output Devices API
storage-access Storage Access API
window-management Window Management API

不同浏览器支持的权限名称参考如下

类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
interface Navigator {
readonly permissions: Permissions
}

partial interface WorkerNavigator {
readonly permissions: Permissions
}

interface Permissions {
query(permissionDesc: PermissionDescriptor): Promise<PermissionStatus>
}

interface PermissionDescriptor {
name: string
}

interface PermissionStatus extends EventTarget {
readonly state: PermissionState
readonly name: string

onchange: ((this: PermissionStatus, ev: Event) => any) | null
}

enum PermissionState {
"granted",
"denied",
"prompt",
}

链接

Beacon API

Beacon API 可用于向服务器发送 HTTP POST 网络请求

通常目的旨在向服务器发送用户数据,特别是在页面关闭时机(能够避免阻碍下一网页的加载)

使用

使用 Navigator 接口的 sendBeacon() 方法发送数据

方法需要传递一个 stringURL,代表请求的目标 URL

方法可以可选地携带一个 ReadableStream Blob ArrayBuffer TypedArray DataView FormData URLSearchParams string,代表请求需要携带的数据

方法返回一个 boolean,表示是否成功完成数据转换

避免使用 beforeunload unload 事件,而是 visibilitychange 事件(或在不兼容时使用 pagehide 事件),因为在移动端网页时卸载事件不能确定地触发

类型

1
2
3
interface Navigator {
sendBeacon(url: string, data?: BodyInit): boolean
}

链接

Window Management API

Window Management API 允许获取连接到设备的显示器的详细信息,并将窗口放置在指定的屏幕上

检测是否多屏

Screen 接口的 isExtended 属性指示当前用户设备是否支持多屏

window-management 权限策略禁止时,始终返回 false

1
screen.isExtended

监测屏幕变化

Screen 接口的 change 事件在屏幕的参数变化时触发,返回一个 Event 事件,针对 width height availWidth availHeight colorDepth orientation 属性

ScreenDetailed 接口的 change 事件亦支持 left top availLeft availTop devicePixelRatio label isPrimary isInternal 属性

1
2
3
screen.addEventListener('change', (e) => {
//
})

获取屏幕信息

调用 Window 接口的 getScreenDetails() 方法获取用户端可用的所有

返回一个 Promise 的 ScreenDetails 实例

window-management 权限策略禁止或明确被用户拒绝授予 window-management 权限时,会抛出 NotAllowedError 异常

1
const screenDetails = await window.getScreenDetails()

ScreenDetails 接口反映了所有设备可用的屏幕的信息,该接口继承自 EventTarget 接口

ScreenDetails 接口的 currentScreen 属性返回一个 ScreenDetailed 实例,代表当前浏览器窗口所在的屏幕

ScreenDetails 接口的 screens 属性返回一个 ScreenDetailed 实例数组,代表当前设备可用的屏幕

1
2
const screens = screenDetails.screens
const current = screenDetails.currentScreen

ScreenDetails 接口的 currentscreenchange 事件在浏览器窗口移动至其他屏幕或当前屏幕的任意属性改变时触发,返回一个 Event 事件

ScreenDetails 接口的 screenschange 事件在当前设备可用的屏幕变化时触发,返回一个 Event 事件

1
2
3
4
5
6
screenDetails.addEventListener('currentscreenchange', () => {
current = screenDetails.currentScreen
})
screenDetails.addEventListener('screenschange', () => {
screens = screenDetails.screens
})

ScreenDetailed 接口反映了单个设备可用的屏幕的信息,该接口继承自 Screen 接口

ScreenDetailed 接口的 availLeft 属性返回一个 number,代表可用屏幕区域的 x 坐标

ScreenDetailed 接口的 availTop 属性返回一个 number,代表可用屏幕区域的 y 坐标

ScreenDetailed 接口的 devicePixelRatio 属性返回一个 number,代表屏幕的 device pixel ratio(当前屏幕时,等价于 window.devicePixelRatio

ScreenDetailed 接口的 isInternal 属性返回一个 boolean,指示是否为设备内部的屏幕

ScreenDetailed 接口的 isPrimary 属性返回一个 boolean,指示是否为操作系统的主屏幕

ScreenDetailed 接口的 label 属性返回一个 string,代表屏幕的描述性标签

ScreenDetailed 接口的 left 属性返回一个 number,代表总屏幕区域的 x 坐标

ScreenDetailed 接口的 top 属性返回一个 number,代表总屏幕区域的 y 坐标

指定屏幕打开窗口

调用 Window 接口的 open() 方法时,通过 windowFeatures 可选参数指定打开窗口的 left top width height 参数,从而实现指定屏幕打开窗口

1
2
3
4
5
6
const left = screenDetails.currentScreen.availLeft
const top = screenDetails.currentScreen.availTop
const width = screenDetails.currentScreen.availWidth / 2
const height = screenDetails.currentScreen.availHeight / 2

window.open('about:blank', '_blank', `left=${left},top=${top},width=${width},height=${height}`)

指定屏幕放置全屏

Element 接口 requestFullscreen() 方法的配置项支持传入 screen 参数,指定将全屏的窗口放置至指定屏幕

1
2
3
4
5
6
el.requestFullscreen({
screen:
screen.isExtended
? screenDetails.screens.filter(s => s !== screenDetails.currentScreen).at(0)
: screenDetails.currentScreen,
})

权限策略

该 API 受 window-management 权限策略的限制(无论是通过 Permissions-Policy 响应头指定抑或是通过 iframe 元素的 allow 属性指定)

默认为 self,即允许在当前上下文或内嵌的其他同源上下文中使用

权限 API

该 API 调用需要用户授予 window-management 权限,可以调用 Permission.query() 方法检查用户是否已授予了该权限

类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
interface Screen extends EventTarget {
readonly isExtended: boolean

onchange: ((this: Screen, ev: Event) => any) | null
}

interface Window {
getScreenDetails(): Promise<ScreenDetails>
}

interface ScreenDetails extends EventTarget {
readonly screens: ScreenDetailed[]
readonly currentScreen: ScreenDetailed

onscreenschange: ((this: ScreenDetails, ev: Event) => any) | null
oncurrentscreenchange: ((this: ScreenDetails, ev: Event) => any) | null
}

interface ScreenDetailed extends Screen {
readonly availLeft: number
readonly availTop: number
readonly left: number
readonly top: number
readonly isPrimary: boolean
readonly isInternal: boolean
readonly devicePixelRatio: number
readonly label: string
}

interface FullscreenOptions {
screen: ScreenDetailed
}

链接

Clipboard API

Clipboard API 允许异步地读写剪切板

剪切板操作通过 Navigator 接口的 clipboard 属性暴露的 Clipboard 接口实例使用

剪切板的部分操作需要获得 clipboard-write 权限和 clipboard-read 权限

读写文本

使用 Clipboard 接口的 writeText() 方法向剪切板中写入文本

方法接收一个字符串参数,代表向剪切板写入的文本内容

1
navigator.clipboard.writeText('data')

若存在用户交互,方法调用会自动授予 clipboard-write 权限

使用 Clipboard 接口的 readText() 方法从剪切板中读取文本

方法返回一个字符串,代表从剪切板读取的文本内容

1
const data = await navigator.clipboard.readText()

方法调用需要用户授予 clipboard-read 权限

读写复杂格式

使用 Clipboard 接口的 write() 方法向剪切板中写入复杂格式内容

方法接收一个 ClipboardItem 数组,代表要向剪切板写入的数据

调用 ClipboardItem() 构造函数创建 ClipboardItem 实例

需传入一个数据对象,该对象要求键为数据的 MIME 类型,值为实际数据(允许为 Promise)

1
2
3
4
5
navigator.clipboard.write([
new ClipboardItem({
'text/plain': new Blob(['data'], { type: 'text/plain' }),
}),
])

方法调用需要用户授予 clipboard-write 权限

使用 Clipboard 接口的 read() 方法从剪切板中读取复杂格式内容

方法返回一个 ClipboardItem 数组,代表要从剪切板读取的数据

ClipboardItem 实例的 types 属性反映其支持的 MIME 类型

ClipboardItem 实例的 getType() 方法根据指定的 MIME 类型返回对应的 Blob

1
2
3
const datas = await navigator.clipboard.read()

const blob = await datas.at(0).getType('text/plain')

方法调用需要用户授予 clipboard-read 权限

权限 API

该 API 调用需要用户授予 clipboard-readclipboard-write 权限,可以调用 Permission.query() 方法检查用户是否已授予了该权限

类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
interface Clipboard extends EventTarget {
read(): Promise<ClipboardItems>
readText(): Promise<string>
write(data: ClipboardItems): Promise<void>
writeText(data: string): Promise<void>
}

declare var Clipboard: {
prototype: Clipboard
}

interface ClipboardItem {
readonly types: ReadonlyArray<string>
getType(type: string): Promise<Blob>
}

declare var ClipboardItem: {
prototype: ClipboardItem
new(items: Record<string, string | Blob | PromiseLike<string | Blob>>): ClipboardItem
}

链接

Broadcast Channel API

Broadcast Channel API 允许在同源的浏览上下文中交换数据

创建广播频道

通过直接调用 BroadcastChannel() 构造函数来创建一个广播频道

需要传递一个字符串参数,代表广播频道的名称

若该名称的广播频道已经创建,则会复用并加入已有的广播频道

1
const bc = new BroadcastChannel('bc')

传递的广播频道名称可以经由 BroadcastChannel 实例的 name 只读属性读取

发送消息

通过调用 BroadcastChannel 实例的 postMessage() 方法来向广播频道发送消息

1
bc.postMessage('message')

发送的消息会经由结构化克隆算法处理,因此任意可由结构化克隆算法处理的类型的数据均可发送

订阅消息

通过监听 BroadcastChannel 实例的 message 事件监听成功接收的消息

1
2
3
bc.addEventListener('message', (e) => {
console.log(e.data)
})

通过监听 BroadcastChannel 实例的 messageerror 事件监听接收失败的消息

1
2
3
bc.addEventListener('messageerror', (e) => {
console.log(e.data)
})

关闭广播频道

通过调用 BroadcastChannel 实例的 close() 方法来断开与广播频道的连接

1
bc.close()

广播频道在没有任一浏览上下文连接至其时被回收

示例

收到的信息为:

类型

1
2
3
4
5
6
7
8
9
10
11
12
interface BroadcastChannel extends EventTarget {
readonly name: string
onmessage: ((this: BroadcastChannel, ev: MessageEvent) => any) | null
onmessageerror: ((this: BroadcastChannel, ev: MessageEvent) => any) | null
close(): void
postMessage(message: any): void
}

declare var BroadcastChannel: {
prototype: BroadcastChannel
new(name: string): BroadcastChannel
}

链接

Pyodide

Pyodide 是一个在浏览器环境中运行的 Python 解释器,利用了 CPython 技术和 WebAssembly 技术,从而可以在浏览器中运行 Python 软件包;并且 Pyodide 保证了 Python 与 JavaScript 的兼容,允许 Python 使用浏览器的 API

基本使用

使用 CDN 形式向项目中引入 Pyodide 软件包

1
<script defer src="https://cdn.jsdelivr.net/pyodide/v0.24.1/full/pyodide.js"></script>

然后调用已注入到全局的 loadPyodide() 方法获取到 Pyodide 实例

1
const pyodide = await loadPyodide()

调用 Pyodide 实例的 runPython() 方法以执行 Python 代码

该方法传入的字符串,代表需要执行的 Python 代码

该方法的返回值,执行 Python 代码的输出

1
pyodide.runPython(`print('Hello world!')`)

调用 print() 方法的效果相当于调用 console.log() 方法

1
2
3
4
5
6
pyodide.runPython('1 + 2')
pyodide.runPython('2 ** 10')
pyodide.runPython(`
import sys
sys.version
`)

同样可以在 Pyodide 中使用内置软件包,也可以导入外部软件包

Pyodide 实例的 runPythonAsync() 方法用于异步执行 Python 代码

1
await pyodide.runPythonAsync('1 * 1')

JS 获取 Python 作用域

Python 全局作用域通过 pyodide.globals 对象暴露

调用其 get() 方法以获取 Python 全局作用域的变量

1
2
3
4
5
6
7
pyodide.runPython('x = 1')
pyodide.runPython(`y = 'xes'`)
pyodide.runPython('z = [2, 4]')

pyodide.globals.get('x')
pyodide.globals.get('y')
pyodide.globals.get('z').toJs()

Python 中复杂类型变量,如列表等,可以通过调用 toJs() 来转换为一个 JS 对象

调用其 set() 方法以设置 Python 全局作用域的变量

1
2
3
4
5
6
7
pyodide.globals.set('xx', 'xxxxx')
pyodide.globals.set('alert', alert)
pyodide.globals.set('square', x => x ** 2)

pyodide.runPython('print(xx)')
pyodide.runPython(`alert('xxxx')`)
pyodide.runPython('print(square(20))')

可以设置普通变量、对象、函数,以及 JS 或 DOM 内置函数等等

Python 获取 JS 作用域

在 Python 中导入 JS 包以使用 JS 的全局变量

1
2
3
import js

js.confirm('message')

使用类似与调用 window 变量

Worker 环境使用

使用 importScripts() 方法导入 CDN 脚本,随后通过暴露在全局的 loadPyodide() 方法使用

1
self.importScripts("https://cdn.jsdelivr.net/pyodide/v0.24.1/full/pyodide.js")

特别的,若在 ServiceWorker 中使用,需要导入 XMLHttpRequest 的 shim 包,因为该类在 ServiceWorker 中无法使用但 Pyodide 依赖于该包,如

1
2
importScripts("./node_modules/xhr-shim/src/index.js")
self.XMLHttpRequest = self.XMLHttpRequestShim

模块 Worker 环境下需要改变导入方式为 ESM

1
2
3
4
import "./node_modules/xhr-shim/src/index.js"
self.XMLHttpRequest = self.XMLHttpRequestShim
import "./pyodide.asm.js"
import { loadPyodide } from "./pyodide.mjs"

文件系统

Pyodide 基于 Emscripten File System API 实现了自定义的文件系统

在 Python 环境中直接调用相关文件操作方法;通过 pyodide.FS 对象向浏览器环境暴露对文件系统的操作

1
2
3
4
5
6
7
with open("/hello.txt", "r") as fh:
data = fh.read()
print(data)

with open("/hello.txt", "r") as fh:
data = fh.read()
print(data)
1
2
3
const file = pyodide.FS.readFile("/hello.txt", { encoding: "utf8" })

pyodide.FS.writeFile("/hello.txt", data, { encoding: "utf8" })

需要注意的是,Pyodide 默认使用的是 MEMFS,可以通过调用其 mount 方法绑定至其他的文件系统

1
pyodide.FS.mount(pyodide.FS.filesystems.NODEFS, { root: "." }, "/mnt")

或者可以使用原生 File System API 绑定至 Origin Private File System 或 Native File System

1
2
3
4
5
const root = await navigator.storage.getDirectory()

const nativefs = await pyodide.mountNativeFS("/mnt", root)

await nativefs.syncfs()

Document Picture-in-Picture API

Document Picture-in-Picture API 是对原有 Picture-in-Picture API 的扩展,允许任意 DOM 元素进入画中画模式

任意时刻,每个顶层浏览上下文只能存在一个画中画窗口(多次调用会复用之前的画中画窗口)

该 API 必须在严格上下文模式下调用

Window 接口暴露的 documentPictureInPicture 只读属性提供的 DocumentPictureInPicture 接口实例来使用该 API

打开画中画窗口

DocumentPictureInPicture 接口的 requestWindow() 方法用于打开一个画中画窗口

方法传入一个可选的配置项,其 width 选项和 height 选项代表画中画窗口的宽度和高度

方法返回一个 Promise 的 Window,代表当前文档对应的画中画窗口

方法可能抛出 NotSupportedError,若该 API 不被支持(如因为用户的设置)

方法可能抛出 NotAllowedError,若未因为用户交互调用,或未在顶层浏览上下文在调用,或在画中画窗口中调用

方法可能抛出 RangeError,若参数 widthheight 仅指定其一或另一为 0

1
2
3
4
const pipwindow = await window.documentPictureInPicture.requestWindow({
width: 800,
height: 600,
})

DocumentPictureInPicture 接口的 enter 事件在打开画中画窗口时触发,返回一个 DocumentPictureInPictureEvent 事件

画中画窗口

DocumentPictureInPicture 接口的 window 只读属性返回 Windownull,反映当前文档对应的画中画窗口,若不存在返回 null

DocumentPictureInPicture 接口的 requestWindow() 方法打开的画中画窗口与 Window 接口的 open() 方法打开的同源的窗口类似

但存在以下一些区别:

  • 画中画窗口始终浮动在其他窗口顶部
  • 画中画窗口的生命周期一定不会比打开其的窗口的生命周期晚结束
  • 画中画窗口无法被导航
  • 画中画窗口的位置无法被网站设置

关闭画中画窗口

可能因为用户点击关闭按钮而关闭,或是调用 Window 接口的 close() 方法编程式关闭

在画中画窗口关闭时,类似与普通页面一样,可以通过监听 pagehide 事件其发生的时机

类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
interface Window {
readonly documentPictureInPicture: DocumentPictureInPicture
}

interface DocumentPictureInPicture extends EventTarget {
requestWindow(options?: DocumentPictureInPictureOptions): Promise<Window>
readonly window: Window | null
onenter
}

interface DocumentPictureInPictureOptions {
width: number
height: number
}

interface DocumentPictureInPictureEvent extends Event {
constructor(type: string, eventInitDict: DocumentPictureInPictureEventInit)
readonly window: Window
}

interface DocumentPictureInPictureEventInit extends EventInit {
window: Window
}

链接

Vibration API

Vibration API 允许调用设备的振动功能

若设备不支持振动,调用该方法不会具有任何效果

振动

Navigator 接口的 vibrate() 方法负责执行振动

方法传入一个数值或一个数值数组,代表振动的模式

方法返回一个 boolean,表示是否因为方法参数的合法性导致能否进行振动

1
2
3
4
navigator.vibrate(200)
navigator.vibrate([200])
navigator.vibrate([200, 100, 200])
navigator.vibrate(0)

若传入一个数值或仅有一个数值的数组,代表执行给定时间的一次振动

若传入一个数值数组,按照振动时间、暂停时间的循环执行振动

振动模式数组有一个用户代理定义的最大长度,传入超出此长度限制的数组会被截取至规定的长度

若传入参数前已有振动运行,会停止已有的振动并执行新的振动模式

若传入 0 或空数组或全 0 的数组,代表停止振动

需要注意的是,该方法要求在页面可见或存在用户交互的情况下调用,否则不具备效果

类型

1
2
3
4
5
interface Navigator {
vibrate(pattern: VibratePattern): boolean
}

type VibratePattern = number | number[]

链接


:D 一言句子获取中...