10个打开了我新世界大门的 WebAPI

53,954

原来我对好多 Web API 一无所知,打开了我新世界的大门,未来 Web 可以做到更多,早日一统江湖吧,吼吼吼。

虽然这些 API 很多目前还存在兼容性的问题,但是还是有必要了解一下的,文中的代码,我已经都测试过了。希望你看完之后能够有所收获。

原文链接:blog.bitsrc.io/10-useful-w…

你可能已经知道并使用更为流行的 Web APIsWeb WorkerFetch等),但也有少数不那么流行的 API,我个人喜欢使用,并建议你也尝试一下。

这篇文章中描述的所有 Web API 示例都可以在这里找到:

1. Web Audio API

Web Audio API MDN

Web Audio API 允许你在 Web 上操作音频流。它可用于向网络上的音频源添加效果和滤镜。

音频源可以来自 <audio>,视频/音频源文件或音频网络流。

让我们看一个简单的例子:

<body>
    <header>
        <h2>Web APIs<h2>
    </header>
    <div class="web-api-cnt">
        <div class="web-api-card">
            <div class="web-api-card-head"> Demo - Audio </div>
            <div class="web-api-card-body">
                <div id="error" class="close"></div>
                <div>
                    <audio controls src="./lovely.mp4" id="audio"></audio>
                </div>
                <div>
                    <button onclick="audioFromAudioFile.init()">Init</button>
                    <button onclick="audioFromAudioFile.play()">Play</button>
                    <button onclick="audioFromAudioFile.pause()">Pause</button>
                    <button onclick="audioFromAudioFile.stop()">Stop</button>
                </div>
                <div>
                    <span>Vol: <input onchange="audioFromAudioFile.changeVolume()" type="range" id="vol" min="1" max="3"
                            step="0.01" value="1" /></span>
                    <span>Pan: <input onchange="audioFromAudioFile.changePan()" type="range" id="panner" min="-1"
                            max="1" step="0.01" value="0" /></span>
                </div>
            </div>
        </div>
    </div>
</body>
<script>
    const l = console.log
    let audioFromAudioFile = (function () {
        var audioContext
        var volNode
        var pannerNode
        var mediaSource

        function init() {
            l("Init")
            try {
                
                audioContext = new AudioContext()
                mediaSource = audioContext.createMediaElementSource(audio)
                volNode = audioContext.createGain()
                volNode.gain.value = 1
                pannerNode = new StereoPannerNode(audioContext, { pan: 0 })

                mediaSource.connect(volNode).connect(pannerNode).connect(audioContext.destination)
                console.log(volNode)
            }
            catch (e) {
                error.innerHTML = "The Web Audio API is not supported in this device."
                error.classList.remove("close")
            }
        }
        function play() {
            audio.play()
        }

        function pause() {
            audio.pause()
        }

        function stop() {
            audio.stop()
        }

        function changeVolume() {
            volNode.gain.value = document.getElementById('vol').value
        }

        function changePan() {
            pannerNode.gain.value = tdocument.getElementById('panner').value
        }

        return {
            init,
            play,
            pause,
            stop,
            changePan,
            changeVolume
        }
    })()
</script>

译者注:源代码有点小问题,上面的代码我已经修改过,可以运行,不过mp4文件换成自己本地有的。

此示例将音频从 <audio> 元素传递到 AudioContext。声音效果(例如声像)在添加到音频输出(扬声器)之前已添加到音频源。

单击 Init 按钮将调用 init 函数。这将创建一个 AudioContext 实例并将其设置为 audioContext。接下来,它创建一个媒体源 createMediaElementSource(audio),将音频元素作为音频源传递。

createGain 创建音量节点 volNode。在这里,我们调整音频的音量。接下来,使用 StereoPannerNode 设置声像效果。最后,将节点连接到媒体源。

我们有一个音量和声像的滑块,拖动它们会影响音量和音频的声像效果。

