Skip to content

Conversation

@cdata
Copy link
Contributor

@cdata cdata commented Apr 16, 2019

Progress Bars

This change proposes the addition of the notion of instance-wide loading progress, as well as corresponding customizable visualizations and API. In other words: progress bars.

Some highlights of this change include:

  • A new progress event is introduced, maps directly to our internal notion of progress
  • A built in progress bar UI has been introduced
    • Can be customized by adding child nodes to slot="progress-bar"
    • Can also be customized with CSS custom properties
  • The poster now has a default background color, and is always displayed before the model is loaded
    • Can now be customized by adding child nodes to slot="poster"
    • Can also be customized with CSS custom properties
  • A new reveal attribute has been introduced to control model visibility
    • Default configuration is equivalent to reveal-when-loaded semantics
    • reveal="interaction" defers loading / revealing model until user interaction
  • 🚨 BREAKING CHANGE: reveal-when-loaded has been removed in favor of new reveal attribute and related semantics
  • A new ProgressTracker utility class has been introduced to assist in tracking progress across multiple, uncoordinated, asynchronous actions at once
  • Several modules have been converted to TypeScript, including: model-viewer-base, features/loading, template and three-components/CachingGLTFLoader
  • Asynchronous actors have been instrumented with progress callback support throughout
  • Type declarations for GLTFLoader have been copied over from the Three.js project
  • A bug was fixed relating to how DPR was applied when zooming
  • A bug was fixed that caused <model-viewer> to have 1px-wide empty margins sometimes

New CSS custom properties

In keeping with our goal of enabling as much idiomatic customization as we can, the following CSS custom properties have been introduced by this change:

CSS property Description
--poster-color Sets the background-color of the poster. Falls back to --background-color if set. Defaults to #fff.
--poster-image Sets the background-image of the poster. This is currently overridden by the poster attribute if it is set. Defaults to none.
--progress-bar-color Sets the background-color of the progress bar. Defaults to rgba(0, 0, 0, 0.4).
--progress-mask                                                    Sets the background of the progress mask. Defaults to #fff.

Tracking progress

In order to present the model at the correct time, we must take into account an uncertain number of possible network actions (environment map, glTF w/ multiple sub-resources etc) as well as an uncertain number of script/GPU processing steps (env map generation, skybox, PMREM etc) in order for the model to be considered presentable. The notion of "progress" introduced by this change encompasses all such actions that are implemented today.

Here is an example of how complex the process of presenting one model can be. Note that this is a fairly heavy configuration, resulting in almost 70MB of total assets transferred before we reveal the model:

Artboard 1@4x

Examples

Basic progress bar w/ poster reveal="interaction"
progressbar progressbar-reveal-interaction
src attribute changing over time Custom progress bar
progressbar-src-changing progressbar-custom-bar

The following is an abbreviated form of the markup used to create the custom progress bar example above:

<style>
  #progress {
    position: absolute;
    width: calc(100% / 3);
    height: 10px;
    color: rgba(255, 255, 255, 0.75);
    box-shadow: 0px 0px 8px 6px rgba(0, 0, 0, 0.25);
    border: 2px solid currentColor;
    border-radius: 10px;
    top: calc(50% - 5px);
    left: calc(50% - 100% / 6);
    opacity: 0;
    transition: opacity 0.3s 0.3s;
  }

  #progress.show {
    transition-delay: 0s;
    opacity: 1;
  }

  #progress>.bar {
    width: 100%;
    height: 100%;
    background-color: currentColor;
    transform-origin: top left;
    transform: scaleX(0);
  }
</style>

<!-- ... -->

<model-viewer src="./any.gltf">
  <div id="progress" slot="progress-bar">
    <div class="bar"></div>
  </div>
</model-viewer>

<script>
  const modelViewer = document.querySelector('model-viewer');
  const progress = document.querySelector('#progress');
  const bar = progress.querySelector('.bar');

  modelViewer.addEventListener('progress', (event) => {
    const {totalProgress} = event.detail;
    progress.classList.toggle('show', totalProgress < 1);
    bar.style.transform = `scaleX(${totalProgress})`;
  });
</script>

Future work

Fixes #335
Fixes #489

