UNPKG

@google/model-viewer

Version:

Easily display interactive 3D models on the web and in AR!

376 lines (328 loc) 7.94 kB
/* @license * Copyright 2019 Google LLC. 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. */ import { html, render } from 'lit'; import CloseIcon from './assets/close-material-svg.js'; import ControlsPrompt from './assets/controls-svg.js'; import ARGlyph from './assets/view-in-ar-material-svg.js'; const templateResult = html ` <style> :host { display: block; position: relative; contain: strict; width: 300px; height: 150px; } .container { position: relative; overflow: hidden; } .userInput { width: 100%; height: 100%; display: none; position: relative; outline-offset: -1px; outline-width: 1px; } canvas { position: absolute; display: none; pointer-events: none; /* NOTE(cdata): Chrome 76 and below apparently have a bug * that causes our canvas not to display pixels unless it is * on its own render layer * @see https://github.com/google/model-viewer/pull/755#issuecomment-536597893 */ transform: translateZ(0); } .show { display: block; } /* Adapted from HTML5 Boilerplate * * @see https://github.com/h5bp/html5-boilerplate/blob/ceb4620c78fc82e13534fc44202a3f168754873f/dist/css/main.css#L122-L133 */ .screen-reader-only { border: 0; left: 0; top: 0; clip: rect(0, 0, 0, 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; white-space: nowrap; width: 1px; pointer-events: none; } .slot { position: absolute; pointer-events: none; top: 0; left: 0; width: 100%; height: 100%; } .slot > * { pointer-events: initial; } .annotation-wrapper ::slotted(*) { opacity: var(--max-hotspot-opacity, 1); transition: opacity 0.3s; } .pointer-tumbling .annotation-wrapper ::slotted(*) { pointer-events: none; } .annotation-wrapper ::slotted(*) { pointer-events: initial; } .annotation-wrapper.hide ::slotted(*) { opacity: var(--min-hotspot-opacity, 0.25); } .slot.poster { display: none; background-color: inherit; } .slot.poster.show { display: inherit; } .slot.poster > * { pointer-events: initial; } .slot.poster:not(.show) > * { pointer-events: none; } #default-poster { width: 100%; height: 100%; /* The default poster is a <button> so we need to set display * to prevent it from being affected by text-align: */ display: block; position: absolute; border: none; padding: 0; background-size: contain; background-repeat: no-repeat; background-position: center; background-color: #fff0; } #default-progress-bar { display: block; position: relative; width: 100%; height: 100%; pointer-events: none; overflow: hidden; } #default-progress-bar > .bar { position: absolute; top: 0; left: 0; width: 100%; height: var(--progress-bar-height, 5px); background-color: var(--progress-bar-color, rgba(0, 0, 0, 0.4)); transition: transform 0.09s; transform-origin: top left; transform: scaleX(0); overflow: hidden; } #default-progress-bar > .bar.hide { transition: opacity 0.3s 1s; opacity: 0; } .centered { align-items: center; justify-content: center; } .cover { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; } .slot.interaction-prompt { display: var(--interaction-prompt-display, flex); overflow: hidden; opacity: 0; will-change: opacity; transition: opacity 0.3s; } .slot.interaction-prompt.visible { opacity: 1; } .animated-container { will-change: transform, opacity; opacity: 0; transition: opacity 0.3s; } .slot.interaction-prompt > * { pointer-events: none; } .slot.ar-button { -moz-user-select: none; -webkit-tap-highlight-color: transparent; user-select: none; display: var(--ar-button-display, block); } .slot.ar-button:not(.enabled) { display: none; } .fab { display: flex; align-items: center; justify-content: center; box-sizing: border-box; width: 40px; height: 40px; cursor: pointer; background-color: #fff; box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.15); border-radius: 100px; } .fab > * { opacity: 0.87; } #default-ar-button { position: absolute; bottom: 16px; right: 16px; transform: scale(var(--ar-button-scale, 1)); transform-origin: bottom right; } .slot.pan-target { display: block; position: absolute; width: 0; height: 0; left: 50%; top: 50%; transform: translate3d(-50%, -50%, 0); background-color: transparent; opacity: 0; transition: opacity 0.3s; } #default-pan-target { width: 6px; height: 6px; border-radius: 6px; border: 1px solid white; box-shadow: 0px 0px 2px 1px rgba(0, 0, 0, 0.8); } .slot.default { pointer-events: none; } .slot.progress-bar { pointer-events: none; } .slot.exit-webxr-ar-button { pointer-events: none; } .slot.exit-webxr-ar-button:not(.enabled) { display: none; } #default-exit-webxr-ar-button { display: flex; align-items: center; justify-content: center; position: absolute; top: env(safe-area-inset-top, 16px); right: 16px; width: 40px; height: 40px; box-sizing: border-box; } #default-exit-webxr-ar-button > svg { fill: #fff; } </style> <div class="container"> <div class="userInput" tabindex="0" role="img" aria-label="3D model"> <div class="slot canvas"> <slot name="canvas"> <canvas></canvas> </slot> </div> </div> <!-- NOTE(cdata): We need to wrap slots because browsers without ShadowDOM will have their <slot> elements removed by ShadyCSS --> <div class="slot poster"> <slot name="poster"> <button type="button" id="default-poster" aria-hidden="true" aria-label="Loading 3D model"></button> </slot> </div> <div class="slot ar-button"> <slot name="ar-button"> <a id="default-ar-button" part="default-ar-button" class="fab" tabindex="2" role="button" href="javascript:void(0);" aria-label="View in your space"> ${ARGlyph} </a> </slot> </div> <div class="slot pan-target"> <slot name="pan-target"> <div id="default-pan-target"> </div> </slot> </div> <div class="slot interaction-prompt cover centered"> <div id="prompt" class="animated-container"> <slot name="interaction-prompt" aria-hidden="true"> ${ControlsPrompt} </slot> </div> </div> <div id="finger0" class="animated-container cover"> <slot name="finger0" aria-hidden="true"> </slot> </div> <div id="finger1" class="animated-container cover"> <slot name="finger1" aria-hidden="true"> </slot> </div> <div class="slot default"> <slot></slot> <div class="slot progress-bar"> <slot name="progress-bar"> <div id="default-progress-bar" aria-hidden="true"> <div class="bar" part="default-progress-bar"></div> </div> </slot> </div> <div class="slot exit-webxr-ar-button"> <slot name="exit-webxr-ar-button"> <a id="default-exit-webxr-ar-button" part="default-exit-webxr-ar-button" tabindex="3" aria-label="Exit AR" aria-hidden="true"> ${CloseIcon} </a> </slot> </div> </div> </div> <div class="screen-reader-only" role="region" aria-label="Live announcements"> <span id="status" role="status"></span> </div>`; export const makeTemplate = (shadowRoot) => { render(templateResult, shadowRoot); }; //# sourceMappingURL=template.js.map