这个例子有问题,所以该链接也无法正常使用,可以拷贝上面的代码在本地运行

try it

2. Fullscreen API

Fullscreen API MDN

Fullscreen API 让我们能够在 Web app 中启用全屏模式。它使你可以选择要在全屏模式下查看的元素。在 Android 手机中,它将删除浏览器窗口和 Android 顶部状态栏(显示网络状态,电池状态等的地方)。

方法:

  • requestFullscreen 在系统上以全屏模式显示选定的元素,从而关闭其他应用程序以及浏览器和系统UI元素。

  • exitFullscreen 将全屏模式退出到正常模式。

让我们看一个简单的示例,其中我们可以使用全屏模式观看视频:

<body>
    <header>
        <h2>Web APIs<h2>
    </header>
    <div class="web-api-cnt">
        <div class="web-api-card">
            <div class="web-api-card-head"> Demo - Fullscreen </div>
            <div class="web-api-card-body">
                <div id="error" class="close"></div>
                <div> This API makes fullscreen-mode of our webpage possible. It lets you select the Element you want to
                    view in fullscreen-mode, then it shuts off the browsers window features like URL bar, the window
                    pane, and presents the Element to take the entire width and height of the system. In Android phones,
                    it will remove the browsers window and the Android UI where the network status, battery status are
                    displayed, and display the Element in full width of the Android system. </div>
                <div class="video-stage">
                    <video id="video" src="./lovely.mp4"></video>
                    <button onclick="toggle()">Toogle Fullscreen</button>
                </div>
                <div> This API makes fullscreen-mode of our webpage possible. It lets you select the Element you want to
                    view in fullscreen-mode, then it shuts off the browsers window features like URL bar, the window
                    pane, and presents the Element to take the entire width and height of the system. In Android phones,
                    it will remove the browsers window and the Android UI where the network status, battery status are
                    displayed, and display the Element in full width of the Android system. </div>
            </div>
        </div>
    </div>
</body>
<script>
    const l = console.log

    function toggle() {
        const videoStageEl = document.querySelector(".video-stage")
        console.log(videoStageEl.requestFullscreen)
        if (videoStageEl.requestFullscreen) {
            if (!document.fullscreenElement) {
                videoStageEl.requestFullscreen()
            }
            else {
                document.exitFullscreen()
            }
        } else {
            error.innerHTML = "Fullscreen API not supported in this device."
            error.classList.remove("close")
        }
    }
</script>

video 元素在 div#video-stage元素中,并带有一个按钮 Toggle Fullscreen

当我们单击 Toggle Fullscreen 按钮时,我们希望使元素 div#video-stage 变为全屏显示。

看一下 toggle 这个函数:

function toggle() {
    const videoStageEl = document.querySelector(".video-stage")
    if(!document.fullscreenElement)
        videoStageEl.requestFullscreen()
    else
        document.exitFullscreen()
}

获取 div#video-stage 元素,并将其实例保留在 videoStageEl 上。

我们用过 document.fullsreenElement 属性可以知道该元素是否处于全屏模式,如果不是全屏模式,可以调用 videoStageEl 上的 requestFullscreen() 方法,使 div#video-stage 接管整个设备视图。

如果在全屏模式下点击 Toggle Fullscreen 按钮,将会调用 document.exitFullcreen() ,从而返回到普通视图。

try it

注:该链接中的视频资源找不到了,但是全屏功能是正常的,大家也可以在本地测试

3. Web Speech API

Web Speech API MDN

Web Speech API 让我们可以将语音合成和语音识别功能添加到Web应用中。

使用此 API ,我们将能够向Web应用发出语音命令,就像在 Android 上通过其 Google Speech 或像在Windows 中使用 Cortana 一样。

让我们看一个简单的例子。我们将看到如何使用 Web Speech API 实现文本到语音和语音到文本的转换。

