From cb196833a0c72cdeb26db78ca407b9403db99e21 Mon Sep 17 00:00:00 2001 From: RoXoM Date: Sun, 9 Jul 2023 16:54:19 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20Marker=20=E7=BB=84?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/AMapMarker/AMapMarker.tsx | 124 ++++++ src/components/AMapMarker/index.ts | 3 + src/components/AMapMarker/interface.d.ts | 27 ++ .../AMapMarker/stories/AMapMarker.stories.tsx | 364 ++++++++++++++++++ src/index.ts | 1 + 5 files changed, 519 insertions(+) create mode 100644 src/components/AMapMarker/AMapMarker.tsx create mode 100644 src/components/AMapMarker/index.ts create mode 100644 src/components/AMapMarker/interface.d.ts create mode 100644 src/components/AMapMarker/stories/AMapMarker.stories.tsx diff --git a/src/components/AMapMarker/AMapMarker.tsx b/src/components/AMapMarker/AMapMarker.tsx new file mode 100644 index 0000000..d9e107b --- /dev/null +++ b/src/components/AMapMarker/AMapMarker.tsx @@ -0,0 +1,124 @@ +/* eslint-disable react/default-props-match-prop-types */ +import type { FC } from 'react'; +import { + useCallback, +} from 'react'; + +import useAMapPluginInstance from '../../hooks/useAMapPluginInstance'; +import useAMapControlBinder from '../../hooks/useAMapControlBinder'; +import useAMapEventBinder from '../../hooks/useAMapEventBinder'; +import useSetter from '../../hooks/useSetter'; +import useVisible from '../../hooks/useVisible'; + +import type { AMapMarkerProps } from './interface'; + +/** + * More Details See: + * + * https://a.amap.com/jsapi/static/doc/index.html?v=2#marker + * https://lbs.amap.com/api/javascript-api/reference/overlay#marker + * + */ + +const defaultProps = { + anchor: 'center' as AMapMarkerProps['anchor'], + cursor: 'pointer', + angle: 0, + zIndex: 12, + // isTop: false, + clickable: true, + draggable: false, + visible: true, +}; + +const AMapMarker: FC = ({ + title, + icon, + zooms, + label, + clickable, + draggable, + cursor, + extData, + position, + anchor, + offset, + angle, + size, + zIndex, + content, + // + // isTop, // 推荐 z-index 控制 + visible, + // event + onHide, + onShow, + onClick, + onDBLClick, + onRightClick, + onMousemove, + onMousedown, + onMouseup, + onMouseover, + onMouseout, + onTouchstart, + onTouchmove, + onTouchend, + onDragstart, + onDragging, + onDragend, + onMoving, + onMoveEnd, + onMoveAlong, +}) => { + const initInstance = useCallback((AMap) => new AMap!.Marker({ zooms }), [zooms]); + const curInstance = useAMapPluginInstance('Marker', initInstance); + + useVisible(curInstance, !!visible); + + useSetter>(curInstance, 'setPosition', position!); + + useSetter>(curInstance, 'setTitle', title!); + // useSetter>(curInstance, 'setHeight', height); + useSetter>(curInstance, 'setIcon', icon!); + useSetter>(curInstance, 'setLabel', label!); + useSetter>(curInstance, 'setClickable', clickable); + useSetter>(curInstance, 'setDraggable', draggable!); + // useSetter>(curInstance, 'setTop', isTop); + useSetter>(curInstance, 'setCursor', cursor!); + useSetter>(curInstance, 'setExtData', extData); + useSetter>(curInstance, 'setAnchor', anchor!); + useSetter>(curInstance, 'setOffset', offset!); + useSetter>(curInstance, 'setAngle', angle!); + useSetter>(curInstance, 'setSize', size!); + useSetter>(curInstance, 'setzIndex', zIndex!); + useSetter>(curInstance, 'setContent', content); + + useAMapEventBinder(curInstance, 'show', onShow); + useAMapEventBinder(curInstance, 'hide', onHide); + useAMapEventBinder(curInstance, 'click', onClick); + useAMapEventBinder(curInstance, 'dblclick', onDBLClick); + useAMapEventBinder(curInstance, 'rightclick', onRightClick); + useAMapEventBinder(curInstance, 'mousemove', onMousemove); + useAMapEventBinder(curInstance, 'mousedown', onMousedown); + useAMapEventBinder(curInstance, 'mouseup', onMouseup); + useAMapEventBinder(curInstance, 'mouseover', onMouseover); + useAMapEventBinder(curInstance, 'mouseout', onMouseout); + useAMapEventBinder(curInstance, 'touchstart', onTouchstart); + useAMapEventBinder(curInstance, 'touchmove', onTouchmove); + useAMapEventBinder(curInstance, 'touchend', onTouchend); + useAMapEventBinder(curInstance, 'dragstart', onDragstart); + useAMapEventBinder(curInstance, 'dragging', onDragging); + useAMapEventBinder(curInstance, 'dragend', onDragend); + useAMapEventBinder(curInstance, 'moving', onMoving); + useAMapEventBinder(curInstance, 'moveend', onMoveEnd); + useAMapEventBinder(curInstance, 'movealong', onMoveAlong); + + useAMapControlBinder(curInstance); + + return null; +}; + +AMapMarker.defaultProps = defaultProps; + +export default AMapMarker; diff --git a/src/components/AMapMarker/index.ts b/src/components/AMapMarker/index.ts new file mode 100644 index 0000000..1f37f1b --- /dev/null +++ b/src/components/AMapMarker/index.ts @@ -0,0 +1,3 @@ +export type { AMapMarkerProps } from './interface'; + +export { default } from './AMapMarker'; diff --git a/src/components/AMapMarker/interface.d.ts b/src/components/AMapMarker/interface.d.ts new file mode 100644 index 0000000..51e7692 --- /dev/null +++ b/src/components/AMapMarker/interface.d.ts @@ -0,0 +1,27 @@ +export type AMapMarkerProps = AMap.MarkerOptions & { + position: AMap.Vector2; + anchor?: 'top-left' | 'top-center' | 'top-right' | 'middle-left' | 'center' | 'middle-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'; + // + visible?: boolean; + // isTop?: boolean; + // + onHide?: (event?: any) => void; + onShow?: (event?: any) => void; + onClick?: (event?: any) => void + onDBLClick?: (event?: any) => void + onRightClick?: (event?: any) => void + onMousemove?: (event?: any) => void + onMousedown?: (event?: any) => void + onMouseup?: (event?: any) => void + onMouseover?: (event?: any) => void + onMouseout?: (event?: any) => void + onTouchstart?: (event?: any) => void + onTouchmove?: (event?: any) => void + onTouchend?: (event?: any) => void + onDragstart?: (event?: any) => void + onDragging?: (event?: any) => void + onDragend?: (event?: any) => void + onMoving?: (event?: any) => void + onMoveEnd?: (event?: any) => void + onMoveAlong?: (event?: any) => void +}; diff --git a/src/components/AMapMarker/stories/AMapMarker.stories.tsx b/src/components/AMapMarker/stories/AMapMarker.stories.tsx new file mode 100644 index 0000000..7a0c20e --- /dev/null +++ b/src/components/AMapMarker/stories/AMapMarker.stories.tsx @@ -0,0 +1,364 @@ +import React from 'react'; +import { renderToString } from 'react-dom/server'; +import { Story, Meta } from '@storybook/react'; +import { actions } from '@storybook/addon-actions'; + +import { AMapToolBar, AMapMarker, AMapMarkerProps } from 'index'; +import { withAMapContainer } from '../../AMapMap/stories/AMapMap.stories'; + +const eventHandler = actions( + 'onHide', + 'onShow', + 'onClick', + 'onDBLClick', + 'onRightClick', + 'onMousemove', + 'onMousedown', + 'onMouseup', + 'onMouseover', + 'onMouseout', + 'onTouchstart', + 'onTouchmove', + 'onTouchend', + 'onDragstart', + 'onDragging', + 'onDragend', + 'onMoving', + 'onMoveEnd', + 'onMoveAlong', +); + +export default { + title: '组件(Components)/覆盖物(Overlay)/AMapMarker', + component: AMapMarker, + decorators: [withAMapContainer], + args: { + position: [116.39, 39.9], + title: '自定义的 Title', + anchor: 'center', + onShow: eventHandler.onShow, + onHide: eventHandler.onHide, + }, + argTypes: { + position: { + description: '点标记在地图上显示的位置', + type: { required: true, summary: 'LngLatLike' }, + control: 'array', + }, + title: { + description: '鼠标滑过点标记时的文字提示。不设置则鼠标滑过点标无文字提示。', + type: { required: false, summary: 'string' }, + control: 'text', + }, + content: { + description: '点标记显示内容。可以是HTML要素字符串或者HTML DOM对象。content有效时,icon属性将被覆盖。', + type: { required: false, summary: 'string | HTMLElement' }, + control: 'text', + }, + icon: { + description: '在点标记中显示的图标。有合法的 content 内容设置时,此属性无效。', + type: { required: false, summary: 'AMap.Icon | string' }, + control: 'text', + }, + label: { + description: '设置点标记文本标签内容', + type: { required: false, summary: 'LabelOptions' }, + control: false, + }, + zooms: { + description: '设置显示级别范围。注意:当前实现变更 zooms 时会触发重新创建实例。', + type: { required: false, summary: 'Vector2', defaultValue: [2, 20] }, + table: { defaultValue: [2, 20] }, + control: 'array', + }, + clickable: { + description: '点标记是否可点击', + type: { required: false, summary: 'boolean', defaultValue: true }, + table: { defaultValue: true }, + control: 'boolean', + }, + draggable: { + description: '点标记是否可拖拽', + type: { required: false, summary: 'boolean', defaultValue: false }, + table: { defaultValue: false }, + control: 'boolean', + }, + cursor: { + description: '指定鼠标悬停时的鼠标样式', + type: { required: false, summary: 'string' }, + control: 'text', + }, + extData: { + description: '设置用户自定义属性', + type: { required: false, summary: 'Object' }, + control: false, + }, + anchor: { + description: '设置点标记锚点', + type: { required: false, summary: 'boolean', defaultValue: 'center' }, + table: { defaultValue: 'center' }, + control: { + type: 'select', + options: [ + 'top-left', + 'top-center', + 'top-right', + 'middle-left', + 'center', + 'middle-right', + 'bottom-left', + 'bottom-center', + 'bottom-right', + ], + }, + }, + offset: { + description: '覆盖物偏移量', + type: { required: false, summary: 'Pixel | Vector2', defaultValue: [0, 0] }, + table: { defaultValue: [0, 0] }, + control: 'array', + }, + angle: { + description: '点标记的旋转角度', + type: { required: false, summary: 'number', defaultValue: 0 }, + table: { defaultValue: 0 }, + control: { type: 'number', min: 0, max: 360 }, + }, + size: { + description: '尺寸', + type: { required: false, summary: 'Size | Vector2' }, + // table: { defaultValue: [36, 36] }, + control: 'array', + }, + zIndex: { + description: '多边形覆盖物的叠加顺序', + type: { required: false, summary: 'number', defaultValue: 12 }, + table: { defaultValue: 12 }, + control: { + type: 'number', + step: 1, + }, + }, + // + visible: { + description: '显示或隐藏', + type: { required: false, summary: 'boolean', defaultValue: true }, + table: { defaultValue: true }, + control: 'boolean', + }, + // + onShow: { + description: '显示,回调函数', + type: { required: false, summary: '(event: any) => void' }, + control: false, + }, + onHide: { + description: '隐藏,回调函数', + type: { required: false, summary: '(event: any) => void' }, + control: false, + }, + onClick: { + description: '左键单击,回调函数', + type: { required: false, summary: '(event: any) => void' }, + control: false, + }, + onDBLClick: { + description: '左键双击,回调函数', + type: { required: false, summary: '(event: any) => void' }, + control: false, + }, + onRightClick: { + description: '右键单击,回调函数', + type: { required: false, summary: '(event: any) => void' }, + control: false, + }, + onMousemove: { + description: '鼠标移动', + type: { required: false, summary: '(event: any) => void' }, + control: false, + }, + onMousedown: { + description: '鼠标按下,回调函数', + type: { required: false, summary: '(event: any) => void' }, + control: false, + }, + onMouseup: { + description: '鼠标抬起,回调函数', + type: { required: false, summary: '(event: any) => void' }, + control: false, + }, + onMouseover: { + description: '鼠标经过,回调函数', + type: { required: false, summary: '(event: any) => void' }, + control: false, + }, + onMouseout: { + description: '鼠标移出,回调函数', + type: { required: false, summary: '(event: any) => void' }, + control: false, + }, + onTouchstart: { + description: '触摸开始,回调函数,仅移动设备有效', + type: { required: false, summary: '(event: any) => void' }, + control: false, + }, + onTouchmove: { + description: '触摸移动,回调函数,仅移动设备有效', + type: { required: false, summary: '(event: any) => void' }, + control: false, + }, + onTouchend: { + description: '触摸结束,回调函数,仅移动设备有效', + type: { required: false, summary: '(event: any) => void' }, + control: false, + }, + onDragstart: { + description: '开始拖拽点标记时触发事件', + type: { required: false, summary: '(event: any) => void' }, + control: false, + }, + onDragging: { + description: '鼠标拖拽移动点标记时触发事件', + type: { required: false, summary: '(event: any) => void' }, + control: false, + }, + onDragend: { + description: '点标记拖拽移动结束触发事件', + type: { required: false, summary: '(event: any) => void' }, + control: false, + }, + onMoving: { + description: '点标记在执行 `moveTo`,`moveAlong` 动画时触发事件', + type: { required: false, summary: '(event: any) => void' }, + control: false, + }, + onMoveEnd: { + description: '点标记在执行 `moveTo`,`moveAlong` 动画时触发事件', + type: { required: false, summary: '(event: any) => void' }, + control: false, + }, + onMoveAlong: { + description: '点标记执行 moveAlong 动画一次后触发事件', + type: { required: false, summary: '(event: any) => void' }, + control: false, + }, + }, +} as Meta; + +const Template: Story = (args) => ; + +export const CommonUse: typeof Template = Template.bind({}); +CommonUse.storyName = '一般使用'; +CommonUse.args = {}; + +export const CustomIcon: typeof Template = Template.bind({}); +CustomIcon.storyName = '自定义标记图标'; +CustomIcon.args = { + icon: '//webapi.amap.com/theme/v1.3/markers/b/mark_r.png', + size: [38, 60], +}; + +export const CustomContent: typeof Template = Template.bind({}); +CustomContent.storyName = '自定义标记内容'; +CustomContent.args = { + content: renderToString( + , + ), + icon: '//webapi.amap.com/theme/v1.3/markers/b/mark_r.png', + size: [38, 60], +}; + +export const CustomLabel: typeof Template = Template.bind({}); +CustomLabel.storyName = '自定义标签内容'; +CustomLabel.args = { + label: { + content: renderToString( + 标签内容, + ), + offset: [0, 10], + direction: 'bottom', + }, +}; + +export const CustomZooms: Story = (args) => ( + <> + + + +); +CustomZooms.storyName = '自定义可显示层级'; +CustomZooms.args = { + zooms: [10, 14], +}; + +export const Draggable: typeof Template = Template.bind({}); +Draggable.storyName = '可拖拽'; +Draggable.args = { + draggable: true, + onDragstart: eventHandler.onDragstart, + onDragging: eventHandler.onDragging, + onDragend: eventHandler.onDragend, +}; + +export const CustomAnchor: typeof Template = Template.bind({}); +CustomAnchor.storyName = '自定义点标记锚点'; +CustomAnchor.args = { + anchor: 'bottom-center', +}; + +export const CustomOffset: typeof Template = Template.bind({}); +CustomOffset.storyName = '自定义偏移量'; +CustomOffset.args = { + offset: [300, 300], +}; + +export const CustomAngle: typeof Template = Template.bind({}); +CustomAngle.storyName = '自定义旋转角度'; +CustomAngle.args = { + angle: 45, +}; + +export const SameZIndex: Story = ({ + // eslint-disable-next-line react/prop-types + zIndex, + ...args +}) => ( + <> + + + +); +SameZIndex.storyName = '自定义 Marker 层级'; +SameZIndex.args = { + zIndex: 13, +}; + +export const ClickEvent: typeof Template = Template.bind({}); +ClickEvent.storyName = '点击事件(左单/左双/右单)'; +ClickEvent.args = { + onClick: eventHandler.onClick, + onDBLClick: eventHandler.onDBLClick, + onRightClick: eventHandler.onRightClick, +}; + +export const MouseEvent: typeof Template = Template.bind({}); +MouseEvent.storyName = '鼠标事件(按下/抬起/经过/移出)'; +MouseEvent.args = { + onMousedown: eventHandler.onMousedown, + onMouseup: eventHandler.onMouseup, + onMouseover: eventHandler.onMouseover, + onMouseout: eventHandler.onMouseout, +}; + +export const TouchEvent: typeof Template = Template.bind({}); +TouchEvent.storyName = '触摸事件(触摸开始/触摸移动/触摸结束)'; +TouchEvent.args = { + onTouchstart: eventHandler.onTouchstart, + onTouchmove: eventHandler.onTouchmove, + onTouchend: eventHandler.onTouchend, +}; diff --git a/src/index.ts b/src/index.ts index 424c543..2c65684 100644 --- a/src/index.ts +++ b/src/index.ts @@ -22,6 +22,7 @@ export { } from './components/AMapGeoJSON'; export { default as AMapCircle, AMapCircleProps } from './components/AMapCircle'; export { default as AMapEllipse, AMapEllipseProps } from './components/AMapEllipse'; +export { default as AMapMarker, AMapMarkerProps } from './components/AMapMarker'; export { default as AMapMouseTool, AMapMouseToolProps } from './components/AMapMouseTool';