Uputstvo na engleskom
The <splat-viewer> element
<script type="module" src="/splat-viewer-0.1.0/splat-viewer.0.1.0.js"></script>
<splat-viewer src="/products/chair.sog" view="/products/chair.view.json" alt="Oak chair">
<img src="/products/chair.webp" alt="Oak chair" width="2048" height="2048">
</splat-viewer>
The <img> inside is the poster: it shows at once, and the 3D view replaces it when ready. If anything fails, the poster stays.
Attributes
| Attribute | Meaning | Default |
|---|---|---|
src |
The product file (.sog or .ply) |
Required |
poster |
Poster image, only when there is no <img> inside |
None |
alt |
Text for screen readers | None |
view |
A .view.json file with the start view, limits, and optionally a background colour behind the product ("background": "#000000"), such as black for night scenes |
Automatic view, see-through background |
up |
The file’s up axis: +x -x +y -y +z -z |
+y |
loading |
lazy waits until the page has loaded and the viewer is near the screen; eager starts at once |
lazy |
auto-rotate |
The product turns slowly by itself until the shopper touches it. Never when the device asks for reduced motion | Off |
gallery |
For a swipe gallery: sideways swipes move the gallery until the shopper taps the viewer; after that they turn the product, until focus moves elsewhere. Use the poster image as the gallery thumbnail | Off |
interaction-prompt |
none hides the “Drag to turn” hint shown after 3 seconds without interaction |
Shown |
Attributes are read once, when the viewer starts.
Events
| Event | When | event.detail |
|---|---|---|
load |
The 3D view has replaced the poster | { ms, splats, rebuilt }: milliseconds from the start of loading, the product’s splat count, and true when the view came back after the graphics card reset |
error |
Something failed; the poster stays | { code, message } |
first-interaction |
The shopper’s first drag, tap, wheel, key or preset-view click, once | { ms }: milliseconds after the 3D view showed |
view-time |
When the page is hidden or left, and when the element is removed | { ms }: how long the 3D view was at least half on screen since the last report |
The usage events (load, first-interaction, view-time) are for your own analytics. They go only to your page, carry no personal data (only times, a count and error codes) and nothing is sent to any server. first-interaction and view-time bubble, so one listener on document hears every viewer. The one exception: the view-time sent when an element is removed is no longer in the page, so only a listener on the element itself hears it. For example:
document.addEventListener('first-interaction', (e) => analytics.track('3d_interaction', { ms: e.detail.ms }));
Preset views
Add your own buttons inside the element; a click turns the camera to that view:
<splat-viewer src="/products/chair.sog" auto-rotate>
<img src="/products/chair.webp" alt="Oak chair">
<button slot="view" data-camera-orbit="0deg 75deg 100%">Front</button>
<button slot="view" data-camera-orbit="90deg 75deg 100%">Side</button>
<button slot="view" data-camera-orbit="0deg 20deg 80%">Top</button>
</splat-viewer>
data-camera-orbit is the angle around the product from the front, the angle down from straight above, and the distance as a share of the start view’s (optional). The view file’s limits still apply. The buttons are yours: style and word them as you like.
Hotspots
Pin your own labels to points on the product:
<splat-viewer src="/products/drill.sog">
<img src="/products/drill.webp" alt="Hammer drill">
<button slot="hotspot" data-position="0.02 0.11 0.05" data-normal="0 0 1">Chuck</button>
</splat-viewer>
data-position: the point, in the product file’s own coordinates (x y z).data-normal: the direction the surface faces there (x y z). The label hides while that side faces away from the shopper. Without it, the label hides only when its point is behind the camera.- The labels are yours: buttons, links or text, styled and worded as you like. Visible ones are in the keyboard’s tab order and read by screen readers; hidden ones are not.
React, Vue and other frameworks
The element works as it is, with no wrapper:
- Set attributes as usual. Changing
srcloads the new product in the same element. - Vue: tell Vue the tag is not one of its components,
app.config.compilerOptions.isCustomElement = (tag) => tag === 'splat-viewer', then listen with@load,@error,@first-interaction,@view-time. - React: listen through a ref,
ref.current.addEventListener('load', …), which works in React 18 and 19.
Shoppers
- Mouse: drag turns, right-drag or Shift-drag moves, double-click resets. The wheel zooms only after the viewer is clicked; before that it scrolls the page.
- Touch: one finger turns left and right, up-down swipes scroll the page, two fingers zoom and move, a double tap resets.
- Keyboard, once the viewer has focus: arrows turn,
+and-zoom, Home or0resets.
Styling
Size the element with your own CSS, such as width: 100%; aspect-ratio: 1. It is 300 × 300 pixels if you set nothing, and stays square if you set only a width. For a wider box use aspect-ratio: 4/3 or aspect-ratio: 16/9: the 3D view keeps the same framing from top to bottom and shows more at the sides. Make the poster in the same shape (the view editor’s Shape and Download poster), or it shows smaller until the 3D view replaces it.
Custom properties, set on the element or any parent:
| Property | Styles | Default |
|---|---|---|
--splat-viewer-background |
Behind the poster and the 3D view | transparent |
--splat-viewer-focus-color |
The keyboard focus ring | The browser’s highlight colour |
--splat-viewer-accent-color |
The line under the loading sign | #fff |
--splat-viewer-text-color |
Text on the loading sign and the hint | #fff |
--splat-viewer-panel-color |
The background of the loading sign and the hint | rgba(0, 0, 0, 0.6) |
--splat-viewer-radius |
The loading sign’s corners | 999px |
--splat-viewer-font |
The loading sign’s font, as the CSS font shorthand |
500 14px/1.2 system-ui, sans-serif |
Parts, for any other CSS through ::part():
| Part | What it is |
|---|---|
loading |
The loading sign, shown after one second of loading |
canvas |
The 3D view |
prompt |
The “Drag to turn” hint |
views |
The row holding your preset-view buttons, at the top right |
hotspots |
The layer holding your hotspot labels |
Your own loading text and hint, for example in another language:
<splat-viewer src="/products/chair.sog">
<img src="/products/chair.webp" alt="Stolica od hrasta">
<span slot="loading">Učitavanje 3D prikaza…</span>
<span slot="prompt">Prevucite da okrenete</span>
</splat-viewer>
Example:
splat-viewer {
--splat-viewer-accent-color: #e4572e;
--splat-viewer-panel-color: #1d3557;
}
splat-viewer::part(loading) { text-transform: uppercase; }