UNPKG

react-bkoi-gl

Version:

React components for Barikoi GL JS

387 lines 11.1 kB
import { transformToViewState, applyViewStateToTransform } from "../utils/transform.js"; import { normalizeStyle } from "../utils/style-utils.js"; import { deepEqual } from "../utils/deep-equal.js"; const DEFAULT_STYLE = { version: 8, sources: {}, layers: [] }; const pointerEvents = { mousedown: "onMouseDown", mouseup: "onMouseUp", mouseover: "onMouseOver", mousemove: "onMouseMove", click: "onClick", dblclick: "onDblClick", mouseenter: "onMouseEnter", mouseleave: "onMouseLeave", mouseout: "onMouseOut", contextmenu: "onContextMenu", touchstart: "onTouchStart", touchend: "onTouchEnd", touchmove: "onTouchMove", touchcancel: "onTouchCancel" }; const cameraEvents = { movestart: "onMoveStart", move: "onMove", moveend: "onMoveEnd", dragstart: "onDragStart", drag: "onDrag", dragend: "onDragEnd", zoomstart: "onZoomStart", zoom: "onZoom", zoomend: "onZoomEnd", rotatestart: "onRotateStart", rotate: "onRotate", rotateend: "onRotateEnd", pitchstart: "onPitchStart", pitch: "onPitch", pitchend: "onPitchEnd" }; const otherEvents = { wheel: "onWheel", boxzoomstart: "onBoxZoomStart", boxzoomend: "onBoxZoomEnd", boxzoomcancel: "onBoxZoomCancel", resize: "onResize", load: "onLoad", render: "onRender", idle: "onIdle", remove: "onRemove", data: "onData", styledata: "onStyleData", sourcedata: "onSourceData", error: "onError" }; const settingNames = ["minZoom", "maxZoom", "minPitch", "maxPitch", "maxBounds", "projection", "renderWorldCopies"]; const handlerNames = ["scrollZoom", "boxZoom", "dragRotate", "dragPan", "keyboard", "doubleClickZoom", "touchZoomRotate", "touchPitch"]; export default class Maplibre { _map = null; _internalUpdate = false; _hoveredFeatures = null; _propsedCameraUpdate = null; _styleComponents = {}; static savedMaps = []; constructor(MapClass, props, container) { this._MapClass = MapClass; this.props = props; this._initialize(container); } get map() { return this._map; } setProps(props) { const oldProps = this.props; this.props = props; const settingsChanged = this._updateSettings(props, oldProps); const sizeChanged = this._updateSize(props); const viewStateChanged = this._updateViewState(props); this._updateStyle(props, oldProps); this._updateStyleComponents(props); this._updateHandlers(props, oldProps); if (settingsChanged || sizeChanged || viewStateChanged && !this._map.isMoving()) { this.redraw(); } } static reuse(props, container) { const that = Maplibre.savedMaps.pop(); if (!that) { return null; } const map = that.map; const oldContainer = map.getContainer(); container.className = oldContainer.className; while (oldContainer.childNodes.length > 0) { container.appendChild(oldContainer.childNodes[0]); } map._container = container; const resizeObserver = map._resizeObserver; if (resizeObserver) { resizeObserver.disconnect(); resizeObserver.observe(container); } that.setProps({ ...props, styleDiffing: false }); map.resize(); const { initialViewState } = props; if (initialViewState) { if (initialViewState.bounds) { map.fitBounds(initialViewState.bounds, { ...initialViewState.fitBoundsOptions, duration: 0 }); } else { that._updateViewState(initialViewState); } } if (map.isStyleLoaded()) { map.fire("load"); } else { map.once("style.load", () => map.fire("load")); } map._update(); return that; } _initialize(container) { const { props } = this; const { mapStyle = DEFAULT_STYLE } = props; const mapOptions = { ...props, ...props.initialViewState, container, style: normalizeStyle(mapStyle) }; const viewState = mapOptions.initialViewState || mapOptions.viewState || mapOptions; Object.assign(mapOptions, { center: [viewState.longitude || 0, viewState.latitude || 0], zoom: viewState.zoom || 0, pitch: viewState.pitch || 0, bearing: viewState.bearing || 0 }); if (props.gl) { const getContext = HTMLCanvasElement.prototype.getContext; HTMLCanvasElement.prototype.getContext = () => { HTMLCanvasElement.prototype.getContext = getContext; return props.gl; }; } const map = new this._MapClass(mapOptions); if (viewState.padding) { map.setPadding(viewState.padding); } if (props.cursor) { map.getCanvas().style.cursor = props.cursor; } map.transformCameraUpdate = this._onCameraUpdate; map.on("style.load", () => { this._styleComponents = { light: map.getLight(), sky: map.getSky(), projection: map.getProjection?.(), terrain: map.getTerrain() }; this._updateStyleComponents(this.props); }); map.on("sourcedata", () => { this._updateStyleComponents(this.props); }); for (const eventName in pointerEvents) { map.on(eventName, this._onPointerEvent); } for (const eventName in cameraEvents) { map.on(eventName, this._onCameraEvent); } for (const eventName in otherEvents) { map.on(eventName, this._onEvent); } this._map = map; } recycle() { const container = this.map.getContainer(); const children = container.querySelector("[mapboxgl-children]"); children?.remove(); Maplibre.savedMaps.push(this); } destroy() { this._map.remove(); } redraw() { const map = this._map; if (map.style) { if (map._frame) { map._frame.cancel(); map._frame = null; } map._render(); } } _updateSize(nextProps) { const { viewState } = nextProps; if (viewState) { const map = this._map; if (viewState.width !== map.transform.width || viewState.height !== map.transform.height) { map.resize(); return true; } } return false; } _updateViewState(nextProps) { const map = this._map; const tr = map.transform; const isMoving = map.isMoving(); if (!isMoving) { const changes = applyViewStateToTransform(tr, nextProps); if (Object.keys(changes).length > 0) { this._internalUpdate = true; map.jumpTo(changes); this._internalUpdate = false; return true; } } return false; } _updateSettings(nextProps, currProps) { const map = this._map; let changed = false; for (const propName of settingNames) { if (propName in nextProps && !deepEqual(nextProps[propName], currProps[propName])) { changed = true; const setter = map[`set${propName[0].toUpperCase()}${propName.slice(1)}`]; setter?.call(map, nextProps[propName]); } } return changed; } _updateStyle(nextProps, currProps) { if (nextProps.cursor !== currProps.cursor) { this._map.getCanvas().style.cursor = nextProps.cursor || ""; } if (nextProps.mapStyle !== currProps.mapStyle) { const { mapStyle = DEFAULT_STYLE, styleDiffing = true } = nextProps; const options = { diff: styleDiffing }; if ("localIdeographFontFamily" in nextProps) { options.localIdeographFontFamily = nextProps.localIdeographFontFamily; } this._map.setStyle(normalizeStyle(mapStyle), options); } } _updateStyleComponents(_ref) { let { light, projection, sky, terrain } = _ref; const map = this._map; const currProps = this._styleComponents; if (map.style._loaded) { if (light && !deepEqual(light, currProps.light)) { currProps.light = light; map.setLight(light); } if (projection && !deepEqual(projection, currProps.projection) && projection !== currProps.projection?.type) { currProps.projection = typeof projection === "string" ? { type: projection } : projection; map.setProjection?.(currProps.projection); } if (sky && !deepEqual(sky, currProps.sky)) { currProps.sky = sky; map.setSky(sky); } if (terrain !== undefined && !deepEqual(terrain, currProps.terrain)) { if (!terrain || map.getSource(terrain.source)) { currProps.terrain = terrain; map.setTerrain(terrain); } } } } _updateHandlers(nextProps, currProps) { const map = this._map; for (const propName of handlerNames) { const newValue = nextProps[propName] ?? true; const oldValue = currProps[propName] ?? true; if (!deepEqual(newValue, oldValue)) { if (newValue) { map[propName].enable(newValue); } else { map[propName].disable(); } } } } _onEvent = e => { const cb = this.props[otherEvents[e.type]]; if (cb) { cb(e); } else if (e.type === "error") { console.error(e.error); } }; _onCameraEvent = e => { if (this._internalUpdate) { return; } e.viewState = this._propsedCameraUpdate || transformToViewState(this._map.transform); const cb = this.props[cameraEvents[e.type]]; if (cb) { cb(e); } }; _onCameraUpdate = tr => { if (this._internalUpdate) { return tr; } this._propsedCameraUpdate = transformToViewState(tr); return applyViewStateToTransform(tr, this.props); }; _queryRenderedFeatures(point) { const map = this._map; const { interactiveLayerIds = [] } = this.props; try { return map.queryRenderedFeatures(point, { layers: interactiveLayerIds.filter(map.getLayer.bind(map)) }); } catch { return []; } } _updateHover(e) { const { props } = this; const shouldTrackHoveredFeatures = props.interactiveLayerIds && (props.onMouseMove || props.onMouseEnter || props.onMouseLeave); if (shouldTrackHoveredFeatures) { const eventType = e.type; const wasHovering = this._hoveredFeatures?.length > 0; const features = this._queryRenderedFeatures(e.point); const isHovering = features.length > 0; if (!isHovering && wasHovering) { e.type = "mouseleave"; this._onPointerEvent(e); } this._hoveredFeatures = features; if (isHovering && !wasHovering) { e.type = "mouseenter"; this._onPointerEvent(e); } e.type = eventType; } else { this._hoveredFeatures = null; } } _onPointerEvent = e => { if (e.type === "mousemove" || e.type === "mouseout") { this._updateHover(e); } const cb = this.props[pointerEvents[e.type]]; if (cb) { if (this.props.interactiveLayerIds && e.type !== "mouseover" && e.type !== "mouseout") { e.features = this._hoveredFeatures || this._queryRenderedFeatures(e.point); } cb(e); delete e.features; } }; } //# sourceMappingURL=maplibre.js.map