360°の映像をブラウザから視点操作は、3Dの球体オブジェクトに
動画画像を貼り付けて、操作するイメージになります。
基本は、インストールなしで誰にでも見てもらえるような
コンテンツにしたいのでJavaScriptで作成された
ライブラリを使うことになります。
有名どころには、手軽に組み込める A-Frame https://aframe.io/
と、A-Frame のベースになっている Three.js https://threejs.org/
があります。
今回は、カスタマイズが色々可能である Three.js をベースに
作っていきたいと思います。
まず、全体の画面用のページを作成します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no"/>
<STYLE type="text/css">
* {
margin: 0;
padding: 0;
border: 0;
}
body, html {
overflow: hidden;
height: 100%;
}
#stage {
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.bt {
position: absolute;
color: #ffffff;
}
.bt1 {
left: 0px;
top: 0px;
}
.bt2 {
left: 40px;
top: 0px;
}
.bt a {
display: block;
position: absolute;
padding: 3px;
color: #ffffff;
}
.bt a:hover {
box-shadow: 0 0 45px rgba(255,255,255, 0.8);
}
</STYLE>
<script type="text/javascript" src="./js/three.min.js"></script>
<script type="text/javascript" src="./js/OrbitControls.js"></script>
<script type="text/javascript" src="./js/jquery.min.js"></script>
<script type="text/javascript" src="./js/hls.min.js"></script>
<SCRIPT>
var fov = 75;
var element;
var scene, camera, renderer, controls;
var sphere1;
var material1;
var texture1;
var onMouseDownMouseX = 0, onMouseDownMouseY = 0,
lon = 0, onMouseDownLon = 0,
lat = 0, onMouseDownLat = 0,
phi = 0, theta = 0;
var isFullScreen = false;
var video = document.createElement('video');
window.onload = function () {
init();
};
function init() {
if (Hls.isSupported()) {
var hls = new Hls();
hls.loadSource('./playlist.m3u8');
hls.attachMedia(video);
hls.on(Hls.Events.MANIFEST_PARSED, function() {
video.play();
});
} else {
console.log('his err');
}
var width = window.innerWidth;
var height = window.innerHeight;
scene = new THREE.Scene(); // シーンの作成
// リサイズイベントを検知してリサイズ処理を実行
window.addEventListener('resize', handleResize, false);
// カメラの作成
camera = new THREE.PerspectiveCamera(fov, width / height, 1, 1000);
camera.position.set(0, 0, 0);
scene.add(camera);
// 球体の形状を作成
var geometry = new THREE.SphereGeometry(5, 60, 40);
geometry.scale(-1, 1, 1);
// マテリアルの作成
material1 = initMaterial('', 1);
// 球体(形状)にマテリアル(質感)を貼り付けて物体を作成
sphere1 = new THREE.Mesh(geometry, material1);
// シーンに追加
scene.add(sphere1);
// レンダラーの作成
renderer = new THREE.WebGLRenderer();
// レンダラーをwindowサイズに合わせる
renderer.setSize(width, height);
renderer.setClearColor({color: 0x000000});
effect = new THREE.StereoEffect(renderer);
element = renderer.domElement;
document.getElementById('stage').appendChild(element);
renderer.render(scene, camera);
setOrbitControls(); // パソコンの場合、マウスドラッグで視点操作を可能にする
render();
}
// リサイズ処理
function handleResize() {
renderer.setSize(window.innerWidth, window.innerHeight);
effect.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
}
function getTexter(image) {
return THREE.ImageUtils.loadTexture(image);
}
function initMaterial(image, opacity) {
texture1 = new THREE.VideoTexture(video);
texture1.minFilter = THREE.LinearFilter;
material1 = new THREE.MeshBasicMaterial({ map: texture1 });
return material1;
}
function onDocumentMouseDown(event) {
event.preventDefault();
if (event.clientX) {
onMouseDownMouseX = event.clientX;
onMouseDownMouseY = event.clientY;
} else if (event.touches) {
onMouseDownMouseX = event.touches[0].clientX
onMouseDownMouseY = event.touches[0].clientY;
} else {
onMouseDownMouseX = event.changedTouches[0].clientX
onMouseDownMouseY = event.changedTouches[0].clientY
}
onMouseDownLon = lon;
onMouseDownLat = lat;
if (window.ontouchstart === null) { //タッチ出来たら
document.addEventListener('touchmove', onDocumentMouseMove, false);
document.addEventListener('touchend', onDocumentMouseUp, false);
} else {
document.addEventListener('mousemove', onDocumentMouseMove, false);
document.addEventListener('mouseup', onDocumentMouseUp, false);
}
}
function onDocumentMouseMove( event ) {
event.preventDefault();
if (event.clientX) {
var touchClientX = event.clientX;
var touchClientY = event.clientY;
} else if (event.touches) {
var touchClientX = event.touches[0].clientX
var touchClientY = event.touches[0].clientY;
} else {
var touchClientX = event.changedTouches[0].clientX
var touchClientY = event.changedTouches[0].clientY
}
lon = (touchClientX - onMouseDownMouseX) * -0.1 + onMouseDownLon;
lat = (touchClientY - onMouseDownMouseY) * -0.1 + onMouseDownLat;
}
function onDocumentMouseUp(event) {
if (window.ontouchstart === null) { // タッチ出来たら
document.removeEventListener('touchmove', onDocumentMouseMove, false);
document.removeEventListener('touchend', onDocumentMouseUp, false);
} else {
document.removeEventListener('mousemove', onDocumentMouseMove, false);
document.removeEventListener('mouseup', onDocumentMouseUp, false);
}
}
// パソコン閲覧時マウスドラッグで視点操作する
function setOrbitControls() {
controls = new THREE.OrbitControls(camera, element);
controls.target.set(
camera.position.x + 0.15,
camera.position.y,
camera.position.z
);
controls.enableDamping = true; // 視点操作のイージングをONにする
controls.dampingFactor = 0.2; // 視点操作のイージングの値
controls.rotateSpeed = 0.1; // 視点変更の速さ
controls.noZoom = false; // ズーム禁止
controls.noPan = false; // パン操作禁止
}
function render() {
lat = Math.max(-90, Math.min(90, lat));
phi = THREE.Math.degToRad(90 - lat);
theta = THREE.Math.degToRad(lon);
var angle = (theta * 360 / Math.PI);
if (angle > 360) {
var tmp = Math.floor(angle / 360);
if (tmp > 0) angle = angle - (360 * tmp);
} else if (angle < 0) {
var tmp = Math.floor(angle / 360);
if (tmp < 0) angle = angle + (360 * tmp * -1);
}
var deg = (theta * 360 / Math.PI) * -1;
if (deg > 180) {
var tmp = Math.floor(deg / 180);
deg = (180 * tmp * -1) + deg - 180;
if (tmp % 2 == 0) deg += 180;
} else if (deg < -180) {
var tmp = Math.floor(deg / 180) * -1;
deg = deg + (180 * (tmp * -1) * -1);
if (tmp % 2 > 0) deg -= 180;
}
$('#courseBox').css({
transform: 'rotate(' + (deg * -1) + 'deg)'
});
$('#arrowBox').css({
transform: 'rotate(' + (deg * -1) + 'deg)'
});
requestAnimationFrame(render);
renderer.render(scene, camera);
controls.update();
}
function setFullScreen() {
//var fulltarget = document.getElementsByClassName('fullsc')[0];
var fulltarget = document.getElementById('stage');
if (!isFullScreen) {
if (fulltarget.webkitRequestFullscreen) {
fulltarget.webkitRequestFullscreen(); // Chrome15+, Safari5.1+, Opera15+
} else if (fulltarget.mozRequestFullScreen) {
fulltarget.mozRequestFullScreen(); // FF10+
} else if (fulltarget.msRequestFullscreen) {
fulltarget.msRequestFullscreen(); // IE11+
} else if (fulltarget.requestFullscreen) {
fulltarget.requestFullscreen(); // HTML5 Fullscreen API仕様
} else {
alert('ご利用のブラウザはフルスクリーン操作に対応していません');
return;
}
$('#scrimg').attr('src', 'img/win.png');
isFullScreen = true;
} else {
if (document.webkitCancelFullScreen) {
document.webkitCancelFullScreen(); // Chrome15+, Safari5.1+, Opera15+
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen(); // FF10+
} else if (document.msExitFullscreen) {
document.msExitFullscreen(); // IE11+
} else if (document.cancelFullScreen) {
document.cancelFullScreen(); // Gecko:FullScreenAPI仕様
} else if (document.exitFullscreen) {
document.exitFullscreen(); // HTML5 Fullscreen API仕様
}
$('#scrimg').attr('src', 'img/full.png');
isFullScreen = false;
}
}
</SCRIPT>
</head>
<body>
<div id="stage">
<div class="bt bt2"><a href="#" onclick="setFullScreen();"><img id="scrimg" src="img/full.png" /></a></div>
</div>
</body>
</html>