<body>
    <header>
        <h2>Web APIs<h2>
    </header>
    <div class="web-api-cnt">
        <div id="error" class="close"></div>
        <div class="web-api-card">
            <div class="web-api-card-head"> Demo - Text to Speech </div>
            <div class="web-api-card-body">
                <div>
                    <input placeholder="Enter text here" type="text" id="textToSpeech" />
                </div>
                <div>
                    <button onclick="speak()">Tap to Speak</button>
                </div>
            </div>
        </div>
        <div class="web-api-card">
            <div class="web-api-card-head"> Demo - Speech to Text </div>
            <div class="web-api-card-body">
                <div>
                    <textarea placeholder="Text will appear here when you start speeaking."
                        id="speechToText"></textarea>
                </div>
                <div>
                    <button onclick="tapToSpeak()">Tap and Speak into Mic</button>
                </div>
            </div>
        </div>
    </div>
</body>
<script>

    try {
        var speech = new SpeechSynthesisUtterance()
        var recognition = new SpeechRecognition()
    } catch (e) {
        error.innerHTML = "Web Speech API not supported in this device."
        error.classList.remove("close")
    }

    function speak() {
        speech.text = textToSpeech.value
        speech.volume = 1
        speech.rate = 1
        speech.pitch = 1
        alert(window.speechSynthesis)
        window.speechSynthesis.speak(speech)
    }

    function tapToSpeak() {
        recognition.onstart = function () { }

        recognition.onresult = function (event) {
            const curr = event.resultIndex
            const transcript = event.results[curr][0].transcript
            speechToText.value = transcript
        }

        recognition.onerror = function (ev) {
            console.error(ev)
        }

        recognition.start()
    }

</script>

第一个演示 Demo - Text to Speech 演示了通过一个简单的输入框接收输入的文字以及一个按钮点击后输出语音的功能。

看一下 speak 函数:

function speak() {
    speech.text = textToSpeech.value
    speech.volume = 1
    speech.rate = 1
    speech.pitch = 1
    window.speechSynthesis.speak(speech)
}

它实例化 SpeechSynthesisUtterance() 对象,将我们在输入框中输入的文本转换为语音。然后,调用语音对象 SpeechSynthesisspeak 函数,使输入框中的文本在我们的扬声器中放出。

第二个演示 Demo - Speech to Text 是语音识别演示。我们点击 Tap and Speak into Mic 按钮,对着麦克风说话,我们说的单词就被翻译成了文本。

Tap and Speak into Mic 按钮单击后调用 tapToSpeak 函数:

function tapToSpeak() {
    recognition.onstart = function () { }

    recognition.onresult = function (event) {
        const curr = event.resultIndex
        const transcript = event.results[curr][0].transcript
        speechToText.value = transcript
    }

    recognition.onerror = function (ev) {
        console.error(ev)
    }

    recognition.start()
}

很简单,实例化 SpeechRecognition,然后注册事件处理程序和回调。在语音识别开始时调用 onstart,在发生错误时调用 onerror 。每当语音识别捕获到一条线时,就会调用 onresult

可以看到,在 onresult 回调中,我们提取文本并将其设置到文本区域。所以当我们对着麦克风说话时,这些内容会输出在文本区域中。

try it

译者:我的爪机和电脑 Chrome(V83) 都不能支持该 API

4. Bluetooth API

Bluetooth API MDN

实验技术

Bluetooth API 使得我们可以访问手机上的低功耗蓝牙设备,并使用它来将网页中的数据共享到另一台设备上。

想象一下能够创建一个Web聊天应用,该应用程序可以通过蓝牙发送和接收来自其他手机的消息。

基础 APInavigator.bluetooth.requestDevice。调用它将使浏览器提示用户选择一个设备,使他们可以选择一个设备或取消请求。

navigator.bluetooth.requestDevice 需要一个对象。该对象定义了用于返回与过滤器匹配的蓝牙设备的过滤器。

