vue3-sketch-ruler
Version:
> 是一个基于 Vue 3 + TypeScript 的标尺组件库,适用于低代码平台、大屏可视化、做图工具等场景,提供类似 Photoshop 的缩放与标尺辅助线体验。
470 lines (372 loc) • 18.4 kB
Markdown
# vue3-sketch-ruler
> 是一个基于 Vue 3 + TypeScript 的标尺组件库,适用于低代码平台、大屏可视化、做图工具等场景,提供类似 Photoshop 的缩放与标尺辅助线体验。
[](https://camo.githubusercontent.com/28479a7a834310a667f36760a27283f7389e864a/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f6c2f76322d646174657069636b65722e737667) [](https://github.com/kakajun/vue3-sketch-ruler/actions/workflows/gh-pages.yml)
[](https://cloudstudio.net/a/21005994397405184?channel=share&sharetype=Markdown)
<div align=center>
<img src="https://github.com/kakajun/vue3-sketch-ruler/blob/master/packages/docs/src/assets/logo.png" width="392" height="300">
</div>
## 🚀 Features
- 💪 Vue 3 Composition API / `<script setup>`
- 🔥 基于 TypeScript,类型完整
- 🎯 内置 TransformEngine 变换引擎,零外部依赖
- 🖱️ 多种缩放模式:鼠标中心、视口中心、内容中心
- 📐 可配置参考线(拖拽创建、吸附、锁定)
- 🗺️ 内置 Minimap 缩略图导航(支持拖拽视口、点击跳转)
- 🔌 插件系统(生命周期钩子 + 自定义渲染器)
- 🎨 动画支持:ease-out / damped / exponential / direct
- 📦 平台与业务代码通过插槽分离,专注业务即可
## 🦄 Demo
案例浏览: [https://kakajun.github.io/vue3-sketch-ruler](https://kakajun.github.io/vue3-sketch-ruler) 
[CodePen 示例](https://codepen.io/kakajun/pen/eYwagJb)
## 安装
```bash
npm install --save vue3-sketch-ruler
# 或
yarn add vue3-sketch-ruler
# 或
pnpm add vue3-sketch-ruler
```
## 引入方式
### ESM
```ts
import { SketchRuler, Minimap } from 'vue3-sketch-ruler'
import type { SketchRulerProps, SketchRulerPlugin } from 'vue3-sketch-ruler'
import 'vue3-sketch-ruler/lib/style.css'
```
### CDN (IIFE)
通过 `<script>` 标签直接引入,挂载到全局变量 `SketchRuler`:
```html
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script src="https://unpkg.com/vue3-sketch-ruler/lib/index.iife.js"></script>
<link rel="stylesheet" href="https://unpkg.com/vue3-sketch-ruler/lib/style.css" />
<script>
const { SketchRuler: SketchRulerComp } = SketchRuler
Vue.createApp({
components: { SketchRuler: SketchRulerComp }
}).mount('#app')
</script>
```
### CDN (UMD)
UMD 格式兼容 CommonJS、AMD 和浏览器全局变量三种环境:
```html
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script src="https://unpkg.com/vue3-sketch-ruler/lib/index.umd.cjs"></script>
<link rel="stylesheet" href="https://unpkg.com/vue3-sketch-ruler/lib/style.css" />
<script>
const { SketchRuler: SketchRulerComp } = SketchRuler
Vue.createApp({
components: { SketchRuler: SketchRulerComp }
}).mount('#app')
</script>
```
## 使用
### 基础用法
```vue
<template>
<div class="wrapper" :style="{ width: width + 'px', height: height + 'px' }">
<SketchRuler
ref="sketchRef"
v-model:scale="state.scale"
:width="width"
:height="height"
:canvas-width="canvasWidth"
:canvas-height="canvasHeight"
:thick="state.thick"
:lines="state.lines"
:is-show-refer-line="state.isShowReferLine"
:enable-animation="true"
animation-mode="ease-out"
@zoomchange="handleZoomChange"
@update:lines="handleLinesChange"
>
<template #default>
<div data-type="page" :style="canvasStyle">
<img :src="bgImg" style="width:100%;height:100%" />
</div>
</template>
<template #toolbar="{ tools, state }">
<div class="btns">
<button @click="tools.reset">还原</button>
<button @click="tools.zoomIn">放大</button>
<button @click="tools.zoomOut">缩小</button>
<button @click="tools.zoomToPreset(1)">100%</button>
<span>{{ (state.scale * 100).toFixed(0) }}%</span>
</div>
</template>
</SketchRuler>
</div>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue'
import { SketchRuler } from 'vue3-sketch-ruler'
import 'vue3-sketch-ruler/lib/style.css'
const sketchRef = ref()
const width = 1470
const height = 700
const canvasWidth = 1000
const canvasHeight = 500
const state = reactive({
scale: 1,
thick: 20,
isShowReferLine: true,
lines: { h: [0, 250], v: [0, 500] }
})
const canvasStyle = {
width: canvasWidth + 'px',
height: canvasHeight + 'px'
}
const handleZoomChange = (detail: { scale: number; x: number; y: number }) => {
console.log('zoomchange', detail)
}
const handleLinesChange = (lines: { h: number[]; v: number[] }) => {
console.log('lines changed', lines)
}
</script>
```
### 配合 Minimap
```vue
<template>
<SketchRuler ref="sketchRef" v-model:scale="state.scale" ...>
<template #default>...</template>
</SketchRuler>
<Minimap
:content-width="canvasWidth"
:content-height="canvasHeight"
:viewport-x="viewportOffset.x"
:viewport-y="viewportOffset.y"
:viewport-width="width"
:viewport-height="height"
:scale="state.scale"
:width="200"
:height="150"
@navigate="handleNavigate"
/>
</template>
<script setup>
import { SketchRuler, Minimap } from 'vue3-sketch-ruler'
const sketchRef = ref()
const viewportOffset = reactive({ x: 0, y: 0 })
const handleZoomChange = (detail) => {
viewportOffset.x = detail.x
viewportOffset.y = detail.y
}
const handleNavigate = (x, y) => {
sketchRef.value?.setTransform({ x, y })
}
</script>
```
### 插件系统
```ts
import type { SketchRulerPlugin } from 'vue3-sketch-ruler'
const plugins: SketchRulerPlugin[] = [
{
name: 'demo-logger',
onLineCreate: (ctx) => console.log('line created', ctx.line.id),
onLineDelete: (ctx) => console.log('line deleted', ctx.line.id)
}
]
```
## API
### Props
| 属性 | 描述 | 类型 | 默认值 |
| --- | --- | --- | --- |
| width | 容器宽度 | `number` | `1400` |
| height | 容器高度 | `number` | `800` |
| canvasWidth | 画布宽度 | `number` | `700` |
| canvasHeight | 画布高度 | `number` | `700` |
| thick | 标尺厚度 | `number` | `16` |
| scale | 缩放值(支持 `v-model:scale`) | `number` | `1` |
| showRuler | 是否显示标尺 | `boolean` | `true` |
| isShowReferLine | 是否显示参考线 | `boolean` | `true` |
| lines | 初始化参考线 | `{ h: number[]; v: number[] }` | `{ h: [], v: [] }` |
| palette | 标尺样式配置 | `Partial<RulerPalette>` | `{}` |
| shadow | 阴影高亮区域 | `{ x, y, width, height }` | `{ x:0, y:0, width:0, height:0 }` |
| zoomMode | 缩放原点模式 | `'pointer' \| 'viewport-center' \| 'content-center'` | `'pointer'` |
| zoomStep | 缩放步长 | `number` | `0.25` |
| minZoom | 最小缩放 | `number` | `0.1` |
| maxZoom | 最大缩放 | `number` | `10` |
| enableAnimation | 是否启用动画 | `boolean` | `false` |
| animationMode | 动画模式 | `'direct' \| 'ease-out' \| 'damped' \| 'exponential'` | `'ease-out'` |
| autoCenter | 初始化自动居中 | `boolean` | `true` |
| paddingRatio | 自动居中边距比(`0~0.5`) | `number` | `0.2` |
| initialOffset | 初始偏移(autoCenter=false 时生效) | `{ x: number; y: number }` | `{ x:0, y:0 }` |
| snapThreshold | 吸附阈值 | `number` | `5` |
| lockLine | 是否锁定参考线 | `boolean` | `false` |
| selfHandle | 自行处理输入事件 | `boolean` | `false` |
| showMinorTicks | 是否显示次刻度线 | `boolean` | `false` |
| eyeIcon | 左上角眼睛图标 base64 | `string` | 内置图标 |
| closeEyeIcon | 左上角闭眼图标 base64 | `string` | 内置图标 |
| deleteLabel | 参考线拖出画布时的删除提示 | `string` | `'放开删除'` |
| plugins | 插件列表 | `SketchRulerPlugin[]` | `[]` |
### Events
| 事件 | 描述 | 回调参数 |
| --------------- | ------------------ | ------------------------------ |
| update:scale | 缩放值双向绑定 | `number` |
| update:offset | 偏移值双向绑定 | `{ x: number; y: number }` |
| zoomchange | 缩放/平移变化 | `{ scale, x, y }` |
| update:lines | 参考线变化 | `{ h: number[]; v: number[] }` |
| update:lockLine | 参考线锁定状态变化 | `boolean` |
| onCornerClick | 左上角按钮点击 | `boolean`(参考线显示状态) |
### Slots
| 插槽名 | 描述 | 作用域参数 |
| --- | --- | --- |
| default | 画布内容(**必须用 `<template #default>` 包裹**) | — |
| toolbar | 右下角工具栏 | `{ tools: { reset, zoomIn, zoomOut, zoomToPreset, setZoomMode, toggleReferLine }, state: { scale, offset, zoomMode, showReferLine } }` |
### Expose
通过 `ref` 可以调用以下方法:
| 方法 | 描述 |
| ---------------------------------- | -------------------- |
| `setTransform({ x?, y?, scale? })` | 设置变换状态 |
| `zoomIn()` | 放大 |
| `zoomOut()` | 缩小 |
| `reset()` | 重置到初始状态 |
| `zoomToPreset(scale)` | 缩放到预设比例 |
| `setZoomMode(mode)` | 设置缩放模式 |
| `engine` | TransformEngine 实例 |
| `guideLines` | 当前参考线数组 |
| `cursorClass` | 当前光标类名 |
### Palette
| 属性 | 描述 | 默认值 |
| -------------------- | -------------- | ---------- |
| bgColor | 画布背景 | `#f6f7f9` |
| tickColor | 刻度颜色 | `#BABBBC` |
| labelColor | 刻度标签颜色 | `#7D8694` |
| guideLineColor | 参考线颜色 | `#51d6a9` |
| guideLineLockedColor | 锁定参考线颜色 | `#d4d7dc` |
| hoverBg | 标签背景色 | `transparent` |
| hoverColor | 标签文字色 | `#000` |
| borderColor | 尺子边框颜色 | `#eeeeef` |
| shadowColor | 阴影高亮色 | `#e9f7fe` |
| guideLineStyle | 参考线样式 | `'dashed'` |
| guideLineWidth | 参考线宽度 | `1` |
| labelEnabled | 是否显示标签 | `true` |
## Minimap API
| 属性 | 描述 | 类型 | 默认值 |
| -------------- | ----------- | -------- | ------ |
| contentWidth | 内容宽度 | `number` | — |
| contentHeight | 内容高度 | `number` | — |
| viewportX | 视口 X 偏移 | `number` | — |
| viewportY | 视口 Y 偏移 | `number` | — |
| viewportWidth | 视口宽度 | `number` | — |
| viewportHeight | 视口高度 | `number` | — |
| scale | 缩放比例 | `number` | — |
| width | 缩略图宽度 | `number` | `200` |
| height | 缩略图高度 | `number` | `150` |
| 事件 | 描述 | 回调参数 |
| --------- | -------------- | ------------------------ |
| navigate | 导航到指定位置 | `(x: number, y: number)` |
| dragstart | 开始拖拽 | — |
| dragend | 结束拖拽 | — |
## 2.x → 3.x 迁移指南
v3.x 是架构重构版本,内置 TransformEngine 替代了外部 `simple-panzoom` 依赖,并引入了 Minimap、插件系统、动画系统等全新能力。以下是核心差异与迁移要点:
### 主要 Breaking Changes
| 变更项 | 2.x 写法 | 3.x 写法 | 说明 |
| --- | --- | --- | --- |
| **组件名** | `<SketchRule>` / `<sketch-rule>` | `<SketchRuler>` | 统一为 PascalCase 完整拼写 |
| **导入方式** | `import SketchRule from 'vue3-sketch-ruler'` | `import { SketchRuler } from 'vue3-sketch-ruler'` | 改为命名导出,同时仍保留默认导出兼容 |
| **工具栏插槽** | `#btn="{ reset, zoomIn, zoomOut }"` | `#toolbar="{ tools, state }"` | `tools` 包含 `reset`、`zoomIn`、`zoomOut`、`zoomToPreset`、`setZoomMode`、`toggleReferLine`;`state` 包含 `scale`、`offset`、`zoomMode`、`showReferLine` |
| **缩放事件** | `@zoomStart` | `v-model:scale` / `@update:scale` | 支持双向绑定,无需手动监听缩放开始 |
| **参考线事件** | `@handleLine` | `@update:lines` | 统一为 `update:` 风格事件 |
| **偏移事件** | 无 | `@update:offset` / `@zoomchange` | 3.x 新增,返回 `{ scale, x, y }` |
| **锁定事件** | 无 | `v-model:lockLine` / `@update:lockLine` | 3.x 新增双向绑定 |
| **缩放控制** | `panzoomOption` | `zoomMode`、`zoomStep`、`minZoom`、`maxZoom` | 移除 `panzoomOption`,改为内置引擎直接配置 |
| **动画系统** | 无 | `enableAnimation`、`animationMode` | 3.x 新增,支持 `ease-out`、`damped`、`exponential`、`direct` |
| **自动居中** | 依赖 panzoom 的 `startX/startY` | `autoCenter`、`paddingRatio`、`initialOffset` | `autoCenter` 为 `true` 时按 `paddingRatio` 留白后自动居中 |
| **阴影文字** | `showShadowText` | 移除 | 3.x 已移除该属性 |
| **palette 属性** | `lineType`、`lineColor`、`longfgColor`、`fontColor` | `guideLineStyle`、`guideLineColor`、`tickColor`、`labelColor` | 命名规范化 |
| **Expose** | `panzoomInstance` | `engine`(TransformEngine) | 直接暴露内置引擎实例 |
| **Expose 方法** | `zoomIn()`、`zoomOut()`、`reset()` | 同上,并新增 `setTransform()`、`zoomToPreset()`、`setZoomMode()` | 方法更丰富 |
| **外部依赖** | `simple-panzoom` | 零外部 panzoom 依赖 | 需从项目中移除 `simple-panzoom` |
### 快速迁移示例
**2.x 代码:**
```vue
<template>
<sketch-rule
ref="sketchruleRef"
v-model:scale="state.scale"
:panzoomOption="panzoomOption"
:palette="{ lineType: 'dashed', lineColor: '#51d6a9' }"
@handleLine="handleLinesChange"
>
<template #default>...</template>
<template #btn="{ reset, zoomIn, zoomOut }">
<button @click="reset">还原</button>
<button @click="zoomIn">放大</button>
<button @click="zoomOut">缩小</button>
</template>
</sketch-rule>
</template>
<script setup>
import SketchRule from 'vue3-sketch-ruler'
import { simplePanzoom } from 'simple-panzoom' // 需移除
</script>
```
**3.x 代码:**
```vue
<template>
<SketchRuler
ref="sketchRef"
v-model:scale="state.scale"
v-model:offset="state.offset"
:zoom-mode="'pointer'"
:enable-animation="true"
animation-mode="ease-out"
:palette="{ guideLineStyle: 'dashed', guideLineColor: '#51d6a9' }"
@update:lines="handleLinesChange"
@zoomchange="handleZoomChange"
>
<template #default>...</template>
<template #toolbar="{ tools }">
<button @click="tools.reset">还原</button>
<button @click="tools.zoomIn">放大</button>
<button @click="tools.zoomOut">缩小</button>
<button @click="tools.zoomToPreset(1)">100%</button>
<span>{{ (tools.state.scale * 100).toFixed(0) }}%</span>
</template>
</SketchRuler>
</template>
<script setup>
import { SketchRuler } from 'vue3-sketch-ruler'
</script>
```
### 自动化迁移脚本
项目提供了官方迁移脚本,可自动处理大部分替换:
```bash
npx vue3-sketch-ruler-migrate <path>
```
脚本会自动处理:
- 组件名 `SketchRule` → `SketchRuler`
- 插槽 `#btn` → `#toolbar`
- 事件 `zoomStart` → `update:scale`、`handleLine` → `update:lines`
- 检测 `simple-panzoom`、`useLine` 等需手动移除的依赖并发出警告
> ⚠️ 脚本仅为辅助工具,执行后请务必 review 变更并运行测试。
### 新增能力速览
- **Minimap 缩略图**:独立组件,支持拖拽视口与点击跳转
- **插件系统**:通过 `plugins` 属性注入生命周期钩子
- **多画布管理器**:`CanvasManager` + `BUILTIN_TEMPLATES`
- **吸附引擎**:`snapThreshold` 配置参考线吸附阈值
- **动画引擎**:`enableAnimation` + `animationMode` 实现平滑缩放/平移
- **纯原生零依赖**:核心引擎 `@sketch-ruler/core` 纯原生实现,无需 panzoom 等第三方库
---
## 变更记录
- **v3.x** 内置 TransformEngine 替代外部 panzoom,新增 Minimap、插件系统、多画布管理器、吸附引擎、动画系统
- **v2.4.x** 多实例支持
- **v2.3.x** 引用简化版 simple-panzoom,提升性能,更新全部依赖
## 🌈 Who is using this?
[avue大屏可视化工具](https://data.avuejs.com/build/1) 
[GoView 高效拖拽式低代码数据可视化开发平台](https://vue.mtruning.club/#/project/items)
> open a PR to add your library ;)
### QQ 技术交流群:
<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=oqnBX-qn7gkWsdfYQdvNCzYbkeNknuOc&jump_from=webapi&authKey=4YXd2jvmWYU0cN8zUky5DoCD6kz+fjUyWv782GLUjDEIHctXYviSXD/pbqxm/ZDD"><img border="0" src="https://github.com/kakajun/vue3-sketch-ruler/blob/master/packages/docs/src/assets/group.png" alt="vue3-sketch-ruler" title="点击这里加入QQ群640166628"></a>
<div align=center>
<img src="https://github.com/kakajun/vue3-sketch-ruler/blob/master/packages/docs/src/assets/qq.png" width="243" height="287">
</div>
## 贡献者
<a href="https://github.com/kakajun/vue3-sketch-ruler/graphs/contributors">
<img src="https://contrib.rocks/image?repo=kakajun/vue3-sketch-ruler" />
</a>
## 最后
这是个开源业余做的功能,欢迎加强该插件的小伙伴加入。如果 `vue3-sketch-ruler` 对您有帮助,请给个 star,您的鼓励是我最大的动力。
<a href='https://gitee.com/majun2232/vue3-sketch-Ruler'><img src='https://gitee.com/majun2232/vue3-sketch-Ruler/widgets/widget_4.svg' alt='Fork me on Gitee'></img></a>
## 引用
vue标尺组件 [vue-sketch-ruler](https://github.com/chuxiaoguo/vue-sketch-ruler.git)