{% liquid assign ProductVRSources = '' assign ProductVRSourcesIos = '' assign ProductVRAlts = '' assign ProductVRPosters = '' assign isFirstModel = false for model in section.settings.product_vr.media if model.media_type == 'model' unless isFirstModel assign firstModel = model assign isFirstModel = true endunless assign ProductVRSources = ProductVRSources | append: ',' | append: model.sources[0].url assign ProductVRSourcesIos = ProductVRSourcesIos | append: ',' | append: model.sources[1].url assign ProductVRAlts = ProductVRAlts | append: ',' | append: model.alt assign modelImg = model.preview_image | image_url assign ProductVRPosters = ProductVRPosters | append: ',' | append: modelImg endif endfor %} {{ firstModel | model_viewer_tag: shadow-intensity: 1, ar: true, camera-controls: true, touch-action: 'pan-y', id: 'main-viewer' }} <button slot="ar-button" id="ar-button">View in your space</button> <div id="ar-prompt"> <img src="{{ "hand.png" | asset_url }}"> </div> <button id="ar-failure">AR is not tracking!</button> <div class="slider" id="slider-model-viewer"> <div class="slides"> {% assign posters = ProductVRPosters | split: ',' %} {% for poster in posters %} {% unless forloop.index0 == 0 %} <button class="slide{% if forloop.index0 == 1 %} selected{% endif %}" onclick="switchSrc(this, {{ forloop.index0 }})" style="background-image: url('{{ poster }}');" ></button> {% endunless %} {% endfor %} </div> </div> <script async type="module"> document.addEventListener('DOMContentLoaded', (event) => { const modelViewer = document.querySelector('#main-viewer'); const sliders = document.querySelector('#slider-model-viewer'); const arBtn = document.querySelector('#ar-button'); const arPrompt = document.querySelector('#ar-prompt'); const arFailure = document.querySelector('#ar-failure'); modelViewer.append(arBtn); modelViewer.append(arPrompt); modelViewer.append(arFailure); modelViewer.append(sliders); const sources = '{{ ProductVRSources }}'.split(',').filter((item) => item != ''); const iosSources = '{{ ProductVRSourcesIos }}'.split(',').filter((item) => item != ''); const alts = '{{ ProductVRAlts }}'.split(',').filter((item) => item != ''); const posters = '{{ ProductVRPosters }}'.split(',').filter((item) => item != ''); window.switchSrc = (element, pos) => { const position = pos - 1; modelViewer.src = sources[position]; modelViewer.poster = posters[position]; modelViewer.iosSrc = iosSources[position]; const slides = document.querySelectorAll('#main-viewer .slide'); slides.forEach((element) => { element.classList.remove('selected'); }); element.classList.add('selected'); }; modelViewer.addEventListener('load', (event) => { document.querySelector('#main-viewer .slider').addEventListener('beforexrselect', (ev) => { ev.preventDefault(); }); }); }); </script> {% schema %} { "name": "Product VR Preview", "tag": "section", "class": "product-vr-wrapper", "settings": [ { "type": "product", "id": "product_vr", "label": "Product VR Models To Showcase" } ], "presets": [ { "name": "Product VR Preview" } ] } {% endschema %} {% stylesheet %} /* This keeps child nodes hidden while the element loads */ :not(:defined) > * { display: none; } model-viewer { background-color: #eee; overflow-x: hidden; height: 80vh; width: 80%; margin: 0 auto; } #ar-button { background-image: url({{'ic_view_in_ar_new_googblue_48dp.png' | asset_url }}); background-repeat: no-repeat; background-size: 20px 20px; background-position: 12px 50%; background-color: #fff; position: absolute; left: 50%; transform: translateX(-50%); white-space: nowrap; bottom: 132px; padding: 0px 16px 0px 40px; font-family: Roboto Regular, Helvetica Neue, sans-serif; font-size: 14px; color: #4285f4; height: 36px; line-height: 36px; border-radius: 18px; border: 1px solid #dadce0; } #ar-button:active { background-color: #e8eaed; } #ar-button:focus { outline: none; } #ar-button:focus-visible { outline: 1px solid #4285f4; } @keyframes circle { from { transform: translateX(-50%) rotate(0deg) translateX(50px) rotate(0deg); } to { transform: translateX(-50%) rotate(360deg) translateX(50px) rotate(-360deg); } } @keyframes elongate { from { transform: translateX(100px); } to { transform: translateX(-100px); } } model-viewer > #ar-prompt { position: absolute; left: 50%; bottom: 175px; animation: elongate 2s infinite ease-in-out alternate; display: none; } model-viewer[ar-status='session-started'] > #ar-prompt { display: block; } model-viewer > #ar-prompt > img { animation: circle 4s linear infinite; } model-viewer > #ar-failure { position: absolute; left: 50%; transform: translateX(-50%); bottom: 175px; display: none; } model-viewer[ar-tracking='not-tracking'] > #ar-failure { display: block; } .slider { width: 100%; text-align: center; overflow: hidden; position: absolute; bottom: 16px; } .slides { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; scroll-behavior: smooth; -webkit-overflow-scrolling: touch; } .slide { scroll-snap-align: start; flex-shrink: 0; width: 100px; height: 100px; background-size: contain; background-repeat: no-repeat; background-position: center; background-color: #fff; margin-right: 10px; border-radius: 10px; border: none; display: flex; } .slide.selected { border: 2px solid #4285f4; } .slide:focus { outline: none; } .slide:focus-visible { outline: 1px solid #4285f4; } {% endstylesheet %}