让我们看一个简单的演示。本演示将使用 navigator.bluetooth.requestDeviceAPI 从BLE设备检索基本设备信息。

<body>
    <header>
        <h2>Web APIs<h2>
    </header>
    <div class="web-api-cnt">
        <div class="web-api-card">
            <div class="web-api-card-head"> Demo - Bluetooth </div>
            <div class="web-api-card-body">
                <div id="error" class="close"></div>
                <div>
                    <div>Device Name: <span id="dname"></span></div>
                    <div>Device ID: <span id="did"></span></div>
                    <div>Device Connected: <span id="dconnected"></span></div>
                </div>
                <div>
                    <button onclick="bluetoothAction()">Get BLE Device</button>
                </div>
            </div>
        </div>
    </div>
</body>
<script>
    function bluetoothAction() {
        if (navigator.bluetooth) {
            navigator.bluetooth.requestDevice({
                acceptAllDevices: true
            }).then(device => {
                dname.innerHTML = device.name
                did.innerHTML = device.id
                dconnected.innerHTML = device.connected
            }).catch(err => {
                error.innerHTML = "Oh my!! Something went wrong."
                error.classList.remove("close")
            })
        } else {
            error.innerHTML = "Bluetooth is not supported."
            error.classList.remove("close")
        }
    }
</script>

设备的信息会展示出来。单击按钮 Get BLE Device 则调用 bluetoothAction 函数。

function bluetoothAction() {
    if (navigator.bluetooth) {
        navigator.bluetooth.requestDevice({
            acceptAllDevices: true
        }).then(device => {
            dname.innerHTML = device.name
            did.innerHTML = device.id
            dconnected.innerHTML = device.connected
        }).catch(err => {
            
            error.innerHTML = "Oh my!! Something went wrong."
            error.classList.remove("close")
        })
    } else {
        error.innerHTML = "Bluetooth is not supported."
        error.classList.remove("close")
    }
}

bluetoothAction 函数调用 navigator.bluetooth.requestDevice API,参数设置为 acceptAllDevices: true,这将使其扫描并列出附近所有开启了蓝牙的设备。它返回的是一个 Promise

try it

译者注:电脑上 Chrome 浏览器上测试了下,是支持该API的。

5. Channel Messaging API

Channel Messaging API MDN

Channel Messaging API 允许两个不同的脚本运行在同一个文档的不同浏览器上下文(比如两个 iframe,或者文档主体和一个 iframe,或者两个 worker)来直接通讯,在每端使用一个端口(port)通过双向频道(channel)向彼此传递消息。。

首先创建一个 MessageChannel 实例:

new MessageChannel()

这将返回一个 MessagePort 对象(通讯信道)。

然后,就可以通过 MessagePort.port1MessageChannel.port2 设置端口。

实例化 MessageChannel 的上下文将使用 MessagePort.port1,另一个上下文将使用 MessagePort.port2。然后,就可以使用 postMessage API 传递消息了。

每个浏览器上下文都使用 Message.onmessage 监听消息,并使用事件的 data 属性获取消息内容。

让我们看一个简单的示例,在这里我们可以使用 MessageChannel 在文档和 iframe 之间发送文本。

译者注:这个demo,原文中代码有错误,译者对代码进行了修改,亲测可以正常运行

<body>
    <header>
        <h2>Web APIs<h2>
    </header>
    <div class="web-api-cnt">
        <div class="web-api-card">
            <div class="web-api-card-head"> Demo - MessageChannel </div>
            <div class="web-api-card-body">
                <div id="error" class="close"></div>
                <div id="displayMsg">
                </div>
                <div>
                    <input id="input" type="text" placeholder="Send message to iframe" />
                </div>
                <div>
                    <button onclick="sendMsg()">Send Msg</button>
                </div>
                <div>
                    <iframe id="iframe" src="./iframe.content.html"></iframe>
                </div>
            </div>
        </div>
    </div>
