网站搜索

typescript - 为什么 SceneView 没有正确渲染


我正在熟悉 ArcGIS JS API 4.x 版本,并认为实现一个小部件是练习 TypeScript 和小部件架构的好方法,该小部件可以在当前的 MapView 上显示一个 SceneView。

/// <amd-dependency path="esri/core/tsSupport/declareExtendsHelper" name="__extends" />
/// <amd-dependency path="esri/core/tsSupport/decorateHelper" name="__decorate" />

import EsriMap from 'esri/Map';
import SceneView = require("esri/views/SceneView");

import { aliasOf, declared, property, subclass } from "esri/core/accessorSupport/decorators";

// building Widgets
import Widget = require("esri/widgets/Widget");
import { accessibleHandler, renderable, cssTransition, tsx } from "esri/widgets/support/widget";


// CSS class lookup object
const CSS = {
    base: "app-sceneView",
    iconClass: "esri-icon-layer-list"
};

@subclass("Widgets.SceneViewWidget")
export default class SceneViewWidget extends declared(Widget) {

    constructor(params?: any) {
        super();
    }

    //icon class for the expand widget(!)
    @property()
    iconClass = CSS.iconClass;

    map: EsriMap = null;

    _bindView() {
        const view = new SceneView({
            map: this.map,
            container: "sceneView-container"
        });
    }

    render() {
        return <div id="sceneView-container" class={CSS.base} afterCreate={this._bindView}></div>
    }
}

小部件的容器在期望的位置渲染正常,并且控件,如缩放按钮也在,但地图没有渲染,当像这样调用时:

const map = new EsriMap({
    basemap: "streets",
    ground: "hybrid"
});
const mapView = new MapView({
    map: map,
    container: "mapView",
    center: [-118.244, 34.052],
    zoom: 12,
    ui: {
        components: ["zoom", "compass"]
    }
});

const sceneView = new SceneViewWidget({
    map: map
});

mapView.ui.add(sceneView, "top-right");

如你所见。div 的边界在那里,控件已经渲染,归属信息也在。只有 SceneView 本身缺失。为什么会这样?

答案#1

上述 TypeScript 代码中 SceneView 未渲染的原因是 _bindView() 中的 this.map 为 null,因为 this 是函数作用域而非对象作用域。

_bindView() {
  new SceneView({
    map: this.map,
    container: "sceneView-container"
  });
}

可以通过在 render() 中传递正确作用域的 _bindView() 函数来修复这个问题:

render() {
  return <div afterCreate={this._bindView.bind(this)} id="sceneView-container" class={CSS.base}></div>;
}

或

render() {
  return <div afterCreate={() => this._bindView()} id="sceneView-container" class={CSS.base}></div>;
}

请查看以下 CodePen 获取有效演示(纯 JavaScript):https://codepen.io/arnofiva/pen/1a801eca6a01ca9bf7625f89f906b6e7

此外,我还建议更改上面代码中的以下内容:

  • 将 SceneView 容器 sceneView-container 包裹在一个额外的 <div> 中,以避免 Widget 和 SceneView 共享相同的元素:
<div><div id="sceneView-container" class={CSS.base} afterCreate={this._bindView.bind(this)}></div></div>
  • 为 Map.ground 提供一个有效的值,例如。
const map = new EsriMap({
  basemap: "streets",
  ground: "world-elevation"
});
  • 为 MapView 和 SceneView 提供独立的 Map 实例,避免两个视图相互干扰。只需使用 new EsriMap(...) 创建一个新的即可。