Skip to content

Revert mixin type inference #761

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
206 changes: 107 additions & 99 deletions src/features/animation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,123 +23,131 @@ const MILLISECONDS_PER_SECOND = 1000.0
const $changeAnimation = Symbol('changeAnimation');
const $paused = Symbol('paused');

export const AnimationMixin =
<T extends Constructor<ModelViewerElementBase>>(ModelViewerElement: T) => {
class AnimationModelViewerElement extends ModelViewerElement {
@property({type: Boolean}) autoplay: boolean = false;
@property({type: String, attribute: 'animation-name'})
animationName: string|void = undefined;
@property({type: Number, attribute: 'animation-crossfade-duration'})
animationCrossfadeDuration: number = 300;

protected[$paused]: boolean = true;

/**
* Returns an array
*/
get availableAnimations(): Array<string> {
if (this.loaded) {
return (this as any)[$scene].model.animationNames;
}

return [];
}
export interface AnimationInterface {
autoplay: boolean;
animationName: string|void;
animationCrossfadeDuration: number;
readonly availableAnimations: Array<string>;
readonly paused: boolean;
currentTime: number;
pause(): void;
play(): void;
}

export const AnimationMixin = <T extends Constructor<ModelViewerElementBase>>(
ModelViewerElement: T): Constructor<AnimationInterface>&T => {
class AnimationModelViewerElement extends ModelViewerElement {
@property({type: Boolean}) autoplay: boolean = false;
@property({type: String, attribute: 'animation-name'})
animationName: string|void = undefined;
@property({type: Number, attribute: 'animation-crossfade-duration'})
animationCrossfadeDuration: number = 300;

protected[$paused]: boolean = true;

/**
* Returns an array
*/
get availableAnimations(): Array<string> {
if (this.loaded) {
return (this as any)[$scene].model.animationNames;
}

get paused(): boolean {
return this[$paused];
}
return [];
}

get currentTime(): number {
return (this as any)[$scene].model.animationTime;
}
get paused(): boolean {
return this[$paused];
}

set currentTime(value: number) {
(this as any)[$scene].model.animationTime = value;
}
get currentTime(): number {
return (this as any)[$scene].model.animationTime;
}

pause() {
if (this[$paused]) {
return;
}
set currentTime(value: number) {
(this as any)[$scene].model.animationTime = value;
}

this[$paused] = true;
this.dispatchEvent(new CustomEvent('pause'));
}
pause() {
if (this[$paused]) {
return;
}

play() {
if (this[$paused] && this.availableAnimations.length > 0) {
this[$paused] = false;
this[$paused] = true;
this.dispatchEvent(new CustomEvent('pause'));
}

if (!(this as any)[$scene].model.hasActiveAnimation) {
this[$changeAnimation]();
}
play() {
if (this[$paused] && this.availableAnimations.length > 0) {
this[$paused] = false;

this.dispatchEvent(new CustomEvent('play'));
}
if (!(this as any)[$scene].model.hasActiveAnimation) {
this[$changeAnimation]();
}

[$onModelLoad]() {
this[$paused] = true;

if (this.autoplay) {
this[$changeAnimation]();
this.play();
}
}

[$tick](_time: number, delta: number) {
super[$tick](_time, delta);

if (this[$paused]) {
return;
}

const {model} = (this as any)[$scene];
model.updateAnimation(delta / MILLISECONDS_PER_SECOND);

this[$needsRender]();
}
this.dispatchEvent(new CustomEvent('play'));
}
}

updated(changedProperties: Map<string, any>) {
super.updated(changedProperties);
[$onModelLoad]() {
this[$paused] = true;

if (changedProperties.has('autoplay') && this.autoplay) {
this.play();
}
if (this.autoplay) {
this[$changeAnimation]();
this.play();
}
}

if (changedProperties.has('animationName')) {
this[$changeAnimation]();
}
}
[$tick](_time: number, delta: number) {
super[$tick](_time, delta);

async[$updateSource]() {
// If we are loading a new model, we need to stop the animation of
// the current one (if any is playing). Otherwise, we might lose
// the reference to the scene root and running actions start to
// throw exceptions and/or behave in unexpected ways:
(this as any)[$scene].model.stopAnimation();
if (this[$paused]) {
return;
}

return super[$updateSource]();
}
const {model} = (this as any)[$scene];
model.updateAnimation(delta / MILLISECONDS_PER_SECOND);

[$changeAnimation]() {
const {model} = (this as any)[$scene];
this[$needsRender]();
}

model.playAnimation(
this.animationName,
this.animationCrossfadeDuration / MILLISECONDS_PER_SECOND);
updated(changedProperties: Map<string, any>) {
super.updated(changedProperties);

// If we are currently paused, we need to force a render so that
// the model updates to the first frame of the new animation
if (this[$paused]) {
model.updateAnimation(0);
this[$needsRender]();
}
}
if (changedProperties.has('autoplay') && this.autoplay) {
this.play();
}

return AnimationModelViewerElement;
};
if (changedProperties.has('animationName')) {
this[$changeAnimation]();
}
}

async[$updateSource]() {
// If we are loading a new model, we need to stop the animation of
// the current one (if any is playing). Otherwise, we might lose
// the reference to the scene root and running actions start to
// throw exceptions and/or behave in unexpected ways:
(this as any)[$scene].model.stopAnimation();

return super[$updateSource]();
}

[$changeAnimation]() {
const {model} = (this as any)[$scene];

model.playAnimation(
this.animationName,
this.animationCrossfadeDuration / MILLISECONDS_PER_SECOND);

// If we are currently paused, we need to force a render so that
// the model updates to the first frame of the new animation
if (this[$paused]) {
model.updateAnimation(0);
this[$needsRender]();
}
}
}

export type AnimationInterface =
InstanceType<ReturnType<typeof AnimationMixin>>;
return AnimationModelViewerElement;
};
13 changes: 10 additions & 3 deletions src/features/ar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,17 @@ const $onExitFullscreenButtonClick = Symbol('onExitFullscreenButtonClick');
const $fullscreenchangeHandler = Symbol('fullscreenHandler');
const $onFullscreenchange = Symbol('onFullscreen');

export interface ARInterface {
ar: boolean;
unstableWebxr: boolean;
iosSrc: string|null;
quickLookBrowsers: string;
readonly canActivateAR: boolean;
activateAR(): Promise<void>;
}

export const ARMixin = <T extends Constructor<ModelViewerElementBase>>(
ModelViewerElement: T) => {
ModelViewerElement: T): Constructor<ARInterface>&T => {
class ARModelViewerElement extends ModelViewerElement {
@property({type: Boolean, attribute: 'ar'}) ar: boolean = false;

Expand Down Expand Up @@ -348,5 +357,3 @@ configuration or device capabilities');

return ARModelViewerElement;
};

export type ARInterface = InstanceType<ReturnType<typeof ARMixin>>;
18 changes: 15 additions & 3 deletions src/features/controls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,22 @@ const $userPromptedOnce = Symbol('userPromptedOnce');
const $lastSpherical = Symbol('lastSpherical');
const $jumpCamera = Symbol('jumpCamera');

export interface ControlsInterface {
cameraControls: boolean;
cameraOrbit: string;
cameraTarget: string;
fieldOfView: string;
interactionPrompt: InteractionPromptStrategy;
interactionPolicy: InteractionPolicy;
interactionPromptThreshold: number;
getCameraOrbit(): SphericalPosition;
getCameraTarget(): Vector3;
getFieldOfView(): number;
jumpCameraToGoal(): void;
}

export const ControlsMixin = <T extends Constructor<ModelViewerElementBase>>(
ModelViewerElement: T) => {
ModelViewerElement: T): Constructor<ControlsInterface>&T => {
class ControlsModelViewerElement extends ModelViewerElement {
@property({type: Boolean, attribute: 'camera-controls'})
cameraControls: boolean = false;
Expand Down Expand Up @@ -455,5 +469,3 @@ export const ControlsMixin = <T extends Constructor<ModelViewerElementBase>>(

return ControlsModelViewerElement;
};

export type ControlsInterface = InstanceType<ReturnType<typeof ControlsMixin>>;
13 changes: 9 additions & 4 deletions src/features/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,16 @@ const $updateShadow = Symbol('updateShadow');
const $updateEnvironment = Symbol('updateEnvironment');
const $cancelEnvironmentUpdate = Symbol('cancelEnvironmentUpdate');

export interface EnvironmentInterface {
environmentImage: string|null;
backgroundImage: string|null;
backgroundColor: string;
shadowIntensity: number;
exposure: number;
}

export const EnvironmentMixin = <T extends Constructor<ModelViewerElementBase>>(
ModelViewerElement: T) => {
ModelViewerElement: T): Constructor<EnvironmentInterface>&T => {
class EnvironmentModelViewerElement extends ModelViewerElement {
@property({
type: String,
Expand Down Expand Up @@ -184,6 +192,3 @@ export const EnvironmentMixin = <T extends Constructor<ModelViewerElementBase>>(

return EnvironmentModelViewerElement;
};

export type EnvironmentInterface =
InstanceType<ReturnType<typeof EnvironmentMixin>>;
13 changes: 10 additions & 3 deletions src/features/loading.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,22 @@ const $onClick = Symbol('onClick');
const $onKeydown = Symbol('onKeydown');
const $onProgress = Symbol('onProgress');

export interface LoadingInterface {
poster: string|null;
reveal: RevealAttributeValue;
preload: boolean;
readonly loaded: boolean;
readonly modelIsVisible: boolean;
dismissPoster(): void;
}

/**
* LoadingMixin implements features related to lazy loading, as well as
* presentation details related to the pre-load / pre-render presentation of a
* <model-viewer>
*/
export const LoadingMixin = <T extends Constructor<ModelViewerElementBase>>(
ModelViewerElement: T) => {
ModelViewerElement: T): Constructor<LoadingInterface>&T => {
class LoadingModelViewerElement extends ModelViewerElement {
/**
* A URL pointing to the image to use as a poster in scenarios where the
Expand Down Expand Up @@ -397,5 +406,3 @@ export const LoadingMixin = <T extends Constructor<ModelViewerElementBase>>(

return LoadingModelViewerElement;
};

export type LoadingInterface = InstanceType<ReturnType<typeof LoadingMixin>>;
Loading