JSとCSSでデバイスの傾きを検知してホログラムを再現する

2023/11/07に公開

さいしょに

こんにちは つきやま です。
今回はデバイスの傾きを検知してホログラムを再現したので記事にしました。
デバイスの傾きの検知の仕方や、使用したCSSの関数についてまとめています。

デモ&ソースコード

https://twitter.com/tyuyakima/status/1713122309375209839

https://twitter.com/tyuyakima/status/1713122551860535496

下記ページで公開しています。
https://hologram-sample-56f28.web.app/

ソースコード

Nuxt3とスタイリングにTailwind CSSを使用しました。

index.vue
<script setup lang="ts">
const alpha = ref<number | null>(null);
const beta = ref<number | null>(null);
const gamma = ref<number | null>(null);

/**
 * refにイベントで取得した傾きを格納する
 * @param e DeviceOrientationEvent
 */
const handleOrientation = (e: DeviceOrientationEvent) => {
  alpha.value = e.alpha;
  beta.value = e.beta;
  gamma.value = e.gamma;
};

/**
 * gammaの度数を算出する
 */
const gammaDeg = computed(() => {
  if (!gamma.value) return "0deg";
  return `${(gamma.value + 90) * 2}deg`;
});

/**
 * ユーザーの許可ボタン
 */
const requestDeviceOrientationPermission = () => {
  if (
    DeviceOrientationEvent &&
    typeof DeviceOrientationEvent.requestPermission === "function"
  ) {
    DeviceOrientationEvent.requestPermission()
      .then((permissionState: string) => {
        if (permissionState === "granted") {
          window.addEventListener(
            "deviceorientation",
            (e: DeviceOrientationEvent) => {
              handleOrientation(e);
            }
          );
        }
      })
      .catch(console.error);
  }
};
</script>

<template>
  <div class="space-y-4">
    <div class="flex h-[50vh] justify-center items-center">
      <div
        class="hologram w-[300px] h-[300px] flex items-center justify-center bg-[length:30px_30px]"
      >
        <img
          src="画像URL"
          width="300"
        />
      </div>
    </div>
    <div class="text-center">
      <p>alpha: {{ Math.round(alpha ?? 0) ?? "-" }}</p>
      <p>beta: {{ Math.round(beta ?? 0) ?? "-" }}</p>
      <p>gamma: {{ Math.round(gamma ?? 0) ?? "-" }}</p>
    </div>
    <div class="flex justify-center">
      <button
        @click="requestDeviceOrientationPermission"
        class="text-base bg-emerald-400 rounded-md px-6 py-3 text-white cursor-pointer"
      >
        傾きを検知する
      </button>
    </div>
  </div>
</template>

<style scoped>
.hologram {
  background-image: repeating-conic-gradient(
    from v-bind(gammaDeg),
    rgb(0, 34, 255) 0%,
    rgb(0, 255, 242) 25%,
    rgb(255, 255, 255) 50%,
    rgb(0, 255, 242) 75%,
    rgb(0, 34, 255) 100%
  );
  filter: drop-shadow(4px 4px 4px rgb(0, 0, 0));
}
</style>

ソースコードもGitHubで公開しているので興味がある方はこちらからご確認ください。

https://github.com/tsukiyama-3/hologram-sample

実装

デバイス傾斜感知

JavaScriptではDeviceOrientationEventというデバイスの傾きを検知するイベントが用意されています。

DeviceOrientationEventイベント

DeviceOrientationEventは、デバイスが物理的にどのように傾いているか(回転しているか)を示すイベントをブラウザで扱うための Web API です。

こちらのイベントを使用するにはイベントの許可を必要とします。

こちらの記事を参考に実装しました。

https://qiita.com/okumura_daiki/items/16a09c9c0d0b2509d261

sample

このようなダイアログが表示され、「許可」を押すとデバイスの傾きを取得できるようになります。

こちらが公式ドキュメントになります。

DeviceOrientationEvent イベントは 4 つの値を持ちます。

  • absolute
  • alpha
  • beta
  • gamma

一つずつ確認していきましょう

absolute

デバイスが提供するオリエンテーションデータが絶対的なものであるか、相対的なものであるかを示す真偽値です。

  • true
    デバイスのオリエンテーションデータは地球の重力に対して絶対的なものであると言えます。つまり、地球の表面に対して水平または垂直など、絶対的な基準に基づいています。

  • false
    データはデバイスに対して相対的なものであり、デバイスが初めてDeviceOrientationEventを送信したときのオリエンテーションを基準にしています。

alpha

デバイスが z 軸周り(デバイスが直立しているときに上に向いている線)に沿って回転している角度を表す(0 から 360 までの)数値。

beta

x 軸周り(デバイスの側面)の前後の傾きを表す角度(-180 から 180 までの数値)。

gamma

y 軸周り(デバイスの上下の側面)の左右の傾きを表す角度(-90 から 90 までの数値)。

図で表すとこんな感じです。

sample

conic-gradient()

ホログラムの模様を再現するためにconic-gradient()というCSS関数を使用しました。

conic-gradient()は円錐形のグラデーションを作成するために使われます。

よく見るところで言うとカラーピッカーでも使用されています。

https://www.selecolor.com/wheel-color-picker/

使い方

conic-gradient()の引数にグラデーションの情報を渡します。

conic-gradient(from 開始角度 at 中心点, 色停止点, 色停止点, ...)

色停止点は任意のカラーフォーマットで指定でき、位置はパーセンテージか角度で指定できます。

例)red, yellow 50%, green

今回のホログラムの再現ではconic-gradientの開始角度をデバイスの傾きと連動するように実装しました。
CodePenでは<input type="range">で再現しました。

あとは、background-sizeでサイズを指定すると良い感じになります。

さいごに

思いついたその日に実装したので少し粗雑な部分もありますが、何かの参考にしてもらえたら幸いです。
今回使用したDeviceOrientationEventの他にもDeviceMotionEventと言うデバイスの加速度を取得できるWeb APIもあるので他にも面白いものが作れたら記事を書きたいと思っています。

参考文献

https://qiita.com/okumura_daiki/items/16a09c9c0d0b2509d261
https://developer.mozilla.org/ja/docs/Web/CSS/gradient/conic-gradient
https://developer.mozilla.org/ja/docs/Web/API/Device_orientation_events/Detecting_device_orientation

Discussion