</body>
<script>
    try {
        var channel = new MessageChannel()
        var port1 = channel.port1
    } catch (e) {
        error.innerHTML = "MessageChannel API not supported in this device."
        error.classList.remove("close")
    }

    iframe.addEventListener("load", onLoad)

    function onLoad() {
        port1.onmessage = onMessage
        iframe.contentWindow.postMessage("load", '*', [channel.port2])
    }

    function onMessage(e) {
        const newHTML = "<div>" + e.data + "</div>"
        displayMsg.innerHTML = displayMsg.innerHTML + newHTML
    }

    function sendMsg() {
        port1.postMessage(input.value)
    }

</script>

注意 iframe 的标签,我们在上面加载了一个 iframe.content.html 文件。按钮和文本是我们键入文字并向 iframe 发送消息的地方。

const channel = new MessageChannel()
const port1 = channel.port1
iframe.addEventListener("load", onLoad)
function onLoad() {
    port1.onmessage = onMessage
    iframe.contentWindow.postMessage("load", '*', [channel.port2])
}

function onMessage(e) {
    const newHTML = "<div>" + e.data + "</div>"
    displayMsg.innerHTML = displayMsg.innerHTML + newHTML
}

function sendMsg() {
    port1.postMessage(input.value)
}

我们初始化了 MessageChannelport1 。我们向 iframe 添加了 load 监听。在这里,我们在port1 注册了 onmessage 监听,然后使用 postMessageAPI 将消息发送到 iframe 。看到 port2 被向下发送到 iframe

让我们看一下 iframeiframe.content.html

<body>
    <div class="web-api-cnt">

        <div class="web-api-card">
            <div class="web-api-card-head">
                Running inside an <i>iframe</i>
            </div>
            <div class="web-api-card-body">
                <div id="iframeDisplayMsg">
                </div>
                <div>
                    <input placeholder="Type message.." id="iframeInput" />
                </div>

                <div>
                    <button onclick="sendMsgiframe()">Send Msg from <i>iframe</i></button>
                </div>

            </div>
        </div>

    </div>
</body>

<script>
    var port2
    window.addEventListener("message", function(e) {
        port2 = e.ports[0]
        port2.onmessage = onMessage
    })

    function onMessage(e) {
        const newHTML = "<div>"+e.data+"</div>"
        iframeDisplayMsg.innerHTML = iframeDisplayMsg.innerHTML + newHTML
    }

    function sendMsgiframe(){
        port2.postMessage(iframeInput.value)
    }
</script>

在这里,我们注册了一个消息事件处理函数。我们检索 port2 并在其上设置 onmessage 事件处理函数。现在,我们可以从 iframe 接收消息并将其发送到其父文档。

try it

译者注:这个 try 不起来哈,可以拷贝我上面的代码在本地尝试

6. Vibration API

Vibration API MDN

大多数现代移动设备包括振动硬件,其允许软件代码通过使设备摇动来向用户提供物理反馈。Vibration APIWeb 应用程序提供访问此硬件(如果存在)的功能,如果设备不支持此功能,则不会执行任何操作。

navigator.vibrate(pattern) 控制振动,pattern 是描述振动模式的单个数字或数字数组。

navigator.vibrate(200);
navigator.vibrate([200]);

以上两个例子都可以使设备振动 200 ms 并停止.

navigator.vibrate([200300400])

这将使设备振动200毫秒,暂停300毫秒,振动400毫秒,然后停止。

可以通过传递0,[][0,0,0](全零数组)来停止振动。

我们看一个简单的演示:

<body>
    <header>
        <h2>Web APIs<h2>
    </header>
    <div class="web-api-cnt">

        <div class="web-api-card">
            <div class="web-api-card-head">
                Demo - Vibration
            </div>
            <div class="web-api-card-body">
                <div id="error" class="close"></div>
                <div>
                    <input id="vibTime" type="number" placeholder="Vibration time" />
                </div>

                <div>
                    <button onclick="vibrate()">Vibrate</button>
                </div>

            </div>
        </div>

    </div>
