294 lines
11 KiB
HTML
294 lines
11 KiB
HTML
<html>
|
|
|
|
<head>
|
|
<link rel="icon" href="./demo/favicon.ico">
|
|
<link rel="stylesheet" type="text/css" href="./demo/global.css">
|
|
<style>
|
|
#myCanvas {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
.viewpoint {
|
|
opacity: 0.8;
|
|
cursor: pointer;
|
|
top: 0px;
|
|
left: 0px;
|
|
}
|
|
.viewpoint-label {
|
|
opacity: 0.9;
|
|
cursor: pointer;
|
|
position: absolute;
|
|
left: -30;
|
|
color: white;
|
|
background-color: #000000bb;
|
|
border-radius: 13px;
|
|
/* border: 1px solid white; */
|
|
text-align: center;
|
|
vertical-align: middle;
|
|
font-size: 25px;
|
|
padding: 6px 10px;
|
|
white-space: nowrap;
|
|
}
|
|
.viewpoint-arrow {
|
|
width: 64px;
|
|
height: 64px;
|
|
opacity: 0.9;
|
|
cursor: pointer;
|
|
position: absolute;
|
|
top: 12px;
|
|
left: -36px;
|
|
background-image: url('./demo/images/arrow.png');
|
|
background-position: calc(100%) calc(100% - 128px);
|
|
background-size: cover;
|
|
transform: scale(1.2);
|
|
}
|
|
.viewpoint-arrow-left {
|
|
width: 64px;
|
|
height: 64px;
|
|
opacity: 0.9;
|
|
cursor: pointer;
|
|
position: absolute;
|
|
top: 12px;
|
|
left: -36px;
|
|
background-image: url('./demo/images/arrow_left.png');
|
|
background-position: calc(100%) calc(100% - 128px);
|
|
background-size: cover;;
|
|
transform: scale(1.2);
|
|
}
|
|
.viewpoint-arrow-right {
|
|
width: 64px;
|
|
height: 64px;
|
|
opacity: 0.9;
|
|
cursor: pointer;
|
|
position: absolute;
|
|
top: 12px;
|
|
left: -36px;
|
|
background-image: url('./demo/images/arrow_right.png');
|
|
background-position: calc(100%) calc(100% - 128px);
|
|
background-size: cover;;
|
|
transform: scale(1.2);
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div id="app">
|
|
<div id="myCanvas" class="container"></div>
|
|
</div>
|
|
<script type="module">
|
|
import { VRViewer } from "./demo/libs/gemini-viewer.esm.min.js";
|
|
|
|
const viewpoints = [{
|
|
panoramas: [{
|
|
id: "panorama_1",
|
|
images: [
|
|
"./demo/images/vr/album_0/客厅/right.jpg",
|
|
"./demo/images/vr/album_0/客厅/left.jpg",
|
|
"./demo/images/vr/album_0/客厅/top.jpg",
|
|
"./demo/images/vr/album_0/客厅/bottom.jpg",
|
|
"./demo/images/vr/album_0/客厅/front.jpg",
|
|
"./demo/images/vr/album_0/客厅/back.jpg",
|
|
],
|
|
thumbnails: [],
|
|
}],
|
|
id: "viewpoint_1",
|
|
name: "客厅",
|
|
position: [0, 1, 0],
|
|
initialDirection: [0, 0, -1],
|
|
hotpoints: [{
|
|
targetViewpointId: "viewpoint_2",
|
|
hotpointId: "hotpoint_11",
|
|
anchorPosition: [1.11, -1.19, -10.00],
|
|
html: "",
|
|
}, {
|
|
targetViewpointId: "viewpoint_4",
|
|
hotpointId: "hotpoint_12",
|
|
anchorPosition: [4.17, 1.56, -10.00],
|
|
html: "",
|
|
arrowClass: "viewpoint-arrow-right",
|
|
}],
|
|
}, {
|
|
panoramas: [{
|
|
id: "panorama_2",
|
|
images: [
|
|
"./demo/images/vr/album_0/客餐厅/right.jpg",
|
|
"./demo/images/vr/album_0/客餐厅/left.jpg",
|
|
"./demo/images/vr/album_0/客餐厅/top.jpg",
|
|
"./demo/images/vr/album_0/客餐厅/bottom.jpg",
|
|
"./demo/images/vr/album_0/客餐厅/front.jpg",
|
|
"./demo/images/vr/album_0/客餐厅/back.jpg",
|
|
],
|
|
thumbnails: [],
|
|
}],
|
|
id: "viewpoint_2",
|
|
name: "客餐厅",
|
|
position: [0, 1, -3],
|
|
initialDirection: [0, 0, -1],
|
|
hotpoints: [{
|
|
targetViewpointId: "viewpoint_1",
|
|
hotpointId: "hotpoint_21",
|
|
anchorPosition: [-5.59, -3.36, 10.00],
|
|
html: "",
|
|
}, {
|
|
targetViewpointId: "viewpoint_3",
|
|
hotpointId: "hotpoint_22",
|
|
anchorPosition: [-1.75, -0.46, -10.00],
|
|
html: "",
|
|
}, {
|
|
targetViewpointId: "viewpoint_4",
|
|
hotpointId: "hotpoint_23",
|
|
anchorPosition: [4.26, 1.53, -13.00],
|
|
html: "",
|
|
arrowClass: "viewpoint-arrow-right",
|
|
}, {
|
|
targetViewpointId: "viewpoint_5",
|
|
hotpointId: "hotpoint_24",
|
|
anchorPosition: [-10.00, 0.5, -11.52],
|
|
html: "",
|
|
arrowClass: "viewpoint-arrow-left",
|
|
}],
|
|
}, {
|
|
panoramas: [{
|
|
id: "panorama_3",
|
|
images: [
|
|
"./demo/images/vr/album_0/餐厅/right.jpg",
|
|
"./demo/images/vr/album_0/餐厅/left.jpg",
|
|
"./demo/images/vr/album_0/餐厅/top.jpg",
|
|
"./demo/images/vr/album_0/餐厅/bottom.jpg",
|
|
"./demo/images/vr/album_0/餐厅/front.jpg",
|
|
"./demo/images/vr/album_0/餐厅/back.jpg",
|
|
],
|
|
thumbnails: [],
|
|
}],
|
|
id: "viewpoint_3",
|
|
name: "餐厅",
|
|
position: [0, 1, -6],
|
|
initialDirection: [0, 0, -1],
|
|
hotpoints: [{
|
|
targetViewpointId: "viewpoint_4",
|
|
hotpointId: "hotpoint_1",
|
|
anchorPosition: [10.00, 0.21, -3.56],
|
|
html: "",
|
|
}, {
|
|
targetViewpointId: "viewpoint_5",
|
|
hotpointId: "hotpoint_2",
|
|
anchorPosition: [-10.00, 0.0, -6.05],
|
|
html: "",
|
|
arrowClass: "viewpoint-arrow-left",
|
|
}],
|
|
}, {
|
|
panoramas: [{
|
|
id: "panorama_4",
|
|
images: [
|
|
"./demo/images/vr/album_0/主卧/right.jpg",
|
|
"./demo/images/vr/album_0/主卧/left.jpg",
|
|
"./demo/images/vr/album_0/主卧/top.jpg",
|
|
"./demo/images/vr/album_0/主卧/bottom.jpg",
|
|
"./demo/images/vr/album_0/主卧/front.jpg",
|
|
"./demo/images/vr/album_0/主卧/back.jpg",
|
|
],
|
|
thumbnails: [],
|
|
}],
|
|
id: "viewpoint_4",
|
|
name: "主卧",
|
|
position: [5, 1, 1],
|
|
initialDirection: [0, 0, -1],
|
|
hotpoints: [{
|
|
targetViewpointId: "viewpoint_1",
|
|
hotpointId: "hotpoint_1",
|
|
anchorPosition: [1.82, 0.47, -10.00],
|
|
html: "",
|
|
arrowClass: "viewpoint-arrow-left",
|
|
}],
|
|
}, {
|
|
panoramas: [{
|
|
id: "panorama_5",
|
|
images: [
|
|
"./demo/images/vr/album_0/小孩房/right.jpg",
|
|
"./demo/images/vr/album_0/小孩房/left.jpg",
|
|
"./demo/images/vr/album_0/小孩房/top.jpg",
|
|
"./demo/images/vr/album_0/小孩房/bottom.jpg",
|
|
"./demo/images/vr/album_0/小孩房/front.jpg",
|
|
"./demo/images/vr/album_0/小孩房/back.jpg",
|
|
],
|
|
thumbnails: [],
|
|
}],
|
|
id: "viewpoint_5",
|
|
name: "小孩房",
|
|
position: [-3, 1, 0],
|
|
initialDirection: [-1, 0, 0],
|
|
hotpoints: [{
|
|
targetViewpointId: "viewpoint_2",
|
|
hotpointId: "hotpoint_1",
|
|
anchorPosition: [-3.51, -0.43, -10.00],
|
|
html: "",
|
|
}],
|
|
}];
|
|
const hotpoints = [];
|
|
viewpoints.forEach(viewpoint => {
|
|
viewpoint.hotpoints.forEach(hotpoint => {
|
|
if (!hotpoint.html) {
|
|
if (hotpoint.targetViewpointId) {
|
|
const linkedViewpoint = viewpoints.find(vp => vp.id === hotpoint.targetViewpointId);
|
|
hotpoint.html = `
|
|
<div class="viewpoint">
|
|
<div class="viewpoint-label">${linkedViewpoint.name}</div>
|
|
<div class=${hotpoint.arrowClass || "viewpoint-arrow"}></div>
|
|
</div>`;
|
|
} else {
|
|
const linkedHotpoint = hotpoints.find(hp => hp.hotpointId === hotpoint.hotpointId);
|
|
hotpoint.html = (linkedHotpoint && linkedHotpoint.html) || "";
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
const config = {
|
|
containerId: "myCanvas",
|
|
}
|
|
const viewer = new VRViewer(config);
|
|
viewer.onHotpointClicked = (hotpoint) => {
|
|
// there can be a better way to distinguish if a hotpoint is a viewpoint!
|
|
if (hotpoint.targetViewpointId) {
|
|
const viewpoint = viewpoints.find((vp) => vp.id === hotpoint.targetViewpointId);
|
|
const panoramaId = viewpoint.panoramas[0].id;
|
|
// If it's gonna adjust camera direction by viewpoint's initialDirection when switching viewpoints.
|
|
// Set it to false in case we want to keep camera direction unchanged.
|
|
// Switching between panoramas of the same viewpoint won't trigger camera direction reset.
|
|
const setCameraToInitialDirection = true;
|
|
viewer.activatePanoramaById(hotpoint.targetViewpointId, panoramaId, setCameraToInitialDirection, (viewpoint) => {
|
|
console.log(`[Demo] Swithced to viewpoint '${viewpoint.name}', panorama '${panoramaId}'`);
|
|
});
|
|
} else {
|
|
viewer.lookToPosition(hotpoint.anchorPosition);
|
|
}
|
|
}
|
|
viewer.setViewpoints(viewpoints);
|
|
viewer.activatePanoramaById(viewpoints[0].id, viewpoints[0].panoramas[0].id);
|
|
|
|
// Implement animation for the arrow. The arrow.png is made by 25 sub images,
|
|
// with 128x128 size. We'll play 25 images in several seconds
|
|
const width = 128;
|
|
const numPictures = 25;
|
|
let currPictureIdx = 0;
|
|
setInterval(() => {
|
|
const arrowDivs = [];
|
|
["viewpoint-arrow", "viewpoint-arrow-left", "viewpoint-arrow-right"].forEach(cls => {
|
|
arrowDivs.push(...document.getElementsByClassName(cls));
|
|
});
|
|
for (let i = 0; i < arrowDivs.length; ++i) {
|
|
arrowDivs[i].style.backgroundPosition = `calc(100%) calc(100% - ${width * currPictureIdx}px)`;
|
|
}
|
|
currPictureIdx = (currPictureIdx + 1) % numPictures;
|
|
}, 2000 / numPictures);
|
|
|
|
window.addEventListener("keydown", (e) => {
|
|
// press "F1" to unlimit controls and show all assets
|
|
if (e.code === "Digit1") {
|
|
viewer.unlimitControlsAndShowAssets();
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
|
|
</html> |