Bachelorarbeit/packages/modelviewer.dev/examples/annotations/index.html

738 lines
35 KiB
HTML

<!--
/* @license
* Copyright 2020 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-->
<!DOCTYPE html>
<html lang="en">
<head>
<title>&lt;model-viewer&gt; Annotations</title>
<meta charset="utf-8">
<meta name="description" content="&lt;model-viewer&gt; annotation examples">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link type="text/css" href="../../styles/examples.css" rel="stylesheet" />
<link type="text/css" href="../../styles/docs.css" rel="stylesheet" />
<link rel="shortcut icon" type="image/png" href="../../assets/favicon.png" />
<script defer src="https://web3dsurvey.com/collector.js"></script>
<script>
window.ga = window.ga || function () { (ga.q = ga.q || []).push(arguments) }; ga.l = +new Date;
ga('create', 'UA-169901325-1', { 'storage': 'none' });
ga('set', 'referrer', document.referrer.split('?')[0]);
ga('set', 'anonymizeIp', true);
ga('send', 'pageview');
</script>
<script async src='https://www.google-analytics.com/analytics.js'></script>
</head>
<body>
<div class="examples-page">
<div class="sidebar" id="sidenav"></div>
<div id="toggle"></div>
<div class="examples-container">
<div class="sample">
<div id="addHotspots" class="demo"></div>
<div class="content">
<div class="wrapper">
<h4><span class="font-medium">Annotations. </span></h4>
<p>
Use &lt;model-viewer&gt; to make your models interactive.
This page showcases how you can add hotspots to your scene. Child elements are hotspots if their slot
begins with "hotspot".
The data-position attribute specifies the 3D position of the hotspot in model coordinates, using the same
format as the
camera-target attribute. The data-normal attribute specifies the normal vector defining the "front" of the
hotspot.
When this normal is pointed away from the viewer, the hotspot's opacity becomes --min-hotspot-opacity. The
<a href="../../editor">Editor</a> lets you drag and drop your own models and add hotspots by clicking
and displays the corresponding position and normal attributes which you can copy into your own page.
</p>
<p>
You can also specify an attribute to be toggled when the hotspot changes between hidden and visible with
data-visibility-attribute.
For example, if you set data-visibility-attribute="visible", then &lt;model-viewer&gt; will toggle the
data-visible attribute on
that hotspot element. This makes it easier to write CSS that styles a hotspot based on its visibility.
</p>
<p>
When a hotspot's visibility changes, it will dispatch a "hotspot-visibility" event, and
event.detail.visible will be true or false
depending on whether the hotspot is facing the camera.
</p>
<div class="heading">
<h2 class="demo-title">Add hotspots</h2>
<h4></h4>
</div>
<example-snippet stamp-to="addHotspots" highlight-as="html">
<template>
<style>
.hotspot {
display: block;
width: 20px;
height: 20px;
border-radius: 10px;
border: none;
background-color: blue;
box-sizing: border-box;
pointer-events: none;
}
.hotspot[slot="hotspot-hand"] {
--min-hotspot-opacity: 0;
background-color: red;
}
.hotspot[slot="hotspot-foot"]:not([data-visible]) {
background-color: transparent;
border: 3px solid blue;
}
.annotation {
background-color: #888888;
position: absolute;
transform: translate(10px, 10px);
border-radius: 10px;
padding: 10px;
}
/* This keeps child nodes hidden while the element loads */
:not(:defined)>* {
display: none;
}
</style>
<model-viewer id="hotspot-demo" ar ar-modes="webxr" camera-controls touch-action="pan-y"
src="../../shared-assets/models/Astronaut.glb" poster="../../assets/poster-astronaut.webp"
tone-mapping="aces" shadow-intensity="1" alt="A 3D model of an astronaut.">
<button class="hotspot" slot="hotspot-visor" data-position="0 1.75 0.35" data-normal="0 0 1"></button>
<button class="hotspot" slot="hotspot-hand" data-position="-0.54 0.93 0.1"
data-normal="-0.73 0.05 0.69">
<div class="annotation">This hotspot disappears completely</div>
</button>
<button class="hotspot" slot="hotspot-foot" data-position="0.16 0.1 0.17"
data-normal="-0.07 0.97 0.23" data-visibility-attribute="visible"></button>
<div class="annotation">This annotation is fixed in screen-space</div>
</model-viewer>
</template>
</example-snippet>
</div>
</div>
</div>
<div class="sample">
<div id="animatedHotspots" class="demo"></div>
<div class="content">
<div class="wrapper">
<h4 id="intro"><span class="font-medium">Animated Hotspots. </span></h4>
<p>
Hotspots can follow a point on the surface of an animated model by
specifying the data-surface attribute instead of world-space
position and normal. You can use the <a
href="../../docs/index.html#entrydocs-annotations-methods-surfaceFromPoint">surfaceFromPoint</a>
method to get this data-surface string, or even easier, just load
your model in our <a href="https://modelviewer.dev/editor/">editor</a> and add the
hotspots there.
</p>
<p>
Hotspots can be queried to build custom overlays in screen space.
For example, by using an SVG canvas and updating the line nodes we
can draw label standoff lines.
</p>
<p>
By using WebXR mode, this markup works equally well in AR. Note that
because the text is not part of the 3D rendering it is easier to
read and more accessible.
</p>
<example-snippet stamp-to="animatedHotspots" highlight-as="html">
<template>
<model-viewer id="animation-demo" autoplay ar ar-modes="webxr" scale="0.01 0.01 0.01"
camera-orbit="90deg auto auto" shadow-intensity="1" camera-controls touch-action="pan-y"
src="../../shared-assets/models/Horse.glb" alt="A 3D model of a horse galloping.">
<div slot="hotspot-nose" class="anchor" data-surface="0 0 228 113 111 0.217 0.341 0.442"></div>
<div slot="hotspot-hoof" class="anchor" data-surface="0 0 752 733 735 0.132 0.379 0.489"></div>
<div slot="hotspot-tail" class="anchor" data-surface="0 0 220 221 222 0.405 0.061 0.534"></div>
<svg id="lines" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" class="lineContainer">
<line class="line"></line>
<line class="line"></line>
<line class="line"></line>
</svg>
<div id="container">
<button id="nose" class="label">Nose</button>
<button id="hoof" class="label">Hoof</button>
<button id="tail" class="label">Tail</button>
</div>
</model-viewer>
<script type="module">
const modelViewer1 = document.querySelector('#animation-demo');
const lines = modelViewer1.querySelectorAll('line');
let baseRect;
let noseRect;
let hoofRect;
let tailRect;
function onResize() {
const arStatus = modelViewer1.getAttribute('ar-status');
baseRect = (arStatus == "not-presenting" || arStatus == "failed") ?
modelViewer1.getBoundingClientRect() : new DOMRect();
noseRect = document.querySelector('#nose').getBoundingClientRect();
hoofRect = document.querySelector('#hoof').getBoundingClientRect();
tailRect = document.querySelector('#tail').getBoundingClientRect();
}
window.addEventListener("resize", onResize);
modelViewer1.addEventListener('ar-status', (event) => {
lines.forEach((element) => {
if (event.detail.status !== 'session-started') {
element.classList.remove('hide');
} else {
element.classList.add('hide');
}
});
onResize();
});
modelViewer1.addEventListener('load', () => {
onResize();
// update svg
function drawLine(svgLine, name, rect) {
const hotspot = modelViewer1.queryHotspot('hotspot-' + name);
svgLine.setAttribute('x1', hotspot.canvasPosition.x);
svgLine.setAttribute('y1', hotspot.canvasPosition.y);
svgLine.setAttribute('x2', (rect.left + rect.right) / 2 - baseRect.left);
svgLine.setAttribute('y2', rect.top - baseRect.top);
}
// use requestAnimationFrame to update with renderer
const startSVGRenderLoop = () => {
drawLine(lines[0], 'nose', noseRect);
drawLine(lines[1], 'hoof', hoofRect);
drawLine(lines[2], 'tail', tailRect);
requestAnimationFrame(startSVGRenderLoop);
};
startSVGRenderLoop();
});
</script>
<style>
.anchor {
display: none;
}
.lineContainer {
pointer-events: none;
display: block;
}
.line {
stroke: #16a5e6;
stroke-width: 2;
stroke-dasharray: 2
}
#container {
position: absolute;
display: flex;
justify-content: space-evenly;
bottom: 8px;
left: 8px;
width: 100%;
}
.label {
background: #fff;
border-radius: 4px;
border: none;
box-sizing: border-box;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
color: rgba(0, 0, 0, 0.8);
display: block;
font-family: Futura, Helvetica Neue, sans-serif;
font-size: 18px;
font-weight: 700;
max-width: 128px;
padding: 0.5em 1em;
bottom: 10px;
pointer-events: none;
}
#animation-demo::part(default-ar-button) {
bottom: 64px;
}
/* This keeps child nodes hidden while the element loads */
:not(:defined)>* {
display: none;
}
</style>
</template>
</example-snippet>
</div>
</div>
</div>
<div class="sample">
<div id="dimensions" class="demo"></div>
<div class="content">
<div class="wrapper">
<h4><span class="font-medium">Show Dimensions. </span></h4>
<p>
Hotspots don't have to be on the model; they can be at arbitrary
points in 3D space. Here we demonstrate using hotspots to show a
model's dimensions, entirely styled with CSS. A JavaScript function
is used to automatically set the dimension values by querying the
&lt;model-viewer&gt; element.
</p>
<p>
Hotspots can be queried to build custom overlays in screen space.
For example, by using an SVG canvas and updating the line nodes we
can draw rudimentary dimension lines. Note that this kind of
extended functionality cannot support occlusion in the 3D scene, as
it always draws on top of the model.
</p>
<p>
By using WebXR mode, this markup works equally well in AR. Note that
because the text is not part of the 3D rendering it is easier to
read and more accessible.
</p>
<example-snippet stamp-to="dimensions" highlight-as="html">
<template>
<model-viewer id="dimension-demo" ar ar-modes="webxr" ar-scale="fixed" camera-orbit="-30deg auto auto"
max-camera-orbit="auto 100deg auto" shadow-intensity="1" camera-controls touch-action="pan-y"
src="../../assets/ShopifyModels/Chair.glb" alt="A 3D model of an armchair.">
<button slot="hotspot-dot+X-Y+Z" class="dot" data-position="1 -1 1" data-normal="1 0 0"></button>
<button slot="hotspot-dim+X-Y" class="dim" data-position="1 -1 0" data-normal="1 0 0"></button>
<button slot="hotspot-dot+X-Y-Z" class="dot" data-position="1 -1 -1" data-normal="1 0 0"></button>
<button slot="hotspot-dim+X-Z" class="dim" data-position="1 0 -1" data-normal="1 0 0"></button>
<button slot="hotspot-dot+X+Y-Z" class="dot" data-position="1 1 -1" data-normal="0 1 0"></button>
<button slot="hotspot-dim+Y-Z" class="dim" data-position="0 -1 -1" data-normal="0 1 0"></button>
<button slot="hotspot-dot-X+Y-Z" class="dot" data-position="-1 1 -1" data-normal="0 1 0"></button>
<button slot="hotspot-dim-X-Z" class="dim" data-position="-1 0 -1" data-normal="-1 0 0"></button>
<button slot="hotspot-dot-X-Y-Z" class="dot" data-position="-1 -1 -1" data-normal="-1 0 0"></button>
<button slot="hotspot-dim-X-Y" class="dim" data-position="-1 -1 0" data-normal="-1 0 0"></button>
<button slot="hotspot-dot-X-Y+Z" class="dot" data-position="-1 -1 1" data-normal="-1 0 0"></button>
<svg id="dimLines" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg"
class="dimensionLineContainer">
<line class="dimensionLine"></line>
<line class="dimensionLine"></line>
<line class="dimensionLine"></line>
<line class="dimensionLine"></line>
<line class="dimensionLine"></line>
</svg>
<div id="controls" class="dim glass">
<label for="src">Product:</label>
<select id="src">
<option value="../../assets/ShopifyModels/Chair.glb">Chair</option>
<option value="../../assets/ShopifyModels/Mixer.glb">Mixer</option>
<option value="../../assets/ShopifyModels/GeoPlanter.glb">Cactus</option>
</select><br />
<label for="show-dimensions">Show Dimensions:</label>
<input id="show-dimensions" type="checkbox" checked="true">
</div>
</model-viewer>
<script type="module">
const modelViewer = document.querySelector('#dimension-demo');
modelViewer.querySelector('#src').addEventListener('input', (event) => {
modelViewer.src = event.target.value;
});
const checkbox = modelViewer.querySelector('#show-dimensions');
const dimElements = [...modelViewer.querySelectorAll('button'), modelViewer.querySelector('#dimLines')];
function setVisibility(visible) {
dimElements.forEach((element) => {
if (visible) {
element.classList.remove('hide');
} else {
element.classList.add('hide');
}
});
}
checkbox.addEventListener('change', () => {
setVisibility(checkbox.checked);
});
modelViewer.addEventListener('ar-status', (event) => {
setVisibility(checkbox.checked && event.detail.status !== 'session-started');
});
// update svg
function drawLine(svgLine, dotHotspot1, dotHotspot2, dimensionHotspot) {
if (dotHotspot1 && dotHotspot2) {
svgLine.setAttribute('x1', dotHotspot1.canvasPosition.x);
svgLine.setAttribute('y1', dotHotspot1.canvasPosition.y);
svgLine.setAttribute('x2', dotHotspot2.canvasPosition.x);
svgLine.setAttribute('y2', dotHotspot2.canvasPosition.y);
// use provided optional hotspot to tie visibility of this svg line to
if (dimensionHotspot && !dimensionHotspot.facingCamera) {
svgLine.classList.add('hide');
}
else {
svgLine.classList.remove('hide');
}
}
}
const dimLines = modelViewer.querySelectorAll('line');
const renderSVG = () => {
drawLine(dimLines[0], modelViewer.queryHotspot('hotspot-dot+X-Y+Z'), modelViewer.queryHotspot('hotspot-dot+X-Y-Z'), modelViewer.queryHotspot('hotspot-dim+X-Y'));
drawLine(dimLines[1], modelViewer.queryHotspot('hotspot-dot+X-Y-Z'), modelViewer.queryHotspot('hotspot-dot+X+Y-Z'), modelViewer.queryHotspot('hotspot-dim+X-Z'));
drawLine(dimLines[2], modelViewer.queryHotspot('hotspot-dot+X+Y-Z'), modelViewer.queryHotspot('hotspot-dot-X+Y-Z')); // always visible
drawLine(dimLines[3], modelViewer.queryHotspot('hotspot-dot-X+Y-Z'), modelViewer.queryHotspot('hotspot-dot-X-Y-Z'), modelViewer.queryHotspot('hotspot-dim-X-Z'));
drawLine(dimLines[4], modelViewer.queryHotspot('hotspot-dot-X-Y-Z'), modelViewer.queryHotspot('hotspot-dot-X-Y+Z'), modelViewer.queryHotspot('hotspot-dim-X-Y'));
};
modelViewer.addEventListener('load', () => {
const center = modelViewer.getBoundingBoxCenter();
const size = modelViewer.getDimensions();
const x2 = size.x / 2;
const y2 = size.y / 2;
const z2 = size.z / 2;
modelViewer.updateHotspot({
name: 'hotspot-dot+X-Y+Z',
position: `${center.x + x2} ${center.y - y2} ${center.z + z2}`
});
modelViewer.updateHotspot({
name: 'hotspot-dim+X-Y',
position: `${center.x + x2 * 1.2} ${center.y - y2 * 1.1} ${center.z}`
});
modelViewer.querySelector('button[slot="hotspot-dim+X-Y"]').textContent =
`${(size.z * 100).toFixed(0)} cm`;
modelViewer.updateHotspot({
name: 'hotspot-dot+X-Y-Z',
position: `${center.x + x2} ${center.y - y2} ${center.z - z2}`
});
modelViewer.updateHotspot({
name: 'hotspot-dim+X-Z',
position: `${center.x + x2 * 1.2} ${center.y} ${center.z - z2 * 1.2}`
});
modelViewer.querySelector('button[slot="hotspot-dim+X-Z"]').textContent =
`${(size.y * 100).toFixed(0)} cm`;
modelViewer.updateHotspot({
name: 'hotspot-dot+X+Y-Z',
position: `${center.x + x2} ${center.y + y2} ${center.z - z2}`
});
modelViewer.updateHotspot({
name: 'hotspot-dim+Y-Z',
position: `${center.x} ${center.y + y2 * 1.1} ${center.z - z2 * 1.1}`
});
modelViewer.querySelector('button[slot="hotspot-dim+Y-Z"]').textContent =
`${(size.x * 100).toFixed(0)} cm`;
modelViewer.updateHotspot({
name: 'hotspot-dot-X+Y-Z',
position: `${center.x - x2} ${center.y + y2} ${center.z - z2}`
});
modelViewer.updateHotspot({
name: 'hotspot-dim-X-Z',
position: `${center.x - x2 * 1.2} ${center.y} ${center.z - z2 * 1.2}`
});
modelViewer.querySelector('button[slot="hotspot-dim-X-Z"]').textContent =
`${(size.y * 100).toFixed(0)} cm`;
modelViewer.updateHotspot({
name: 'hotspot-dot-X-Y-Z',
position: `${center.x - x2} ${center.y - y2} ${center.z - z2}`
});
modelViewer.updateHotspot({
name: 'hotspot-dim-X-Y',
position: `${center.x - x2 * 1.2} ${center.y - y2 * 1.1} ${center.z}`
});
modelViewer.querySelector('button[slot="hotspot-dim-X-Y"]').textContent =
`${(size.z * 100).toFixed(0)} cm`;
modelViewer.updateHotspot({
name: 'hotspot-dot-X-Y+Z',
position: `${center.x - x2} ${center.y - y2} ${center.z + z2}`
});
renderSVG();
modelViewer.addEventListener('camera-change', renderSVG);
});
</script>
<style>
#controls {
position: absolute;
bottom: 16px;
left: 16px;
max-width: unset;
transform: unset;
pointer-events: auto;
z-index: 100;
}
.dot {
display: none;
}
.glass {
background: rgba(255, 255, 255, 0.37);
backdrop-filter: blur(8px) contrast(0.89) saturate(1.27);
-webkit-backdrop-filter: blur(8px) contrast(0.89) saturate(1.27);
border: 1px solid rgba(255, 255, 255, 0.4);
padding: 0.5rem;
border-radius: 0.5rem;
}
.dim {
border-radius: 4px;
border: none;
box-sizing: border-box;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
color: rgba(0, 0, 0, 0.8);
display: block;
font-family: Futura, Helvetica Neue, sans-serif;
font-size: 1em;
font-weight: 700;
max-width: 128px;
overflow-wrap: break-word;
padding: 0.5em 1em;
position: absolute;
width: max-content;
height: max-content;
transform: translate3d(-50%, -50%, 0);
pointer-events: none;
--min-hotspot-opacity: 0;
}
@media only screen and (max-width: 800px) {
.dim {
font-size: 3vw;
}
}
.dimensionLineContainer {
pointer-events: none;
display: block;
}
.dimensionLine {
stroke: #16a5e6;
stroke-width: 2;
stroke-dasharray: 2;
}
.hide {
display: none;
}
/* This keeps child nodes hidden while the element loads */
:not(:defined)>* {
display: none;
}
</style>
</template>
</example-snippet>
</div>
</div>
</div>
<div class="sample">
<div id="cameraViews" class="demo"></div>
<div class="content">
<div class="wrapper">
<h4><span class="font-medium">Camera Views. </span></h4>
<p>
Hotspots can contain additional data attributes and can be interactive. This example attaches click events
to hotspots to move the camera.
</p>
<p>
Showcase by <a href="https://twitter.com/hybridherbst">hybridherbst</a> / <a
href="https://prefrontalcortex.de">prefrontal cortex</a>.<br />
</p>
<example-snippet stamp-to="cameraViews" highlight-as="html">
<template>
<model-viewer id="hotspot-camera-view-demo"
src="../../assets/SketchfabModels/ThorAndTheMidgardSerpent.glb" alt="Thor and the Midgard Serpent"
camera-controls touch-action="none" camera-orbit="-8.142746deg 68.967deg 0.6179899m"
camera-target="-0.003m 0.0722m 0.0391m" field-of-view="45deg" min-field-of-view="25deg"
max-field-of-view="45deg" interpolation-decay="200" min-camera-orbit="auto auto 5%"
touch-action="none" poster="../../assets/SketchfabModels/ThorAndTheMidgardSerpent.webp"
tone-mapping="aces" ar>
<button class="view-button" slot="hotspot-0" data-position="-0.0569m 0.0969m -0.1398m"
data-normal="-0.5829775m 0.2863482m -0.7603565m" data-orbit="-50.94862deg 84.56856deg 0.06545582m"
data-target="-0.04384604m 0.07348397m -0.1213202m">
The Fighters
</button>
<button class="view-button" slot="hotspot-1" data-position="-0.1997m 0.11766m 0.0056m"
data-normal="-0.4421014m 0.04410423m 0.8958802m" data-orbit="3.711166deg 92.3035deg 0.04335197m"
data-target="-0.1879433m 0.1157161m -0.01563221m">
Hold Tight!
</button>
<button class="view-button" slot="hotspot-2" data-position="0.0608m 0.0566m 0.0605m"
data-normal="0.2040984m 0.7985359m -0.56629m" data-orbit="42.72974deg 84.74043deg 0.07104211m"
data-target="0.0757959m 0.04128428m 0.07109568m">
The Encounter
</button>
<button class="view-button" slot="hotspot-3" data-position="0.1989m 0.16711m -0.0749m"
data-normal="0.7045857m 0.1997957m -0.6809117m" data-orbit="-40.11996deg 88.17818deg 0.07090651m"
data-target="0.2011831m 0.1398312m -0.07917573m">
Catapult
</button>
<button class="view-button" slot="hotspot-4" data-position="0.0677m 0.18906m -0.0158m"
data-normal="-0.008245394m 0.6207898m 0.7839338m" data-orbit="-118.8446deg 98.83521deg 0.06m"
data-target="0.06528695m 0.1753406m -0.01964653m">
Thunder and Lightning
</button>
<button class="view-button" slot="hotspot-5" data-position="-0.1418m -0.041m 0.174m"
data-normal="-0.4924125m 0.4698265m 0.7326617m" data-orbit="-2.305313deg 110.1798deg 0.04504082m"
data-target="-0.1151219m -0.04192762m 0.1523764m">
Knock Knock
</button>
<button class="view-button" slot="hotspot-6" data-position="0.08414419m 0.134m -0.215m"
data-normal="0.03777227m 0.06876653m -0.9969176m" data-orbit="-37.54149deg 82.16209deg 0.0468692m"
data-target="0.08566038m 0.1249514m -0.1939646m">
Lucky Shot
</button>
<button class="view-button" slot="hotspot-7" data-position="0.14598m 0.03177m -0.05945886m"
data-normal="-0.9392524m 0.2397608m -0.2456009m" data-orbit="-142.3926deg 86.45934deg 0.06213665m"
data-target="0.1519967m 0.01904771m -0.05945886m">
Get Away!
</button>
<button class="view-button" slot="hotspot-8" data-position="0.0094m 0.0894m -0.15103m"
data-normal="-0.3878782m 0.4957891m -0.7770094m" data-orbit="-118.6729deg 117.571deg 0.03905975m"
data-target="0.007600758m 0.06771782m -0.1386167m">
The Jump
</button>
<button class="view-button" slot="hotspot-9" data-position="-0.0658m 0.1786m -0.0183m"
data-normal="0.7857152m 0.4059967m 0.46671m" data-orbit="53.28236deg 95.91318deg 0.1102844m"
data-target="-0.07579391m 0.1393538m -0.00851791m">
The Beast
</button>
<button class="view-button" slot="hotspot-10" data-position="0.02610224m 0.01458751m -0.004978945m"
data-normal="-0.602551m 0.7856147m -0.1405055m" data-orbit="-78.89725deg 77.17752deg 0.08451112m"
data-target="0.02610223m 0.0145875m -0.004978945m">
Treasure
</button>
<button class="view-button" slot="hotspot-11" data-position="-0.1053838m 0.01610652m 0.1076345m"
data-normal="-0.624763m 0.5176854m 0.5845283m" data-orbit="10.89188deg 119.9775deg 0.03543022m"
data-target="-0.1053838m 0.01610652m 0.1076345m">
Desperation
</button>
</model-viewer>
<script type="module">
const modelViewer2 = document.querySelector("#hotspot-camera-view-demo");
const annotationClicked = (annotation) => {
let dataset = annotation.dataset;
modelViewer2.cameraTarget = dataset.target;
modelViewer2.cameraOrbit = dataset.orbit;
modelViewer2.fieldOfView = '45deg';
}
modelViewer2.querySelectorAll('button').forEach((hotspot) => {
hotspot.addEventListener('click', () => annotationClicked(hotspot));
});
</script>
<style type="text/css">
.view-button {
background: #fff;
border-radius: 4px;
border: none;
box-sizing: border-box;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
color: rgba(0, 0, 0, 0.8);
display: block;
font-family: Futura, Helvetica Neue, sans-serif;
font-size: 12px;
font-weight: 700;
max-width: 128px;
overflow-wrap: break-word;
padding: 0.5em 1em;
position: absolute;
width: max-content;
height: max-content;
transform: translate3d(-50%, -50%, 0);
}
</style>
</template>
</example-snippet>
</div>
</div>
</div>
<div class="footer">
<ul>
<li class="attribution">
<a href="https://poly.google.com/view/dLHpzNdygsg">Astronaut</a> by <a
href="https://poly.google.com/user/4aEd8rQgKu2">Poly</a>,
licensed under <a href="https://creativecommons.org/licenses/by/2.0/">CC-BY</a>.
</li>
<li class="attribution">
<a href="https://github.com/dataarts/3-dreams-of-black/tree/master/deploy/files/models/soup">Horse</a> by <a
href="https://github.com/dataarts">Google Data Arts Team</a>,
licensed under <a
href="https://github.com/dataarts/3-dreams-of-black/blob/master/deploy/files/models/soup/LICENSE.txt">Creative
Commons Attribution-NonCommercial-ShareAlike</a>.
</li>
<li class="attribution">
Chair, Mixer, GeoPlanter ©Copyright 2020 <a href="https://www.shopify.com/">Shopify Inc.</a>,
licensed under <a href="https://creativecommons.org/licenses/by/4.0/">CC-BY-4.0</a>.
</li>
<li class="attribution">
<a href="https://sketchfab.com/3d-models/thor-and-the-midgard-serpent-2ef4c45caa35450db1b876a7f94ff79d">Thor
and the Midgard Serpent</a> by <a href="https://sketchfab.com/MatthijsDeRijk">Matthijs de Rijk</a>,
licensed under <a href="https://creativecommons.org/licenses/by/4.0/">CC-BY</a>.
</li>
</ul>
<div style="margin-top:24px;" class="copyright">©Copyright 2019 Google Inc. Licensed under the Apache License
2.0.</div>
<div id='footer-links'></div>
</div>
</div>
</div>
<script type="module" src="../../examples/built/docs-and-examples.js">
</script>
<script type="module">
(() => { init('examples-annotations'); })();
(() => { initFooterLinks(); })();
</script>
<!-- Documentation-specific dependencies: -->
<script type="module" src="../built/dependencies.js">
</script>
<!-- Loads <model-viewer> on modern browsers: -->
<script type="module" src="../../../../node_modules/@google/model-viewer/dist/model-viewer.js">
</script>
</body>
</html>