</body>

<script>
    if(navigator.vibrate) {
        function vibrate() {
            const time = vibTime.value
            if(time != "")
                navigator.vibrate(time)
        }
    } else {
        error.innerHTML = "Vibrate API not supported in this device."
        error.classList.remove("close")        
    }
</script>

我们有输入和一个按钮。在输入框中输入振动的持续时间,然后点击按钮。设备将在输入的时间内振动

try it

译者注:在安卓手机上测试正常

7. Broadcast Channel API

Broadcast Channel API MDN

Broadcast Channel API 允许相同源下的不同浏览上下文的消息或数据进行通信。浏览上下文可以是窗口、iframe 等。

BroadcastChannel 类用于创建或加入频道。

const politicsChannel = new BroadcastChannel("politics")

politics 将是频道的名称。任何通过 politics 来初始化 BroadcastChannel 构造函数的上下文都将加入频道,它将接收在该频道上发送的任何消息,并且可以将消息发送到该频道。

如果是第一个使用 BroadcastChannel 的构造函数,politics 则会创建该频道。

要发布到频道,请使用 BroadcastChannel.postMessageAPI

要订阅频道(收听消息),请使用该 BroadcastChannel.onmessage 事件。

为了演示广播频道的用法,我构建了一个简单的聊天应用程序:

<body>
    <header>
        <h2>Web APIs<h2>
    </header>
    <div class="web-api-cnt">
        <div class="web-api-card">
            <div class="web-api-card-head"> Demo - BroadcastChannel </div>
            <div class="web-api-card-body">
                <div class="page-info">Open this page in another <i>tab</i>, <i>window</i> or <i>iframe</i> to chat with
                    them.</div>
                <div id="error" class="close"></div>
                <div id="displayMsg" style="font-size:19px;text-align:left;">
                </div>
                <div class="chatArea">
                    <input id="input" type="text" placeholder="Type your message" />
                    <button onclick="sendMsg()">Send Msg to Channel</button>
                </div>
            </div>
        </div>
    </div>
</body>
<script>
    const l = console.log;
    try {
        var politicsChannel = new BroadcastChannel("politics")
        politicsChannel.onmessage = onMessage
        var userId = Date.now()
    } catch (e) {
        error.innerHTML = "BroadcastChannel API not supported in this device."
        error.classList.remove("close")
    }

    input.addEventListener("keydown", (e) => {
        if (e.keyCode === 13 && e.target.value.trim().length > 0) {
            sendMsg()
        }
    })

    function onMessage(e) {
        const { msg, id } = e.data
        const newHTML = "<div class='chat-msg'><span><i>" + id + "</i>: " + msg + "</span></div>"
        displayMsg.innerHTML = displayMsg.innerHTML + newHTML
        displayMsg.scrollTop = displayMsg.scrollHeight
    }

    function sendMsg() {
        politicsChannel.postMessage({ msg: input.value, id: userId })

        const newHTML = "<div class='chat-msg'><span><i>Me</i>: " + input.value + "</span></div>"
        displayMsg.innerHTML = displayMsg.innerHTML + newHTML

        input.value = ""

        displayMsg.scrollTop = displayMsg.scrollHeight
    }  
</script>

初始化了 politicsChannel ,并在 politicsChannel 上设置了一个 onmessage 事件监听器,以便它可以接收和显示消息。

点击按钮时,会调用 sendMsg 函数。它通过 BroadcastChannel#postMessageAPI 将消息发送到 politicsChannel。初始化相同脚本的 tab 页,iframeworker 都将接收从此处发送的消息,因此该页面可以接收其他上下文发送的消息。

Try it

8. Payment Request API

Payment Request API MDN

Payment Request API 提供了为商品和服务选择支付途径的方法。

