TypeScript+leafletjs 自定义 Marker
Leaflet 的 Marker 类通常是通过扩展 L.Marker 来实现的,要支持角度设置,关键点在于如何让 Marker 的图标能够旋转。在 Leaflet 中,可以通过修改图标的 CSS 属性来实现旋转。因此,自定义的 arker 类需要有一个设置角度的方法,并在内部更新图标的样式。
以下是一个示例代码,展示了如何创建一个支持角度设置的 Marker 类(大部分代码来自于 deepseek R1):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| import type { MarkerOptions, Point, LatLngExpression } from 'leaflet' import * as L from 'leaflet'
interface AngleOptions extends MarkerOptions { className?: string html?: string rotationAngle?: number rotationOrigin?: string }
export class AngleMarker<T = any> extends L.Marker { constructor(latlng: LatLngExpression, options?: AngleOptions) setRotationAngle(angle: number): this setRotationOrigin(origin: string): this }
// 实现AngleMarker类 AngleMarker = L.Marker.extend({ initialize: function (latlng: LatLngExpression, options?: AngleOptions) { this.setIcon(this.createIcon(options)) L.Marker.prototype.initialize.call(this, latlng, options) this._applyRotation() },
createIcon(options?: AngleOptions) { const className = `angle-marker ${options.className || ''}` return L.divIcon({ className, html: `<div class="text">${options.html || ''}</div>`, iconSize: [30, 36], iconAnchor: [15, 36], }) },
_setPos: function (pos: Point) { L.Marker.prototype._setPos.call(this, pos) this._applyRotation() },
_applyRotation: function () { const options = this.options as AngleOptions if (this._icon && options.rotationAngle !== undefined) { // 保留原有transform属性(例如图标偏移) const baseTransform = this._icon.style.transform.replace(/rotate\([^)]+\)/g, '') this._icon.style.transform = `${baseTransform} rotate(${options.rotationAngle}deg)` this._icon.style.transformOrigin = options.rotationOrigin || 'center bottom' } },
setHover: function (hover: boolean) { if (!this._icon) { return } if (hover) { this._icon.classList.add('hover') } else { this._icon.classList.remove('hover') } },
setRotationAngle: function (angle: number) { ;(this.options as AngleOptions).rotationAngle = angle if (this._map) this.update() return this },
setRotationOrigin: function (origin: string) { ;(this.options as AngleOptions).rotationOrigin = origin if (this._map) this.update() return this }, }) as any
// 创建工厂函数 angleMarker = function (latlng: LatLngExpression, options?: AngleOptions) { return new AngleMarker(latlng, options || {}) } as any
export default angleMarker
|
需要添加的配套 CSS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| .angle-marker { width: 30px; height: 36px; position: relative;
&::before { content: ''; position: absolute; top: 0; width: 30px; height: 30px; background-color: #1c9dff; border-radius: 50%; }
&::after { content: ''; position: absolute; bottom: 0; left: 50%; transform: translateX(-50%); width: 0; height: 0; border-left: 6px solid transparent; border-right: 6px solid transparent; border-top: 12px solid #1c9dff; } &:hover, &.hover { &::before { background-color: #f00; } &::after { border-top: 12px solid #f00; } } .text { position: absolute; top: 0; left: 0; width: 30px; height: 30px; line-height: 30px; text-align: center; overflow: hidden; white-space: nowrap; color: #fff; } }
|
使用示例:
1 2 3 4 5
| const marker: AngleMarker = angleMarker([lat, lng], { rotationAngle: 180, }) marker.setHover(true) marker.setRotationAngle(90)
|