スマートフォンでアプリを使っていると、時々デバイスの傾きで画面内もお洒落に傾く事があるかと思います。
Webでも出来るかなと思い調べたところ「DeviceOrientationEvent」というものが見付かりましたので、簡単なデモを作成してみました。
今回は手軽にwebpackとBabelを使用しておりますのでモダンな記法が多く見られるかと思いますが、最後にファイル一式ダウンロード出来ますのでぜひぜひ読んでみて下さい。
目次
今回の完成系はこちら!
主題はSPでの傾き検知になりますが、PCを考慮してマウスの動きにも対応。
また効果をわかりやすく、かつお洒落に演出するためテキストと背景に別々の係数を掛けて視差効果を表現しています。
はじめに
今回の検証環境は以下になります。
Android バーション7.0 HUAWEI P9 lite
iPhone 7 バージョン12.4.1
PCはモダンブラウザ/IE11以降
※注意点として、iPhoneの場合iOS12.2からモーションと画面の向きのアクセスを許可する必要があります。
「Safari」 → 「モーションと画面の向きのアクセス」にチェックを入れて下さい。
また、iOS13からさらに実装方法が変わっているようですので、そちらをご使用の方はもしかすると動作しません。
iOS13はクリエイターも頭を抱える変更が多いため、SPを考慮したビジュアル前提のコーディングはまだまだ実務向きではないと感じています。
Webを少し豊かにする知識として予めご了承下さい。
参考:https://qiita.com/nakakaz11/items/a9be602874bd54819a18
背景のCanvasアニメーションは下記のサイトを参考にさせて頂きました。
扱う記事がどれも魅力的で素敵なおすすめサイトです。
参考:Animated Background Headers | Codrops
今回のゴール
デバイスを傾けると背景とタイトルが滑らかに動く事が今回のゴールになります。
また、最低限の実務レベルに持っていくためIEと傾き検知に非対応なデバイスでも見れるようにしましょう。
Chromeの検証ツールが優秀です。
制作にあたって、最後にはもちろん実機で確認しますが常に実機片手で制作するのも時間が掛かります。
そんな時はChromeの検証ツールがおすすめです。
サンプルページから検証ツール(Win: F12)を開き、メニューから「Sensors」を開きます。
見付からない場合はRun command(Win: Ctrl+Shift+P)を選択し「show sensors」と入力してみて下さい。
Sensorsメニューの「Orientation」がデバイスの向きを検証するツールになりますので、こちらをOffから適当な値に変更します。
そうすると、Webページが動いたのが確認出来たかと思います。
あまり使われない機能で今回初めて知った方もいると思いますが、Chromeにはこういうマイナーな機能も数多くありますので、使いこなして制作を効率的に行いましょう。
Chromeの主な検証ツールについて、弊社でまとめたものがありますので興味のある方はご一読下さい。
今すぐコーディング爆速化!プロ仕様なChrome検証ツールの使い方
それでは解説いきます。
傾きを検知する「DeviceOrientationEvent」
今回の主役です。
DeviceOrientationEventは方向を検知するイベントで、下記の4つの値を持ちます。
● alpha → z軸を中心としたデバイスの動きを0~360の範囲で表す
● beta → x軸を中心としたデバイスの動きを-180~180の範囲で表す
● gamma → y軸を中心としたデバイスの動きを-90~90の範囲で表す
● absolute
こちらは上記の3つと違いtrueかfalseの値を返します。
調べたところ「デバイスが方位データを絶対的に提供しているかを示す」ということですが、具体的な理解が難しく「地球に対しての向きを正確に返すか?」…というニュアンスに感じました。
今回はユーザーがページを訪問したタイミングで初期化を行うため、こちらは使用しておりません。
参考:https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/absolute
今回は縦方向の傾きをbeta、横方向の傾きをgammaで判定しています。
また、計算しやすいようにbetaの範囲をgammaと同じ-90~90の範囲に丸めています。
[JS]
1 2 3 4 5 6 7 8 | _deviceorientation(e) { let position = { x: e.gamma, y: Math.max(-90, Math.min(e.beta, 90)) } ~ this.diff = position; } |
マウスによるイベントも同様に中心を0として、-90~90の範囲になるように値を取得します。
扱いやすくpositionを整形したら、値をthis.diffに入れるまでがこちらの処理です。
実際の移動はrequestAnimationFrameによる_render関数の繰り返しで行います。
requestAnimationFrameについての説明は省略しますが、繰り返して処理を行うんだな…という考えで大丈夫です。
滑らかに動かす小技のようなもの
ループ処理でレンダリングしてマウスや傾きの値を使用していますが、そのまま取得された値を使用するとマウスの動きと全く一緒に要素が動くため少しもさっとします。
そのため、緩急をつけて動かすために以下のような処理を行います。
こうすることで「使用する値」を徐々に「取得された値」に近づけることが出来ます。
文章で説明するとわかりづらいかと思いますので、実際に値を代入して調べると以下のような感じです。
使用する値 = 0
取得された値 = 10
0.2の変化率で「取得された値」に徐々に近づけたい場合
0 += (10 – 0) * 0.2 = 5
5 += (10 – 5) * 0.2 = 6
6 += (10 – 6) * 0.2 = 6.8
6.8 += (10 – 6.8) * 0.2 = 7.44
…
9.99 += (10 – 9.99) * 0.2 = 9.992
となり、限りなく取得された値に近付かせることが出来ます。
変化率が1に近付くほど値の変化も早くなりますので、実際に動かしながら調整すると良いでしょう。
今回のコードは以下になります。
[JS]
1 2 3 4 5 6 7 | _render() { this.position.x += (this.diff.x - this.position.x) * 0.03; this.position.y += (this.diff.y - this.position.y) * 0.03; this.nodeList.forEach(x => { x.updated(this.position); }); } |
少しの変化で視差効果を
使用するthis.positionの値が決まったら「updated関数」が実行されます。
updated関数が含まれている「this.nodeList」にはあらかじめ「要素」と「設定」を含めたインスタンスを生成して配列にまとめておき、関数が実行されるとそれぞれの要素にtransformが設定されます。(id=”canvas”に対してquerySelectorAllを使用しているのは、複数ある「.js-title」と同様にNodeListで取得したかったからですが…同じ形に保って後を楽にする書き方が結構大事なのかなと個人的には思っています)
[JS]
1 2 3 4 5 6 7 8 9 10 11 12 | const node01 = document.querySelectorAll('.js-title'); const node02 = document.querySelectorAll('#canvas'); this.nodeList.push(new Item({ elm: node01, k: 0.8 })); this.nodeList.push(new Item({ elm: node02, k: 0.3 })); ~ updated({x, y}) { const kx = Math.round(x * 10 * this.k) / 10; const ky = Math.round(y * 10 * this.k) / 10; [...this.elm].forEach(node => { node.style.transform = `translateX(${kx}px) translateY(${ky}px)`; }); } |
ポイントはプロパティ「k」の差で、transformを設定する前にpositionのデータに掛けています。
それぞれ違う値を掛けることで変化に差が付き、視差効果が表現出来ます。
こちらも良く使えるテクニックかなと思いますので、ぜひぜひ自身のWeb制作に役立てて下さい。
まとめ
ということで、傾きを検知してから要素を動かすまでの流れは以上になります。
ポイントとしては、マウスを動かす・デバイスを傾ける処理そのものに要素を動かす処理まで入れてしまうと、予期せぬ不具合が起こる事があります。
(例えばスクロールによく見られますが、スクロールイベントに要素を表示する記述があるとページを開いた直後は要素が非表示の状態になります。その場合はページを表示した際にも判定を追加する対応が必要です)
requestAnimationFrameなどで常にループ処理でレンダリングを行い、マウスを動かす・デバイスを傾けるイベントは値を設定するだけに留めることで安定した動作になる場合が多いので、方法のひとつとして押さえておいても良いと思います。
今回のファイルを一式ダウンロード出来ますので、宜しければ遊んでみて下さい。
(srcファイル内をトランスパイルしているため、変更の際にはnpmとnodeが必要です)
記事を書き終わった頃に自身のiPhoneをアップデートしたため、傾き検知が動作しなくなりました。
今回の総括として「傾き検知は夢があるけどiPhoneの壁が邪魔をする」です。
マウスではしっかり動き続けますので、がっかりはしませんが不要なコストが掛かりますね。
それでは。