API 提供了一种一致的方式来向不同的商家提供付款细节,而无需用户再次输入细节。

它向商家提供账单地址,收货地址,卡详细信息等信息。

注意:API 提供了用户付款明细,但并不会带来新的付款方式。

让我们看一个演示如何使用付款请求 API 接受信用卡付款的演示:

<body>
    <header>
        <h2>Web APIs<h2>
    </header>
    <div class="web-api-cnt">
        <div class="web-api-card">
            <div class="web-api-card-head"> Demo - Credit Card Payment </div>
            <div class="web-api-card-body">
                <div id="error" class="close"></div>
                <div>
                    <button onclick="buy()">Buy</button>
                </div>
            </div>
        </div>
    </div>
</body>
<script>
    const networks = ["visa", "amex"]
    const types = ["debit", "credit"]

    const supportedInstruments = [
        {
            supportedMethods: "basic-card",
            data: {
                supportedNetworks: networks,
                supportedTypes: types
            }
        }
    ]

    const details = {
        total: {
            label: "Total",
            amount: {
                currency: "USD",
                value: "100"
            }
        },
        displayItems: [
            {
                label: "Item 1",
                amount: {
                    currency: "USD",
                    value: "50"
                }
            },
            {
                label: "Item 2",
                amount: {
                    currency: "USD",
                    value: "50"
                }
            },
        ]
    }

    try {
        var paymentRequest = new PaymentRequest(supportedInstruments, details)
    } catch (e) {
        error.innerHTML = "PaymentRequest API not supported in this device."
        error.classList.remove("close")
    }

    function buy() {
        paymentRequest.show().then(response => {
            console.log(response)
        })
    }
</script>

networkstypessupportedTypes 都是描述付款方式。details 列出了我们的购买商品和总费用。

构建 PaymentRequest 实例,paymentRequest.show() 将在浏览器中显示付款界面。并在 Promise 成功的回调中处理用户的数据。

它们是使用 Payment API 进行付款的许多配置,至少通过上面的示例,我们已经了解了 Payment Request API 的使用方式和工作方式。

try it

译者注:测试了下,但是没有走完全流程,毕竟我坚决不付款的~

9. Resize Observer API

Resize Observer API MDN

Resize Observer API 提供了一种方式,以任何方式调整了注册观察者的元素的大小,都通知观察者。

ResizeObserver 类提供了一个观察器,该观察器将在每个 resize 事件上调用。

const resizeObserver = new ResizeObserver(entries => {
    for(const entry of entries) {
        if(entry.contentBoxSize)
            consoleo.log("element re-sized")
    }
})
resizeObserver.observe(document.querySelector("div"))

每当调整 div 大小时,控制台上都会打印 "element re-sized"

让我们看一下如何使用 Resize Observer API 的示例:

<body>
    <header>
        <h2>Web APIs<h2>
    </header>
    <div class="web-api-cnt">

        <div class="web-api-card">
            <div class="web-api-card-head">
                Demo - ResizeObserver
            </div>
            <div class="web-api-card-body">
                <div id="error" class="close"></div>
                <div id="stat"></div>

                <div id="resizeBoxCnt">
                    <div id="resizeBox"></div>
                </div>

                <div>
                    <span>Resize Width:<input onchange="resizeWidth(this.value)" type="range" min="0" max="100" value="0" /></span>
                </div>

                <div>
                    <span>Resize Height:<input onchange="resizeHeight(this.value)" type="range" min="0" max="100" value="0" /></span>
                </div>

            </div>
        </div>

    </div>
</body>

<script>
    try {
        var resizeObserver = new ResizeObserver(entries => {
            for(const entry of entries) {
                    stat.innerHTML = "Box re-sized. Height:" + entry.target.style.height + " - Width:" + entry.target.style.width
            }
        })
        resizeObserver.observe(resizeBox)
    } catch(e) {
        error.innerHTML = "ResizeObserver API not supported in this device."
        error.classList.remove("close")        
    }

    function resizeWidth(e) {
        resizeBox.style.width = `${e}px`
    }

    function resizeHeight(e) {
        resizeBox.style.height = `${e}px`
    }
