diff --git a/demo/index.ts b/demo/index.ts index 76fc17a..ac753ed 100644 --- a/demo/index.ts +++ b/demo/index.ts @@ -6,7 +6,7 @@ import { REALTY_SCENE, REALTY_SCENE_1 } from './mocks'; let isDarkTheme = false; async function start() { - const mapglAPI = await load('https://mapgl.2gis.com/api/js/v0.0.322'); + const mapglAPI = await load('https://mapgl.2gis.com/api/js/v0.0.323'); const map = new mapglAPI.Map('container', { center: [47.245286302641034, 56.134743473834099], @@ -20,7 +20,7 @@ async function start() { (window as any).map = map; const plugin = new GltfPlugin(map, { - modelsLoadStrategy: 'waitAll', + modelsLoadStrategy: 'dontWaitAll', modelsBaseUrl: 'https://disk.2gis.com/digital-twin/models_s3/realty_ads/zgktechnology/', floorsControl: { position: 'centerRight' }, poiConfig: { @@ -31,8 +31,8 @@ async function start() { fontSize: 14, }, }, - hoverHighlight: { - intencity: 0.1, + hoverOptions: { + color: '#FFEFEF', }, groundCoveringColor: 'rgba(0, 0, 0, 0.8)', }); diff --git a/src/defaultOptions.ts b/src/defaultOptions.ts index e0383e7..d47089c 100644 --- a/src/defaultOptions.ts +++ b/src/defaultOptions.ts @@ -1,9 +1,8 @@ import type { PluginOptions } from './types/plugin'; export const defaultOptions: Required = { - hoverHighlight: { + hoverOptions: { color: '#ffffff', - intencity: 0.0, }, modelsBaseUrl: '', modelsLoadStrategy: 'waitAll', diff --git a/src/plugin.ts b/src/plugin.ts index bcf96f8..fb7fbd5 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -10,10 +10,12 @@ import { concatUrl, isAbsoluteUrl } from './utils/url'; import { createModelEventData } from './utils/events'; import { RealtyScene } from './realtyScene/realtyScene'; import { GROUND_COVERING_LAYER } from './constants'; +import { ModelStatus } from './types/plugin'; interface Model { instance: any; // GltfModel options: ModelOptions; + isLoaded: boolean; } const MODEL_DEFAULTS = { @@ -121,17 +123,25 @@ export class GltfPlugin extends Evented { hideOnInit: this.options.modelsLoadStrategy === 'waitAll' || (modelIdsToShow && !modelIdsToShow.includes(options.modelId)), + hover: { + color: this.options.hoverOptions.color, + }, + disableAnimation: true, }); - const model = { + const model: Model = { options, instance, + isLoaded: false, }; this.models.set(options.modelId, model); return new Promise((resolve) => { - instance.once('modelloaded', () => resolve(model)); + instance.once('modelloaded', () => { + model.isLoaded = true; + resolve(model); + }); (['click', 'mousemove', 'mouseover', 'mouseout'] as const).forEach( (eventType) => { instance.on(eventType, (ev) => { @@ -155,8 +165,13 @@ export class GltfPlugin extends Evented { }); } - public isModelAdded(id: Id) { - return this.models.has(id); + public getModelStatus(id: Id) { + const model = this.models.get(id); + if (!model) { + return ModelStatus.NoModel; + } + + return !model.isLoaded ? ModelStatus.Loading : ModelStatus.Loaded; } public removeModel(id: Id) { diff --git a/src/realtyScene/realtyScene.ts b/src/realtyScene/realtyScene.ts index f21f62f..3cba58f 100644 --- a/src/realtyScene/realtyScene.ts +++ b/src/realtyScene/realtyScene.ts @@ -4,7 +4,13 @@ import { GltfPlugin } from '../plugin'; import { GltfFloorControl } from '../control'; import classes from './realtyScene.module.css'; -import type { BuildingState, Id, ModelOptions, PluginOptions } from '../types/plugin'; +import { + ModelStatus, + type BuildingState, + type Id, + type ModelOptions, + type PluginOptions, +} from '../types/plugin'; import type { BuildingOptions, MapOptions, @@ -78,44 +84,94 @@ export class RealtyScene { private setState(newState: RealtySceneState) { const prevState = this.state; - + const buildingVisibility: RealtySceneState['buildingVisibility'] = new Map(); this.buildings.forEach((_, buildingId) => { const prevModelOptions = prevState.buildingVisibility.get(buildingId); const newModelOptions = newState.buildingVisibility.get(buildingId); - if (prevModelOptions) { - this.plugin.hideModel(prevModelOptions.modelId); - } - if (newModelOptions) { - this.plugin.isModelAdded(newModelOptions.modelId) - ? this.plugin.showModel(newModelOptions.modelId) - : this.plugin.addModel(newModelOptions); + if (prevModelOptions?.modelId === newModelOptions?.modelId) { + buildingVisibility.set(buildingId, prevModelOptions); + return; } - }); - if (prevState.activeModelId !== newState.activeModelId) { if ( - prevState.activeModelId !== undefined && - this.undergroundFloors.has(prevState.activeModelId) + prevModelOptions && + (!newModelOptions || + this.plugin.getModelStatus(newModelOptions.modelId) === ModelStatus.Loaded) ) { - this.switchOffGroundCovering(); - } - - if (newState.activeModelId !== undefined) { - const options = - this.buildings.get(newState.activeModelId) ?? - this.floors.get(newState.activeModelId); - if (options) { - this.setMapOptions(options.mapOptions); - // this.addFloorPoi(activeFloor); - // this.clearPoiGroups(); + this.plugin.hideModel(prevModelOptions.modelId); + buildingVisibility.set(buildingId, undefined); + + if ( + prevState.activeModelId !== undefined && + prevState.activeModelId === prevModelOptions.modelId && + this.undergroundFloors.has(prevModelOptions.modelId) + ) { + this.switchOffGroundCovering(); } + } - if (this.undergroundFloors.has(newState.activeModelId)) { - this.switchOnGroundCovering(); + if (newModelOptions) { + const modelStatus = this.plugin.getModelStatus(newModelOptions.modelId); + if (modelStatus === ModelStatus.Loaded) { + this.plugin.showModel(newModelOptions.modelId); + buildingVisibility.set(buildingId, newModelOptions); + + if ( + newState.activeModelId !== undefined && + newState.activeModelId === newModelOptions.modelId + ) { + const options = + this.buildings.get(newModelOptions.modelId) ?? + this.floors.get(newModelOptions.modelId); + + if (options) { + this.setMapOptions(options.mapOptions); + } + + if (this.undergroundFloors.has(newModelOptions.modelId)) { + this.switchOnGroundCovering(); + } + } + } else { + if (modelStatus === ModelStatus.NoModel) { + this.plugin.addModel(newModelOptions, true).then(() => { + if (this.state.activeModelId !== newModelOptions.modelId) { + return; + } + + const currModelOptions = this.state.buildingVisibility.get(buildingId); + if (currModelOptions) { + this.plugin.hideModel(currModelOptions.modelId); + if (this.undergroundFloors.has(currModelOptions.modelId)) { + this.switchOffGroundCovering(); + } + } + + this.plugin.showModel(newModelOptions.modelId); + this.state.buildingVisibility.set(buildingId, newModelOptions); + + const options = + this.buildings.get(newModelOptions.modelId) ?? + this.floors.get(newModelOptions.modelId); + + if (options) { + this.setMapOptions(options.mapOptions); + } + + if (this.undergroundFloors.has(newModelOptions.modelId)) { + this.switchOnGroundCovering(); + } + }); + } + + buildingVisibility.set(buildingId, prevModelOptions); } } - } + }); + + // this.addFloorPoi(activeFloor); + // this.clearPoiGroups(); const prevBuildingModelId = this.getBuildingModelId(prevState.activeModelId); const newBuildingModelId = this.getBuildingModelId(newState.activeModelId); @@ -140,7 +196,10 @@ export class RealtyScene { } } - this.state = newState; + this.state = { + buildingVisibility, + activeModelId: newState.activeModelId, + }; } public async init(scene: BuildingOptions[], state?: BuildingState) { diff --git a/src/types/plugin.ts b/src/types/plugin.ts index 9dacf36..ef78479 100644 --- a/src/types/plugin.ts +++ b/src/types/plugin.ts @@ -1,13 +1,5 @@ export type Id = string; -export type ColorModelString = `${'rgb' | 'hsl'}(${string})`; -export type HexColorString = `#${string}`; - -/** - * Color representation can be rgb(), hsl(), or hex value - */ -export type ColorRepresentation = ColorModelString | HexColorString | number; - /** * Configuration of the poi */ @@ -46,20 +38,14 @@ export interface ControlOptions { } /** - * Options for the highlight color of hovered models + * Options for the hover state of models */ -export interface HightlightOptions { - // TODO: MAJOR. Rename to «HighlightOptions» in the next major release. +export interface HoverOptions { /** - * Color of the hover + * Hover color * @default '#ffffff' */ - color?: ColorRepresentation; - /** - * Intensity of the color on the hover in the range from 0 to 1 - * @default 0.0 - */ - intencity: number; // TODO: MAJOR. Rename to «intensity» in the next major release. + color?: string; } /** @@ -94,9 +80,9 @@ export interface PluginOptions { */ floorsControl?: ControlOptions; /** - * Settings of the highlighted models + * Settings of the hovered models */ - hoverHighlight?: HightlightOptions; + hoverOptions?: HoverOptions; /** * Color for the ground covering when an underground floor's plan is shown. */ @@ -235,3 +221,9 @@ export interface PoiGroupOptions { */ fontColor?: string; } + +export enum ModelStatus { + NoModel, + Loading, + Loaded, +}