スマートフォンを縦から横に持ち替えたときに、レイアウトを組み直したい、ゲームの画面の大きさを変えたい、ということがあります。かつては orientationchange イベントと window.orientation プロパティを使うのが定番でしたが、これらは今では非推奨です。ここでは、昔の方法で何が困ったのかと、今の書き方を説明します。
昔の方法: orientationchange と window.orientation
iPhone の Safari が始めた方法で、Android のブラウザーにも広まりました。
// 昔の書き方 (今は非推奨)
window.addEventListener("orientationchange", () => {
// 0: 縦向き、90 と -90: 横向き、180: 上下逆さま
console.log(window.orientation);
});
この方法には、端末やブラウザーによる違いが多くありました。
orientationchangeとresizeのどちらが先に起きるかが、端末によって違ったorientationchangeが起きた時点で、window.innerWidthなどの画面の大きさが、まだ向きを変える前の値のままのことがあったwindow.orientationの 0 が縦向きとは限らなかった。もともと横長の画面のタブレットでは、横向きが 0 になる機種があった
そのため、イベントのあとに setTimeout() で少し待ってから画面の大きさを読む、といった回避策が使われていました。待つ時間は機種ごとに試して決めるしかなく、確実な方法ではありませんでした。
window.orientation と orientationchange は、今も一部のブラウザーで動きますが、非推奨とされています。新しく書くコードでは使いません。
今の方法 1: screen.orientation
画面の向きを知るための標準の API が、Screen Orientation API です。screen.orientation の type に向きの種類が、angle に回転の角度が入ります。向きが変わると、change イベントが起きます。
function showOrientation() {
const { type, angle } = screen.orientation;
// type: "portrait-primary"、"landscape-primary" など
// angle: 0、90、180、270 のどれか
console.log(type, angle);
}
if (screen.orientation) {
showOrientation();
screen.orientation.addEventListener("change", showOrientation);
}
window.orientation では横向きが 90 と -90 でしたが、angle は 0〜359 の範囲の値 (0、90、180、270) になります。古いブラウザーには screen.orientation が無いことがあるので、上の例のように有るかどうかを確かめてから使います。
なお、screen.orientation.lock() で向きを固定する機能もありますが、全画面表示のときだけ使えるなどの条件があり、対応していないブラウザーもあります。
今の方法 2: matchMedia と resize
知りたいのが「端末の向き」ではなく「表示領域が縦長か横長か」なら、メディアクエリを使うほうが確実です。orientation メディア特性は、表示領域の高さが幅以上なら portrait、そうでなければ landscape になります。
const portrait = matchMedia("(orientation: portrait)");
function applyOrientation() {
document.documentElement.dataset.orientation = portrait.matches ? "portrait" : "landscape";
}
applyOrientation();
portrait.addEventListener("change", applyOrientation);
これは端末を回したときだけでなく、パソコンでウィンドウの大きさを変えたときや、タブレットで画面を分割したときにも働きます。ブラウザーによっては、ソフトウェアキーボードを出したときに表示領域が縮み、横長と判定されることもあります。
表示領域の大きさに合わせて描き直したいだけなら、向きにこだわらず resize イベントで大きさを読みます。resize は続けて何度も起きることがあるので、requestAnimationFrame() でまとめます。
let scheduled = false;
window.addEventListener("resize", () => {
if (scheduled) return;
scheduled = true;
requestAnimationFrame(() => {
scheduled = false;
// ここでは、向きを変えたあとの大きさが読める
// resizeGame() は、画面の大きさに合わせて描き直す処理 (別に用意しておく)
resizeGame(window.innerWidth, window.innerHeight);
});
});
特定の要素の大きさが変わったことを知りたいなら、ResizeObserver を使う方法もあります。
レイアウトはできるだけ CSS で済ませる
見た目を変えるだけなら、JavaScript は要りません。CSS のメディアクエリで、表示領域の向きや幅に合わせて指定を切り替えます。
/* 横長の表示領域では、2 列に並べる */
@media (orientation: landscape) {
.gallery {
display: grid;
grid-template-columns: 1fr 1fr;
}
}
/* iOS の Safari で、横向きにしたときに文字が勝手に大きくならないようにする */
html {
-webkit-text-size-adjust: 100%;
text-size-adjust: 100%;
}
多くの場合は、向きよりも幅で切り替えるほうが、さまざまな画面の大きさに対応できます。iOS の Safari では、横向きにすると文字が自動で拡大されることがあり、text-size-adjust で止められます。スマートフォンのブラウザーに特有の扱いは、スマートフォンのカテゴリーのほかの記事でも取り上げています。
まとめ
orientationchangeとwindow.orientationは非推奨。端末によってイベントの順番や、その時点の画面の大きさが違った- 画面の向きは
screen.orientationのtypeとangle、changeイベントで知る - 表示領域が縦長か横長かは、
matchMedia("(orientation: portrait)")のchangeイベントで知る - 大きさに合わせて描き直すだけなら、
resizeをrequestAnimationFrame()でまとめて処理する - レイアウトはできるだけ CSS のメディアクエリで済ませる