</script>

我们在这里有范围滑块。如果我们滑动它们,它们将改变 idv#resizeBox 的宽高。我们在div#resizeBox 上注册了 ResizeObserver 观察器,指示该消息指示框已被调整大小以及其高度和宽度的当前值。

尝试滑动范围滑块,你将看到 div#resizeBox 宽高的变化,此外,我们还将看到 div#stat 框中显示的信息。

try it

10. Pointer Lock API

Pointer Lock API MDN

Pointer Lock API 对于需要大量的鼠标输入来控制运动,旋转物体,以及更改项目的应用程序来说非常有用。对高度视觉化的应用程序尤其重要,例如那些使用第一人称视角的应用程序,以及 3D 视图和建模。

方法:

  • requestPointerLock:此方法将从浏览器中删除鼠标并发送鼠标状态事件。这将持续到调用 document.exitPointerLock 为止。
  • document.exitPointerLock:此 API 释放鼠标指针锁定并恢复鼠标光标。

让我们来看一个例子:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #box {
            background-color: green;
            width: 100%;
            height: 400px;
            position: relative;
        }

        #ball {
            border-radius: 50%;
            background-color: red;
            width: 50px;
            height: 50px;
            position: absolute;
        }
    </style>
</head>

<body>
    <header>
        <h2>Web APIs<h2>
    </header>
    <div class="web-api-cnt">
        <div class="web-api-card">
            <div class="web-api-card-head"> Demo - PointerLock </div>
            <div class="web-api-card-body">
                <div id="error" class="close"></div>
                <div id="box">
                    <div id="ball"></div>
                </div>
            </div>
        </div>
    </div>
</body>
<script>
    const l = console.log
    box.addEventListener("click", () => {
        if (box.requestPointerLock)
            box.requestPointerLock()
        else {
            error.innerHTML = "PointerLock API not supported in this device."
            error.classList.remove("close")
        }
    })

    document.addEventListener("pointerlockchange", (e) => {
        document.addEventListener("mousemove", (e) => {
            const { movementX, movementY } = e
            ball.style.top = movementX + "px"
            ball.style.left = movementY + "px"
        })
    })
</script>

</html>

div#box 中我们有一个 div#boxdiv#ball

我们在 div#box 上设置了一个 click 事件,当单击它时会调用 requestPointerLock(),这会使光标消失。

PointerLock 有一个 pointerlockchange 事件监听器。当指针锁定状态更改时,将触发此事件。在其回调中,我们将其添加到 mousemove 事件。在当前浏览器选项卡上移动鼠标时,将触发其回调。在此回调中,因此我们使用它来获取鼠标的当前X和Y位置。使用此信息,我们可以设置 div#balltopleft 样式属性,因此,当鼠标移动时,我们会看到一个跳舞的球(译者注:原文的demo中没有设置div#ball的定位,因此修改 topleft 值时,小球位置没有变化)。

鼠标事件的两个新参数 —— movementXmovementY 提供了鼠标位置的变化情况。当指针锁定被启动之后,正常的 MouseEvent 属性 clientX, clientY, screenX, 和 screenY ,保持不变,就像鼠标没有在移动一样。

try it

译者注:这个demo有点问题,因此try不起来,大家可以拷贝我上面的代码在本地 try.

总结

Web日趋复杂。越来越多的原生功能正在使用中,这是因为Web用户的数量远远大于原生APP用户。用户在原生应用上的体验被带到Web上,这样他们无需去使用原生应用。

好嘛,如果看到这里,说明是真爱了。要不要给我的 Github 增加一个 star。

如有翻译不当的地方,多多包涵,欢迎在评论区指正~