以Cesium.ScreenSpaceEventType.LEFT_CLICK和Cesium.ScreenSpaceEventType.MOUSE_MOVE为例,当setInputAction方法的第二个参数为Cesium.ScreenSpaceEventType.LEFT_CLICK时该方法的回调函数的click参数的类型为Cesium.ScreenSpaceEventHandler.PositionedEvent;
setInputAction方法的第二个参数为Cesium.ScreenSpaceEventType.MOUSE_MOVE时该方法的回调函数的click参数的类型为Cesium.ScreenSpaceEventHandler.MotionEvent,现在我已经将第二个参数改为动态的了,那回调函数的click参数类型怎么改呢?
我尝试将
this.handler.setInputAction((click: Cesium.ScreenSpaceEventHandler.PositionedEvent) {}, DRAW_SCREEN_SPACE_EVENT_TYPES[drawEntityParams.graphics])
改为联合类型:
type ClickType = Cesium.ScreenSpaceEventHandler.PositionedEvent | Cesium.ScreenSpaceEventHandler.MotionEvent;this.handler.setInputAction((click: ClickType) { if (click instanceof Cesium.ScreenSpaceEventHandler.MotionEvent) { // TODO return; }}, DRAW_SCREEN_SPACE_EVENT_TYPES[drawEntityParams.graphics])
click instanceof Cesium.ScreenSpaceEventHandler.MotionEvent这行ts提示
'类型“typeof ScreenSpaceEventHandler”上不存在属性“MotionEvent”',
当前完整的代码如下:
import * as Cesium from 'cesium';import type { DrawEntityParams, StyleConfig, EntityCollect, GraphicalType } from '@/components/Map/draw.d'import { generateRandomString } from '@/utils/utils';const DEFAULT_DRAW_IMAGE_ICON = new URL('@/components/Map/components/assets/point.svg', import.meta.url).href;const DRAW_SCREEN_SPACE_EVENT_TYPES: { [key: string]: Cesium.ScreenSpaceEventType;} = { point: Cesium.ScreenSpaceEventType.LEFT_CLICK, billboard: Cesium.ScreenSpaceEventType.LEFT_CLICK, polygon: Cesium.ScreenSpaceEventType.MOUSE_MOVE, polyline: Cesium.ScreenSpaceEventType.MOUSE_MOVE, box: Cesium.ScreenSpaceEventType.MOUSE_MOVE, rectangle: Cesium.ScreenSpaceEventType.MOUSE_MOVE, ellipse: Cesium.ScreenSpaceEventType.MOUSE_MOVE};export default class Draw { viewer: Cesium.Viewer; // 当前的视图容器提供程序 config: StyleConfig; // 用于创建实体的颜色配置 entityCollect: EntityCollect; // 记录已经绘制的实体 handler: Cesium.ScreenSpaceEventHandler; // 用户交互程序对象 constructor(viewer: Cesium.Viewer, config: StyleConfig) { /** * cesium实例对象 */ this.viewer = viewer; /** * 绘制要素的相关配置 * 默认配置 */ this.config = config || { borderColor: Cesium.Color.BLUE, borderWidth: 2, material: Cesium.Color.GREEN.withAlpha(0.5), } /** * 初始化保存绘制实体的集合 */ this.entityCollect = { point: [], rectangle: [], polyline: [], polygon: [], billboard: [], box: [], ellipse: [] } // 初始化用户屏幕事件对象 this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas) }; /** * 通过屏幕二维坐标获取点击位置得详细信息 */ private getPositionInfo(position: Cesium.Cartesian2, graphics?: GraphicalType) { // 点击位置笛卡尔坐标 let cartesian = this.viewer.camera.pickEllipsoid(position, this.viewer.scene.globe.ellipsoid); if (!cartesian) return; // 笛卡尔转弧度坐标 let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid, new Cesium.Cartographic()) // 经度 let lng = Cesium.Math.toDegrees(cartographic.longitude); // 维度 let lat = Cesium.Math.toDegrees(cartographic.latitude); // 海拔高度 let height = Number(this.viewer.scene.globe.getHeight(cartographic)?.toFixed(2)); // 实体的唯一标注 let id = graphics ? `${graphics}_${generateRandomString()}` : generateRandomString(); return { cartesian, cartographic, lng, lat, height, id } }; /** * 绘制实体 * */ drawEntity(drawEntityParams: DrawEntityParams) { const { graphics, _type, callback } = drawEntityParams; this.handler.destroy(); this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas) this.handler.setInputAction((click: Cesium.ScreenSpaceEventHandler.PositionedEvent) => { const positionInfo = this.getPositionInfo(click.position, drawEntityParams.graphics); if (!positionInfo) return; const { lng, lat, id, height } = positionInfo; const entity = this.viewer.entities.add({ position: Cesium.Cartesian3.fromDegrees(lng, lat, height), id, billboard: { // 图像地址,URI或Canvas的属性 image: DEFAULT_DRAW_IMAGE_ICON, // 设置颜色和透明度 color: Cesium.Color.WHITE.withAlpha(0.8), // 高度(以像素为单位) height: 50, // 宽度(以像素为单位) width: 50, // 大小是否以米为单位 sizeInMeters: false, // 相对于坐标的垂直位置 verticalOrigin: Cesium.VerticalOrigin.CENTER, // 相对于坐标的水平位置 horizontalOrigin: Cesium.HorizontalOrigin.CENTER, // 该属性指定标签在屏幕空间中距此标签原点的像素偏移量 pixelOffset: new Cesium.Cartesian2(0, -12.5), // 应用于图像的统一比例。比例大于会1.0放大标签,而比例小于会1.0缩小标签。 scale: 1.0, // 显示在距相机的距离处的属性,多少区间内是可以显示的 // distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 1500), // 是否显示 show: true, heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, disableDepthTestDistance: Number.POSITIVE_INFINITY } }); const entityInfo = { id, position: [lng, lat], entity, graphics, _type }; this.entityCollect.point.push(entityInfo); if (callback) callback(entityInfo) }, DRAW_SCREEN_SPACE_EVENT_TYPES[drawEntityParams.graphics]) this.handler.setInputAction(() => { this.handler.destroy(); }, Cesium.ScreenSpaceEventType.RIGHT_CLICK) }; /** * 获取绘制的实体集合 */ get getEntityCollect() { return this.entityCollect }}
补充:
我按照采纳的回答修改了代码,
然后在setInputAction的回调方法内通过 click instanceof Cesium.ScreenSpaceEventHandler.MotionEvent,来处理两个不同情况,但是ts提示了类型“typeof ScreenSpaceEventHandler”上不存在属性“MotionEvent”
代码如下:
this.handler.setInputAction((click: ClickType<typeof eventType>) => { if (click instanceof Cesium.ScreenSpaceEventHandler.MotionEvent) { // TODO } else { // TODO }}, DRAW_SCREEN_SPACE_EVENT_TYPES[drawEntityParams.graphics])
然后在GPT的帮助下,写了如下方法,一切就都正常了:
function isPositionedEvent(click: ClickType<Cesium.ScreenSpaceEventType>): click is Cesium.ScreenSpaceEventHandler.PositionedEvent { return 'position' in click;}this.handler.setInputAction((click: ClickType<typeof eventType>) => { if (!isPositionedEvent(click)) { console.log(click.endPosition) } else { console.log(click.position) }}, DRAW_SCREEN_SPACE_EVENT_TYPES[drawEntityParams.graphics])
我想知道为什么不能使用click instanceof Cesium.ScreenSpaceEventHandler.MotionEvent
?
GPT给我的解决方案是否合理?
我这样写复杂的方式是否合理?
因为完全可以通过以下方式分不同情况编码, 只是多写了几次this.handler.setInputAction
,比如:
const graphicsFuns = { billboard: () => { this.handler.setInputAction((click: Cesium.ScreenSpaceEventHandler.PositionedEvent) => { const positionInfo = this.getPositionInfo(click.position, drawEntityParams.graphics); if (!positionInfo) return; const { lng, lat, id, height } = positionInfo; const entity = this.viewer.entities.add({ position: Cesium.Cartesian3.fromDegrees(lng, lat, height), id, billboard: { // ... } }); const entityInfo = { id, position: [lng, lat], entity, graphics, _type }; this.entityCollect.point.push(entityInfo); if (callback) callback(entityInfo) }, Cesium.ScreenSpaceEventType.LEFT_CLICK) }, polyline: () => { this.handler.setInputAction((click: Cesium.ScreenSpaceEventHandler.MotionEvent) => { const positionInfo = this.getPositionInfo(click.endPosition, drawEntityParams.graphics); if (!positionInfo) return; const { lng, lat, id, height } = positionInfo; const entity = this.viewer.entities.add({ position: Cesium.Cartesian3.fromDegrees(lng, lat, height), id, polyline: { // ... } }); const entityInfo = { id, position: [lng, lat], entity, graphics, _type }; this.entityCollect.point.push(entityInfo); if (callback) callback(entityInfo) }, Cesium.ScreenSpaceEventType.MOUSE_MOVE) } };
写一个映射:
type EventTypeMap = { [K in Cesium.ScreenSpaceEventType]: K extends Cesium.ScreenSpaceEventType.LEFT_CLICK ? Cesium.ScreenSpaceEventHandler.PositionedEvent : Cesium.ScreenSpaceEventHandler.MotionEvent;};
再写一个类型:
type ClickType<T extends Cesium.ScreenSpaceEventType> = EventTypeMap[T];
最后:
drawEntity(drawEntityParams: DrawEntityParams) { const eventType = DRAW_SCREEN_SPACE_EVENT_TYPES[drawEntityParams.graphics]; this.handler.setInputAction((click: ClickType<typeof eventType>) => { // ... }, eventType); // ...}
比如: 我尝试过用‘url’ in opts来判断,但是这样可能会访问到原型链,用opts.url === undefined也会被ts提醒说可能没有url属性。或者用断言来回避这个问题,但是我想知道有没有更好的代码组织方式,让这个问题不发生,而不是回避它
我以为我知道指针,但当我研究运行时多态性/动态绑定时,我看到了指针的一种非常不同的用法。下面是我的3个问题,它们都是关于下面的单行代码的:
问题内容: 我有两个不同的数组,一个带有字符串,另一个带有整数。我想将它们连接成一个数组,其中每一列都具有原始数据类型。我当前执行此操作的解决方案(请参见下文)将整个数组转换为dtype = string,这似乎在内存方面效率很低。 何时和何时可能使dtypes多元化? 问题答案: 一种方法可能是使用记录数组。“列”与标准numpy数组的列不同,但是对于大多数用例来说,这就足够了: 请注意,您还可
问题内容: ./chains.go:26:10:不能在作业中使用UpperCaseHandler(typefunc(asl.MessageDelivery))作为asl.MessageHandler类型./chains.go:37:86:无法使用RepeatHandler(类型func(asl.MessageDelivery))与Repeater.ConsumeFunc的参数中的asl.Messa
为了自定义,我创建了两个函数,它们具有相同的返回类型和不同的参数,一个是单节点配置,另一个是集群配置。守则如下: 调用它们时,返回值(LettuceConnectionFactory)是另一个函数的参数。守则如下: 但是spring会报告 由于项目稍微复杂一些,所以有很多redis单服务器A、B、C。和集群服务器A、B、C...,它们有不同的ip、端口和池策略。我最初的想法是,通过将RedisCo
给定一个依赖于泛型类型参数的结构,我们可以定义一个实现依赖于该类型的关联函数吗? 我想将结构传递给例程,但根据内部类型,让关联的函数以不同的方式计算。这个例程还取决于结构中的成员,所以我宁愿不把所有东西都移到一个特质上。 例如,以下代码尝试根据所涉及的类型定义不同的函数: 这会导致编译器错误: 如果我将的定义转移到另一个特征中,我们就会遇到一个类似但不同的问题。 这会导致编译器错误: 严格来说,我