comments: 'none',
}),
);
if (NODE_ENV !== 'development') {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default run mode for the build script was set as "development", but should probably be "production" so that naively running Rollup with the config builds all the things.

@cdata cdata force-pushed the progress-bars branch 3 times, most recently from 530fc81 to 69c0e99 Compare April 16, 2019 16:40
@cdata cdata force-pushed the progress-bars branch 2 times, most recently from ce29777 to ddbadd5 Compare April 17, 2019 04:08
@cdata cdata added this to the v0.3.0 milestone Apr 17, 2019
pointer-events: none;
}
.click-to-view {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed this entirely, as it is vestigial from <xr-model> prototype days

@yuinchien
Copy link
Contributor

@cdata I wonder if we should simplify the default css properties for progress bar & future features. My concern is that the list of properties can be endless if we aren't extremely selective. I believe developers can further customize CSS if they want to, we should keep defaults to a minimum.

To me, the most crucial properties for progress bar are:

  • --background-color
  • --poster-image
  • --progress-bar-color

If we set default poster image & progress-mask to background-size: cover, we can still have a pretty ok baseline results. The progress-mask color should be directly linked to background-color, without transparency. We should recommend using poster image with the same background color for consistency. WDYT?

smalls
smalls previously approved these changes Apr 17, 2019
@@ -0,0 +1,402 @@
/*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a nit, this would have been easier to read broken out into separate PRs. But, it was broken out into separate commits, so it was pretty easy to just go back there to take a look:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, agreed. Lot's of regret.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:)


get[$shouldRevealModel](): boolean {
return this.reveal === RevealStrategy.AUTO ||
this.reveal === this[$posterDismissalSource];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would the second clause be clearer as PosterDismissalSource.INTERACTION === this[$posterDismissalSource] or !!this[$posterDismissalSource]?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose my thought (particularly with the second) is that we probably don't care what the dismissal source is, only if we have one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, at this stage we only care that a dismissal source is set. As written, it's only helpful if we had N+1 possible sources.

(async () => {
const updateSourceProgress = this[$progressTracker].beginActivity();
await this[$updateSource](
(progress: number) => updateSourceProgress(progress * 0.9));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why * 0.9? So it doesn't go to 1.0 until we're actually done (below)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah

@@ -0,0 +1,141 @@
import {clamp} from '../utils.js';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File header?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will fix

@cdata cdata changed the title (WIP) Progress bars Progress bars Apr 18, 2019
@cdata cdata marked this pull request as ready for review April 18, 2019 20:39
@cdata
Copy link
Contributor Author

cdata commented Apr 18, 2019

@yuinchien thanks for the feedback. The CSS properties have been pared down to four.

@cdata cdata merged commit 60f37e9 into master Apr 18, 2019
@AYTSistemas13
Copy link

const modelViewer = document.querySelector('model-viewer');
const progress = document.querySelector('#progress');
const bar = progress.querySelector('.bar');

modelViewer.addEventListener('progress', (event) => {
const {totalProgress} = event.detail;
progress.classList.toggle('show', totalProgress < 1);
bar.style.transform = scaleX(${totalProgress});
});

when I put this code into an Angular Project, typescript show a few of error messages, I already achieved most of them but one it is still going, and the error says: Property 'detail' does not exist on type 'ProgressEvent'.

This is my code in my component from Angular project:

const modelViewerload = document.querySelector('model-viewer') as ModelViewerElement;
const progress = document.querySelector('#progress') as HTMLElement;
const bar = progress.querySelector('.bar') as HTMLElement;

modelViewerload.addEventListener('progress', (event) => {
  const totalProgress:any = event.detail;
  progress.classList.toggle('show', totalProgress! < 1);
  bar.style.transform = `scaleX(${totalProgress})`;
} );

I installed @google/model-viewer via npm, and this are my imports:
import { ModelViewerElement, RGBA } from "@google/model-viewer/lib/model-viewer";

I hope you can read me and find a solution. Thanks.

@elalish
Copy link
Contributor

elalish commented Nov 7, 2022

maybe const {totalProgress} = event.detail as any;? We may have an error in our types.

@AYTSistemas13
Copy link

Yes, I did this and worked:
const totalProgress = (event as any).detail.totalProgress;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pixel rounding of canvas size leads to unsightly margins Add a visual progress bar / loading state to indicate that model is loading

5 participants