第七节:交互工具管理器设计和实现
对于地图上直接的数据展示,我们已经都完成了,现在要做的就是交互工具的开发。
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"
}
];
保存后,工具按钮就能被显示出来了:
支持默认选中,响应切换
这时候我们发现,之前设置的默认选中的功能还没有使用,我们需要定义一个内部状态,表示当前选中的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选中的值创建不同的地图工具即可。
No Comments