Skip to main content

第七节:交互工具管理器设计和实现

对于地图上直接的数据展示,我们已经都完成了,现在要做的就是交互工具的开发。

UI支持配置的交互工具

在第一节中,我们放入了占位的工具栏UI组件,实际上对于简单的工具,我们完全可以直接在UI中把用到的工具都写上,但是这里我们为了与图层管理器的逻辑相匹配,也把交互工具的业务逻辑作为一个ViewModel提取出来,通过配置驱动UI,然后ViewModel响应UI的变化。

定义交互工具对象模型

在最简单的情况下,一个交互工具至少需要有一个名称,和一个唯一ID用来标记这个工具,因此定义如下:

interface IToolItem {
    value: string;
    name: string;
}

这样,对于一组工具,我们就可以通过一个IToolItem[]这样的数组来表示。

设计交互工具管理器

既然要把业务逻辑提出来,那么就要有一个对象来管理所有的工具,响应UI操作,激活或者停用选择的工具,这里我们构建一个ToolStore类来做这样的事:

class ToolStore {

    public tools: IToolItem[] = [
    ];

    public defaultToolValue = "";

    public switchTool(value: string) {
        
    }

}

在上面的交互工具管理类中,提供了一个tools数组,用来表示系统中所有的地图工具,然后defaultToolValue表示系统加载时默认选中的工具,当前还没有定义工具,所以使用空字符串作为值,再提供一个切换选中工具的外部方法来响应UI的变化。

在UI中接入工具管理器

要在UI中接入工具管理器,需要先对UI组件的输入属性进行修改,增加一个toolStore用来传入工具管理器,所以先修改IAppProps如下:

interface IAppProps {
    layerStore: LayerStore
    toolStore: ToolStore
}

然后在渲染组件代码的地方创建管理器实例并传入:

const load = async () => {
    ...
    const toolStore = new ToolStore();
    ReactDOM.render(<AviationApp
        layerStore={layerStore}
        toolStore={toolStore}
    />, document.getElementById("plugins"));
}

在UI组件中加入以下代码,用来生成工具按钮组,然后在render中接入:

const AviationApp: React.FC<IAppProps> = (props: IAppProps) => {
    ...

    const toolRadios = props.toolStore.tools.map(t => {
        return <Radio value={t.value} style={{ color: "rgb(170,170,170)" }}>{t.name}</Radio>
    });

    return (
        <div>
            ...
            <Radio.Group
                style={{ position: "fixed", zIndex: "999", left: "0px", bottom: "30px", width: "110px",backgroundColor: "rgb(3,21,39)"  }}
            >
                <Space direction="vertical" >
                    {toolRadios}
                </Space>
            </Radio.Group>
        </div>

    )
}

这样,我们的交互工具管理器就创建好,并且跟UI进行了绑定。

增加交互工具配置

我们可以把本项目中要用的工具,先增加到配置,这样可以在界面上显示出来,虽然现在还没有实际的功能,更新工具管理器中tools的配置如下:

public tools: IToolItem[] = [
        {
            name: "漫游",
            value: "pan"
        },
        {
            name: "任意点预报",
            value: "forecast"
        },
        {
            name: "区域绘制",
            value: "draw_poly"
        },
        {
            name: "测距",
            value: "distance"
        }
];

保存后,工具按钮就能被显示出来了:

监测预报-第七节-交互工具.png

支持默认选中,响应切换

这时候我们发现,之前设置的默认选中的功能还没有使用,我们需要定义一个内部状态,表示当前选中的tool的value,在UI组件中加入如下代码:

const [tool, setTool] = useState(props.toolStore.defaultToolValue);

然后将tool值绑定到工具按钮组,更新按钮组的代码如下:

<Radio.Group
    style={{ position: "fixed", zIndex: "999", left: "0px", bottom: "30px", width: "110px", backgroundColor: "rgb(3,21,39)" }}
    value={tool}
>
    <Space direction="vertical" >
        {toolRadios}
    </Space>
</Radio.Group>

这时候,地图上的工具按钮组已经默认选中了漫游了!

此时我们再点击其他按钮,发现没法切换了,这是因为一旦组件绑定了外部值之后,就需要通过监听数据的变化来更新这个绑定的值,否则绑定的值不变,界面自然不会改变,因此,我们需要一个方法来响应按钮切换的事件,同时在这个方法里面调用外部的切换地图工具的方法:

function selectTool(value: string) {
    setTool(value);
    props.toolStore.switchTool(value);
}

...
return (
	...
  <Radio.Group
      style={{ position: "fixed", zIndex: "999", left: "0px", bottom: "30px", width: "110px", backgroundColor: "rgb(3,21,39)" }}
      value={tool}
      onChange={args => selectTool(args.target.value)}
  >
      <Space direction="vertical" >
          {toolRadios}
      </Space>
  </Radio.Group>
   ...
);

这时候再切换按钮,就能够正常响应了。

支持取消工具选中

虽然我们提供了漫游工具,表示对地图的平移和缩放操作,但是我们也可以不提供这个工具,即默认就是对地图的平移和缩放,这时候,我们就可以允许点击当前选中的按钮来取消当前的选中,这需要响应按钮的click事件,可以在创建Radio的时候进行,更新创建UI组件的代码如下:

...
function clickTool(value: string) {
    if (value === tool) {
        setTool(undefined);
        props.toolStore.switchTool(null);
    }
}

...

const toolRadios = props.toolStore.tools.map(t => {
    return <Radio onClick={args => clickTool(args.target.value)} value={t.value} style={{ color: "rgb(170,170,170)" }}>{t.name}</Radio>
});

这时候再点击当前选中的工具,就会发现选中被取消了。(该功能如果提供了漫游组件,通常是不需要的)

总结

在本节中,我们把地图工具的逻辑进行了创建,虽然还没有真正的激活地图工具,但是UI和业务逻辑的绑定已经完成,后面只要根据UI选中的值创建不同的地图工具即可。

完整代码和效果