hand-painted-map
v1.0.4
Published
一个基于 Vue 的地图组件
Readme
Vue 3 + TypeScript + Vite
主要适用于:设备版本过低、内容自定义过高、图片加载速度慢等场景
Canvas 地图组件
这是一个基于Canvas的高性能地图组件,支持图片底图、缩放、拖拽、路线绘制、标记点等功能。
功能特性
- 🖼️ 图片底图支持 - 支持任意尺寸的图片作为地图底图
- 🔍 缩放功能 - 鼠标滚轮或按钮控制缩放,支持自定义缩放范围
- 🖱️ 拖拽功能 - 鼠标拖拽移动地图,支持触摸设备
- 🛣️ 路线绘制 - 支持绘制多条路线,自定义颜色和宽度
- 📍 标记点 - 支持添加标记点,点击显示弹窗
- 🎬 分割动画 - 从中间分割成两半,分别向左右移动,中间留出间隙,然后合并成完整地图
- 📱 响应式设计 - 自适应容器大小变化,支持响应式图片尺寸
- 🖥️ 屏幕适配 - 监听屏幕变化,自动调整Canvas和图片尺寸
- 🖼️ 完全铺满 - 图片和Canvas完全铺满容器,宽度和高度都100%覆盖,不显示背景色
- ⚡ 高性能 - 基于Canvas渲染,流畅的交互体验
组件结构
├── CanvasMap.vue # 核心地图组件使用方法
基本使用
<template>
<CanvasMap
:image-url="imageUrl"
:image-width="7158"
:image-height="5010"
:lines="lines"
:markers="markers"
:min-zoom="1"
:max-zoom="3"
:initial-zoom="1"
:enable-animation="true"
:animation-duration="2000"
:responsive-image="true"
:min-scale="1"
:max-scale="3"
:split-gap="500" <!-- 可选:分割动画间隙,不传为经典动画 -->
:bg-image-url="bgImageUrl" <!-- 可选:背景图片,不传为六边形渐变背景 -->
@marker-click="handleMarkerClick"
@map-click="handleMapClick"
>
<!-- 自定义弹窗内容 -->
<template #popup="{ marker, close, position }">
<div class="custom-popup">
<h3>{{ marker?.title }}</h3>
<p>{{ marker?.description }}</p>
<button @click="close">关闭</button>
</div>
</template>
</CanvasMap>
</template>
<script setup>
import { ref } from 'vue'
import CanvasMap from './components/CanvasMap.vue'
const imageUrl = '/src/assets/imgMap.png'
const bgImageUrl = '' // 或 'https://pic1.imgdb.cn/item/663cc08b0ea9cb14039b7ded.png'
const markers = ref([
{
id: 1,
position: { x: 100, y: 100 },
title: '标记点1',
description: '这是第一个标记点'
}
])
const lines = ref([
{
id: 1,
points: [
{ x: 100, y: 100 },
{ x: 200, y: 150 },
{ x: 300, y: 200 }
],
color: '#f56c6c',
width: 8
}
])
const handleMarkerClick = (marker) => {
console.log('点击了标记点:', marker)
}
const handleMapClick = (position) => {
console.log('点击了地图位置:', position)
}
</script>组件属性
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| imageUrl | string | '/src/assets/imgMap.png' | 地图底图URL |
| imageWidth | number | 7158 | 图片宽度(像素) |
| imageHeight | number | 5010 | 图片高度(像素) |
| lines | Line[] | [] | 路线数据,见下方Line类型 |
| markers | Marker[] | [] | 标记点数据,见下方Marker类型 |
| minZoom | number | 1 | 最小缩放比例(缩放不会小于此值) |
| maxZoom | number | 3 | 最大缩放比例(缩放不会大于此值) |
| initialZoom | number | 1 | 初始缩放比例 |
| enableAnimation | boolean | true | 是否启用分割动画 |
| animationDuration | number | 2000 | 分割动画持续时间(毫秒) |
| responsiveImage | boolean | false | 是否自适应容器尺寸铺满 |
| minScale | number | 1 | 最小缩放倍数(与minZoom类似,优先级高于minZoom) |
| maxScale | number | 3 | 最大缩放倍数(与maxZoom类似,优先级高于maxZoom) |
| splitGap | number/undefined | undefined | 分割动画中间最大间隙(像素),不传时为经典分割动画,传递时为自定义间隙 |
| bgImageUrl | string | '' | 地图容器背景图片URL,不传时为六边形深色渐变背景,传递时为图片背景 |
数据类型
Line 路线
interface Line {
id: number
points: Point[] // 路线点数组
color?: string // 路线颜色,默认 '#f56c6c'
width?: number // 路线宽度,默认 10
}Marker 标记点
interface Marker {
id: number
position: Point // 标记点位置
icon?: string // 图标URL(可选)
title?: string // 标题
description?: string // 描述
}Point 坐标点
interface Point {
x: number
y: number
}事件
| 事件 | 参数 | 说明 |
|------|------|------|
| marker-click | marker: Marker | 点击标记点时触发 |
| map-click | position: Point | 点击地图时触发 |
插槽 (Slots)
| 插槽名 | 作用域参数 | 说明 |
|--------|------------|------|
| popup | { marker, close, position } | 自定义弹窗内容 |
插槽参数说明
marker: 当前选中的标记点对象close: 关闭弹窗的函数position: 弹窗的屏幕坐标位置
插槽使用示例
<template #popup="{ marker, close, position }">
<div class="custom-popup">
<div class="popup-header">
<h3>{{ marker?.title }}</h3>
<button @click="close">×</button>
</div>
<div class="popup-body">
<p>{{ marker?.description }}</p>
<p>位置: ({{ marker?.position.x }}, {{ marker?.position.y }})</p>
<button @click="handleAction(marker)">操作</button>
</div>
</div>
</template>方法
通过ref可以调用以下方法:
<template>
<CanvasMap ref="mapRef" />
</template>
<script setup>
const mapRef = ref()
// 获取当前缩放比例
const zoom = mapRef.value?.zoom()
// 设置缩放比例
mapRef.value?.setZoom(1.5)
// 移动到指定位置
mapRef.value?.panTo(100, 200)
// 适应边界
mapRef.value?.fitBounds({
minX: 0,
minY: 0,
maxX: 1000,
maxY: 1000
})
// 播放分割动画
mapRef.value?.playAnimation()
// 检查动画状态
const isAnimating = mapRef.value?.isAnimating()
</script>与原始实现的对比
1.0 (DOM版本)
- ✅ 支持缩放和拖拽
- ✅ 支持路线绘制
- ✅ 支持标记点
- ❌ 路线连接处有空白问题
- ❌ 性能较差
1.0.1 (Canvas版本)
- ✅ 路线绘制完美
- ✅ 性能优秀
- ❌ 无法拖拽和缩放
- ❌ 交互功能缺失
1.0.2 (新版本)
- ✅ 结合了两种实现的优点
- ✅ 完美的路线绘制
- ✅ 完整的交互功能
- ✅ 高性能Canvas渲染
- ✅ 响应式设计
效果视频
开发说明
- 组件使用Canvas 2D API进行渲染
- 支持鼠标和触摸事件
- 实现了边界约束,防止地图移出可视区域
- 使用Vue 3 Composition API开发
- 完全支持TypeScript
运行项目
# 安装依赖
pnpm install
# 启动开发服务器
pnpm dev
# 构建生产版本
pnpm build访问 http://localhost:5173 查看演示效果。
