使用WebMercatorViewport的fitBounds时遇padding只读属性错误
问题:WebMercatorViewport调用fitBounds设置padding后触发只读属性错误
在使用WebMercatorViewport的fitBounds方法并传入padding参数时,初始渲染正常,但状态变化(如关闭地图、更新状态)时出现以下错误:
Uncaught TypeError: Cannot assign to read only property 'padding' of object '#<WebMercatorViewport>' at positionChildrenUnderViews (position-children-under-views.ts:60:1) at deckgl.ts:254:1 at renderWithHooks (react-dom.development.js:16175:1) at updateForwardRef (react-dom.development.js:20023:1) at beginWork (react-dom.development.js:22465:1) at HTMLUnknownElement.callCallback (react-dom.development.js:4161:1) at Object.invokeGuardedCallbackDev (react-dom.development.js:4210:1) at invokeGuardedCallback (react-dom.development.js:4274:1) at beginWork$1 (react-dom.development.js:27405:1) at performUnitOfWork (react-dom.development.js:26513:1)
使用的依赖版本:
- deck.gl: "8.8.20"
- react-map-gl: "7.0.20"
相关代码:
const initialViewState: IViewport | WebMercatorViewport = useMemo(() => { if (locationsFitBounds) { const minLat: number = Math.min(...locationsFitBounds.map((l: MapLocation) => l.latitude)) const maxLat: number = Math.max(...locationsFitBounds.map((l: MapLocation) => l.latitude)) const minLng: number = Math.min(...locationsFitBounds.map((l: MapLocation) => l.longitude)) const maxLng: number = Math.max(...locationsFitBounds.map((l: MapLocation) => l.longitude)) const southWest: [number, number] = [minLng, minLat] const northEast: [number, number] = [maxLng, maxLat] return new WebMercatorViewport({ width: 1920, height: 400 }).fitBounds([southWest, northEast], {padding: 200}) } else { return viewport } }, [locationsFitBounds, viewport]) <React.Fragment> <DeckGL width="100%" height="100%" controller={true} initialViewState={initialViewState} > <Map attributionControl={false} style={{ width: "100%", height: "100%" }} /> </DeckGL> </React.Fragment>
解决方案
问题根源是直接返回WebMercatorViewport实例给initialViewState,DeckGL内部会尝试修改该实例的padding属性,但实例的属性是只读的。需要将Viewport实例转换为普通可写对象。
修改后的代码:
const initialViewState: IViewport | Record<string, any> = useMemo(() => { if (locationsFitBounds) { const minLat: number = Math.min(...locationsFitBounds.map((l: MapLocation) => l.latitude)) const maxLat: number = Math.max(...locationsFitBounds.map((l: MapLocation) => l.latitude)) const minLng: number = Math.min(...locationsFitBounds.map((l: MapLocation) => l.longitude)) const maxLng: number = Math.max(...locationsFitBounds.map((l: MapLocation) => l.longitude)) const southWest: [number, number] = [minLng, minLat] const northEast: [number, number] = [maxLng, maxLat] // 将Viewport实例转为普通可写对象 const viewportInstance = new WebMercatorViewport({ width: 1920, height: 400 }) return viewportInstance.fitBounds([southWest, northEast], {padding: 200}).toJSON() } else { // 若viewport是实例,同样转为普通对象 return viewport instanceof WebMercatorViewport ? viewport.toJSON() : viewport } }, [locationsFitBounds, viewport])
关键说明
WebMercatorViewport实例的内部属性被设置为只读,DeckGL在状态更新时需要修改padding等属性,直接传入实例会触发只读属性错误。toJSON()方法会将实例转换为包含所有视图状态(如经度、纬度、缩放级别、padding等)的普通JavaScript对象,该对象的属性可正常修改,符合DeckGL对initialViewState的要求。
内容的提问来源于stack exchange,提问作者Ar26
相关产品推荐
相关产品推荐

