constructor()

in packages/core/base/src/Polaris.ts [43:198]


	constructor(props: PolarisProps) {
		// @note do not pass props into AbstractLayer constructor
		super()

		const _props = {
			...defaultProps,
			...props,
		}

		this.setProps(_props)

		this.#width = _props.width
		this.#height = _props.height
		this.#ratio = _props.ratio

		this.watchProps(['width', 'height', 'ratio'], (e) => {
			this.resize(
				this.getProp('width') ?? this.#width,
				this.getProp('height') ?? this.#height,
				this.getProp('ratio') ?? this.#ratio
			)
		})

		/**
		 * init timeline
		 */
		const timeline =
			_props.timeline ||
			new Timeline({
				duration: Infinity,
				// pauseWhenInvisible: false, // 检测标签页是否隐藏,已知在一些环境中不可用,建议关闭
				openStats: false,
				autoRelease: true, // 自动回收过期的track
				maxStep: 1000, // 最大帧长
				maxFPS: 30, // 最大帧率
				// ignoreErrors: true, // 出错后是否停止
			})

		/**
		 * 等到全部初始化完成后再开始计时运行
		 */
		if (_props.autoplay) {
			if (_props.timeline) {
				console.warn('Polaris:: autoplay will be ignored for custom timeline.')
			} else {
				setTimeout(() => {
					if (this.timeline.playing)
						throw new Error('Polaris:: Timeline is already playing. Autoplay may restart timeline.')

					this.timeline.play()
				})
			}
		}

		/**
		 * init projection
		 */
		const projection =
			_props.projection ||
			new MercatorProjection({
				center: [0, 0],
			})

		this.timeline = timeline
		this.projection = projection

		/**
		 * init html / canvas
		 */

		/**
		 * init CameraProxy
		 * proxy Renderer.camera
		 */
		const { fov, center, zoom, pitch, rotation } = _props
		const cameraProxy = new AnimatedCameraProxy({
			cameraFOV: fov as number,
			timeline: timeline as any, // AnimatedCameraProxy use old version of timeline but only unchanged api
			canvasWidth: this.canvasWidth,
			canvasHeight: this.canvasHeight,
			ratio: this.#ratio,
			onUpdate: () => {},
		})

		// cameraProxy config props listener
		this.watchProps(
			['zoomLimit', 'pitchLimit'],
			(e) => {
				if (!e.initial) console.warn('Changing camera limit after init is deprecated.')

				cameraProxy['limit'].zoom = this.getProp('zoomLimit')
				cameraProxy['limit'].pitch = this.getProp('pitchLimit')
			},
			true
		)

		// 更新相机初始状态
		const geo = this.projection.project(...(center as [number, number, number]))
		const states: GeographicStates = {
			center: geo,
			pitch: pitch || 0 - 0,
			rotation: rotation || 0 - 0,
			zoom: zoom || 0 - 0,
		}
		// 使用无缓动的setStates方法,因为此时timeline可能还未start
		cameraProxy.setGeographicStates(states)

		// this.cameraman = new Cameraman({ camera: cameraProxy })

		this.cameraProxy = cameraProxy

		// handle projection alignment and ViewChangeEvent, on every frame
		this.addEventListener('beforeRender', (e) => {
			this.traverse((layer) => {
				// projection alignment
				// skip root
				if (layer.parent) {
					const parentProjection = resolveProjection(layer.parent)!
					const projection = resolveProjection(layer)!

					const visualCenter = this.getGeoCenter()
					const alignmentMatrix = Coordinator.getRelativeMatrix(parentProjection, projection, [
						visualCenter[0],
						visualCenter[1],
					])
					layer.updateAlignmentMatrix(alignmentMatrix)
				}

				// ViewChange detection
				checkViewChange(this, layer)
			})
		})

		// 基本绘制循环
		this.timeline.addTrack({
			id: 'polaris_render_loop',
			duration: Infinity,
			loop: false,
			onUpdate: () => {
				if (_props.asyncRendering) {
					setTimeout(() => {
						this.tick()
					})
				} else {
					this.tick()
				}
			},
		})

		// static props(change these props will not be reacted or even cause problems)
		this.watchProps(STATIC_PROPS, (e) => {
			const msg = `Do not modify static props: [${e.changedKeys.join(',')}]`
			this.dispatchEvent({ type: 'error', error: new Error(msg) })
			console.error(msg)
		})
	}