TECH SCAPE

AR関連多め(HoloLens, AzureKinect、SparkAR)

iOS Safariで3DTouchが動作すると映像が真っ暗になるバグを回避

※ この記事は撤退予定のQiitaから移植した内容です。

※ 2020年01月08日投稿

問題

以前8thWallを使用したWebARコンテンツを制作した際、以下のようなスクリーンショット機能を実装しました。

f:id:a_hancho:20200524221823g:plain
8thwall-camera-mock01

ボタンを押すと結果のモーダルが出て、長押しで保存してもらう形です。

画像をダウンロードしてほしいとき、PCではダウンロードボタン(a要素のdownload属性)を置くことが多いですが、スマートフォンではこの方法が主流と思われます。 iOS12以下でdownload属性が、別画面で画像を開く挙動になってしまうためですね。

しかし、 ① iOS12以下でimg要素を強く長押しした ② iOS13でimg要素を長押しした 場合に3DTouchが動作すると、映像が暗くなってしまう課題があることに気づきました。

f:id:a_hancho:20200524222111g:plain
8thwall-sample

調査したところ、これは8thWallに限らず、WebRTC全体で発生するバグのようです。

iPhoneをお持ちの方は以下のWebRTCサンプルページでOpen Cameraした後、上にあるWebRTC samplesリンクを3DTouchしてみてください。 https://webrtc.github.io/samples/src/content/getusermedia/gum/

解決策

3DTouch動作を防ぐことで、無理やり回避できる」ことがわかりました。

iOS12以下の場合

@katsuya_U さんのこちらの記事で解決しました。

以下は引用です。詳しくは上記リンクをご覧ください。

// 全てのimgタグに対して実行
document.querySelectorAll("img").forEach(el => el.addEventListener("touchforcechange", e => {
  if (!e.touches || !e.touches[0]) return

  if (e.touches[0].force > 0.15) {
    e.preventDefault()
  }
}))

3DTouchが発動する圧力を超えた時にpreventDefaultするという内容でした。Appleの意表をついた感じの実装でちょっと面白いです。

iOS13の場合

さて、当記事メインの問題はiOS13です。 iOS13から、3D Touchが廃止され、その主要機能が全iOSデバイスで利用可能になりました

これがどういうことかというと... 圧力に関わらず長く押していると3DTouch的な動きが起きてしまう ということです。ややこしい...。

しかし、逆にiOS13から嬉しい仕様が追加されました。それが、iOS12以下では微妙だったdownload属性の挙動変化です。 img要素を以下のようにa要素で囲ってみてください。

// pug
a.screenshot-link
    img.screenshot-img
// js
const $download = document.querySelector('.screenshot-link');
const $img = document.querySelector('.screenshot-img');

// 保存された画像ソースのパスを登録
const srcPath = 'data:image/jpeg;base64,xxxxx...';
$download.setAttribute('href', srcPath);
$img.setAttribute('src', srcPath);

// a要素にdownload属性追加
//上書き保存しないように、スクショごとに変わる名前が望ましい
$download.setAttribute('download', `hoge${date.now()}.jpg`);

タップすると画像ダウンロードできます。

f:id:a_hancho:20200524221823g:plain

注意点 初期設定ではカメラロールではなくファイルアプリのダウンロードフォルダに保存されるようです。 iOSユーザーには馴染みなく、「保存されていないじゃん!」という誤解を招くこともありました。

しばらくは、iOS13だけカメラ機能を切ってしまうという選択肢も視野に入れると良いかなと思います。

Android(おまけ)

img要素単体だろうがa要素で囲おうが、長押しで画像保存をすることができます。

ただし、img要素単体ですとダウンロード.jpgのように勝手に名前がつけられた挙句上書き保存されてしまいます。iOS13と同様の実装で、download要素で名前をつけてあげるのが親切かもしれません。