Safari 16.4
Safari 16.4 リリース。
Web API
多くの Web API がサポートされるようになりました。モダンブラウザの中で Safari のみがサポートしていなかったものが多く、ブラウザ互換性を見て失望することや、冗長な記述を強いられること、ポリフィルを含める必要がなくなるのは大変ありがたいです。数が多いので、使う機会があるかもしれないと思うものを適当にピックアップして軽く紹介します。
Fullscreen API
特定の要素を全画面モードで表示したり、それを解除したりできる API です。ブラウザゲームや、賃貸情報サイトの間取り図などの拡大などで有用かもしれません。ただし、これは離脱用の UI を作らない限り、ESC キーなどで離脱する必要があるため、ユーザーのことをよく考え利用した方が良いでしょう。
document.addEventListener(
"keydown",
(e) => {
if (e.key === "Enter") {
toggleFullScreen();
}
},
false
);
function toggleFullScreen() {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen();
} else if (document.exitFullscreen) {
document.exitFullscreen();
}
}Screen Orientation API
画面の向きを取得できる API です。
const orientation = screen.orientation;
orientation.addEventListerner("change", () => {
console.log(orientation.type, orientation.angle);
});
rotateBtn.addEventListener("click", async () => {
const oppositeOrientation = screen.orientation.type.startsWith("portrait")
? "landscape"
: "portrait";
scrren.orientation
.lock(oppositeOrientation)
.then(() => {
console.log(`Locked to ${oppositeOrientation}`);
})
.catch((error) => {
console.error(error);
});
});
unlockBtn.addEventListener("click", () => {
screen.otientation.unlock();
});Screen Wake Lock API
端末が自動で画面を暗くしたり、ロックしたりするのを防ぐ API です。アプリケーションの継続が必要なケースや、QR コードを表示しておく必要があるアプリなどで有用です。
const wakeLock = await navigator.wakeLock.request("screen");
wakeLock.addEventListener("release", () => {
console.log("Happy release wakeLock!!");
});
await heavyTask();
await wakeLock.release();ページのタブを切り替えるなどでドキュメントの可視性が変化すると解除されてしまう点には注意が必要です。必要に応じて、visibilityChange イベントを監視し、ロックを掛け直しましょう。
document.addEventListener("visibilitychange", async () => {
if (wakeLock !== null && document.visibilityState === "visible") {
wakeLock = await navigator.wakeLock.request("screen");
}
});OffscreenCanvas
OffscreenCanvas は、端的に言えば DOM と Canvas API の分離です。
従来の Canvas を利用した高度なアニメーションの描画はメインスレッドを圧迫するため、ユーザーの阻害するという問題がありました。OffscreenCanvas では、アニメーションの描画を Web Worker の Worker スレッドで行うことで、この問題が解消されます。
今までも、DOM の操作は出来ないものの重いデータ処理を Web Worker などで行うことで、負荷を軽減することは出来ましたが、OffscreenCanvas は Transferable オブジェクトであり、実質オフスクリーンでレンダリングできる DOM から分離された Canvas であるため、描画までを Web Worker で担えます。
const offscreenCanvas = canvas.transferControllToOffscreen();
const worker = new Worker("worker.js");
worker.postMessage({ canvas: offscreenCanvas }, [offscreenCanvas]);
// worker.js
onmessage = (event) => {
const offscreenCanvas = event.data.canvas;
const ctx = offscreenCanvas.getContext("webgl");
// ...
};Web Push API
今までは Android などでしかサポートされていなかった Web Push が、iOS 16.4 でサポートされました。 ホーム画面に追加された Web アプリから、Push API、Notifications API、Service Worker などを駆使して、ユーザーにプッシュ通知を送ることが可能になります。
またホーム画面に追加された Web アプリでカウントを表示できる Badging API などもサポートされ、今後 PWA の利用が広がると期待されます。
https://webkit.org/blog/13878/web-push-for-web-apps-on-ios-and-ipados/
ECMAScript features
Safari 16.4 は、非常に巨大なリリースです。JavaScritp の多くの機能も新たにサポートされました。
https://developer.apple.com/documentation/safari-release-notes/safari-16_4-release-notes
以下に、サポートされた機能のいくつかを軽く紹介します。
Array.formAsync
Array.from が for 相当なのに対して、Array.fromAsync は for await と見れば理解しやすいと思います。 Async iterable を処理する方法として、Promise.all などがありますが、Promise.all が並列実行であるのに対して、 Array.fromAsync は for await 相当であり、順次実行されます。
Array#group, Array#groupToMap
Array#group は、配列を指定した関数の戻り値でグルーピングしたオブジェクトを返します。 一方、Array#groupToMap は、配列を指定した関数の戻り値でグルーピングした Map を返します。
https://github.com/tc39/proposal-array-grouping
コードを見た方が理解しやすいと思います。
const animals = [
{ name: "たま", type: "猫" },
{ name: "みけ", type: "猫" },
{ name: "ぽち", type: "犬" },
];
console.log(animals.group((animal) => animal.type));
/* result
{
猫: [
{ name: 'たま', type: '猫' },
{ name: 'みけ', type: '猫' },
],
犬: [
{ name: 'ぽち', type: '犬', },
],
}
*/
console.log(animals.groupToMap((animal) => animal.type).get("猫"));
/* result
[
{ name: 'たま', type: '猫' },
{ name: 'みけ', type: '猫' },
]
*/Import Maps
ブラウザで実行される import によって読み込まれるパッケージの URL を指定する手段です。
https://github.com/WICG/import-maps#multiple-import-map-support
<head>
<script type="importmap">
{
"imports": {
"react": "https://unpkg.com/react@18/umd/react.development.js",
"react-dom": "https://unpkg.com/react-dom@18/umd/react-dom.development.js"
}
}
</script>
</head>
<body>
<script type="module">
import { createRoot } from "react-dom/client";
import React from "react";
</script>
</body>