#TL;DR
・iOSのPWAでは表示モードがstandalone
の場合にカメラを開くことができない。
・Androidでの起動時にはstandalone
、iOSではbrowser
で起動する。
・ページアクセス時にuserAgentからOSを判定し、OSに応じて異なるmanifestを読み込む。
#カメラを開く最小限のPWA構成
ファイル構成は下記の通りです。
icon.pngは適当な192x192の画像を用意してください。
root/
├ icon.png
├ index.html
├ js/
│ └ camera.js
├ manifest.json
└ sw.js
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="manifest" href="manifest.json">
<link rel="apple-touch-icon" href="icon.png" sizes="192x192">
</head>
<body onload="openCamera();">
<video autoplay playsinline></video>
</body>
<script src="js/camera.js"></script>
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('sw.js').then(function(registration) {
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}).catch(function(err) {
console.log('ServiceWorker registration failed: ', err);
});
}
</script>
</html>
var CACHE_NAME = 'pwa-camera-test-caches';
var urlsToCache = ['/index.html', '/js/camera.js'];
self.addEventListener('install', function(event) {
event.waitUntil(
caches
.open(CACHE_NAME)
.then(function(cache) {
return cache.addAll(urlsToCache);
})
);
});
self.addEventListener('fetch', function(event) {
event.respondWith(
caches
.match(event.request)
.then(function(response) {
return response ? response : fetch(event.request);
})
);
});
{
"name": "pwa camera test",
"short_name": "PWA CAMERA",
"icons": [{
"src": "icon.png",
"sizes": "192x192",
"type": "image/png"
}],
"start_url": "index.html",
"display": "standalone"
}
function openCamera() {
var video = document.querySelector('video');
navigator.mediaDevices = navigator.mediaDevices
|| ((navigator.mozGetUserMedia
|| navigator.webkitGetUserMedia) ? {
getUserMedia: function(c) {
return new Promise(function(y, n) {
(navigator.mozGetUserMedia ||
navigator.webkitGetUserMedia).call(navigator, c, y, n);
});
}
} : null);
var constraints = { video: { facingMode: 'environment', width: 1280, height: 720 } };
navigator.mediaDevices.getUserMedia(constraints)
.then(function(stream) {
video.srcObject = stream;
video.onloadedmetadata = function(e) {
video.play();
};
})
.catch(function(err) {
console.log(err);
});
}
PWA参考:シンプルなPWAサンプルここに置いておきますね
カメラ参考:「MediaDevices.getUserMedia() 」について
これでカメラを開く最低限のPWAアプリの完成です。
https接続のできる場所にアップロードして動作を確認します。
今回はGithub Pagesを使用しました。
ブラウザ
iOS | Android |
---|---|
インストール(ホーム画面に追加)
iOS | Android |
---|---|
PWA
iOS | Android |
---|---|
冒頭で述べた通り、iOSでは表示モードがstandalone
の場合にカメラを開くことができません。
参考:PWA来てるからカメラアプリ作れるんじゃね?と思ったら失敗した話
表示モードをbrowser
にすると単にブラウザが起動されるため、iOSでもカメラを開くことができますが、本来standalone
で起動できるAndroidもブラウザでの起動になってしまいます。
そこで、表示モードがstandalone
とbrowser
のmanifestをそれぞれ用意し、OSによって読込先を変更することで、Androidでの起動時にはstandalone
、iOSではbrowser
で起動するようにします。
#OS別のmanifest読み込み
manifest.jsonと同階層に、表示モードをbrowser
に変更しただけのmanifest_ios.jsonを作成します。
また、index.htmlでuserAgentからOSを判別し、OSに応じて読み込むmanifestを変更します。
{
"name": "pwa camera test",
"short_name": "PWA CAMERA",
"icons": [{
"src": "icon.png",
"sizes": "192x192",
"type": "image/png"
}],
"start_url": "index.html",
"display": "browser"
}
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- スクリプトから追加するため削除 -->
<!-- <link rel="manifest" href="manifest.json"> -->
<link rel="apple-touch-icon" href="icon.png" sizes="192x192">
</head>
<body onload="openCamera();">
<video autoplay playsinline></video>
</body>
<script src="js/camera.js"></script>
<script>
// manifestのlinkタグを生成
function setManifest(path) {
const manifest = document.createElement('link');
manifest.rel = 'manifest';
manifest.href = path;
document.head.appendChild(manifest);
}
// OSに応じて読み込むmanifestを変更
var userAgent = navigator.userAgent.toLowerCase();
if (userAgent.indexOf("iphone") > 0 || userAgent.indexOf("ipad") > 0) {
setManifest('manifest_ios.json')
} else {
setManifest('manifest.json')
}
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('sw.js').then(function(registration) {
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}).catch(function(err) {
console.log('ServiceWorker registration failed: ', err);
});
}
</script>
</html>
これでホーム画面のアイコンから起動した時、AndroidではPWAのstandalone
として起動、iOSではSafariのbrowser
で起動するようになりました。
iOS/Android共にカメラを使用できるほか、AndroidではPWAのキャッシュ機能を使用できるためオフラインでも動作も可能です。
#まとめ
記事作成時点ではiOSのPWAのstandalone
ではカメラを起動できなかったため、manifestをOSに応じて差し替える事で、AndroidではPWAアプリとして動作させつつiOSでのカメラ起動を可能にしました。
iOSでもSafari以外からカメラにアクセスできるようになると嬉しいですね。
あまりスマートな解決策ではない気がしているため、より良い方法がありましたらご教授ください。