@kirkw/vue2-image-hotzone
v1.0.2
Published
A Vue 2 component for creating image hotzones with drag, resize and move support
Maintainers
Readme
markdown
ImageHotzone - Vue2 图片热区组件
一个功能强大的 Vue 2 图片热区编辑组件,支持拖拽绘制、移动、缩放热区,提供丰富的配置选项和事件系统。
特性
- 🖱️ 拖拽绘制 - 在图片上按住鼠标拖拽即可创建热区
- 📐 自由缩放 - 8个手柄支持全方位调整热区大小
- 🔄 拖拽移动 - 点击热区内部可拖拽移动位置
- 📱 响应式适配 - 基于百分比坐标,适配所有分辨率
- 🎨 主题定制 - 完整的配色系统,支持预设主题和自定义
- ⚙️ 灵活配置 - 丰富的配置选项,满足各种需求
- 🎯 独立热区控制 - 支持禁用、隐藏、锁定单个热区
- 📦 数据导入导出 - 支持 JSON 格式的热区数据导入导出
- ⌨️ 键盘快捷键 - 支持 Delete/Backspace 删除,Escape 取消选择
- ⚡ 高性能 - 使用 requestAnimationFrame 优化拖拽性能
- 🔌 完善的事件系统 - 覆盖所有交互事件
安装
直接引入
<script src="./components/ImageHotzone.vue"></script>
模块化引入
javascript
import ImageHotzone from './components/ImageHotzone.vue'
export default {
components: {
ImageHotzone
}
}
全局注册
javascript
// main.js
import Vue from 'vue'
import ImageHotzone from './components/ImageHotzone.vue'
Vue.component('image-hotzone', ImageHotzone)
快速开始
vue
<template>
<image-hotzone
ref="hotzone"
:src="imageUrl"
:zones.sync="zones"
:theme="theme"
:config="config"
@zone-created="onZoneCreated"
@zone-updated="onZoneUpdated"
@zone-deleted="onZoneDeleted"
/>
</template>
<script>
import ImageHotzone from './components/ImageHotzone.vue'
export default {
components: { ImageHotzone },
data() {
return {
imageUrl: 'https://example.com/image.jpg',
zones: [],
theme: {},
config: {}
}
},
methods: {
onZoneCreated(zone) {
console.log('热区创建:', zone)
},
onZoneUpdated(zone) {
console.log('热区更新:', zone)
},
onZoneDeleted(zone) {
console.log('热区删除:', zone)
}
}
}
</script>
Props
src
类型: String
必填: true
说明: 图片地址
zones
类型: Array
默认值: []
支持修饰符: .sync
说明: 热区数据数组,支持双向绑定
热区数据结构:
javascript
{
id: 'unique_id', // 唯一标识(可选,自动生成)
x: 20, // X坐标(百分比 0-100)
y: 15, // Y坐标(百分比 0-100)
width: 25, // 宽度(百分比 0-100)
height: 30, // 高度(百分比 0-100)
label: '热区名称', // 标签文本
title: '热区标题', // 标题(备用)
data: {}, // 自定义数据
disabled: false, // 是否禁用
visible: true, // 是否可见
draggable: true, // 是否可拖拽
resizable: true, // 是否可调整大小
style: {} // 自定义样式
}
theme
类型: Object
默认值: {}
说明: 主题配色配置
javascript
{
// 绘制中虚线框
drawingLineColor: '#4f6ef7',
drawingLineWidth: 2,
drawingFillColor: 'rgba(79, 110, 247, 0.15)',
// 热区默认状态
zoneBorderColor: '#4f6ef7',
zoneBorderWidth: 2,
zoneFillColor: 'rgba(79, 110, 247, 0.2)',
zoneBorderStyle: 'solid',
zoneOpacity: 1,
// 热区激活状态
zoneActiveBorderColor: '#e53e3e',
zoneActiveFillColor: 'rgba(229, 62, 62, 0.15)',
zoneActiveBorderStyle: 'solid',
zoneActiveShadow: '0 0 0 2px rgba(229, 62, 62, 0.3)',
// 热区悬停状态
zoneHoverBorderColor: '#718096',
zoneHoverFillColor: 'rgba(113, 128, 150, 0.2)',
// 手柄样式
handleSize: 10,
handleColor: '#ffffff',
handleBorderColor: '#4f6ef7',
handleBorderWidth: 2,
handleActiveBorderColor: '#e53e3e',
handleHoverColor: '#4f6ef7',
handleActiveHoverColor: '#e53e3e',
handleBorderRadius: '3px',
handleShadow: '0 1px 4px rgba(0,0,0,0.15)',
// 标签样式
labelColor: '#333333',
labelFontSize: '12px',
labelFontWeight: '600',
labelBackgroundColor: 'transparent',
labelPadding: '2px 4px',
labelBorderRadius: '0px',
labelMaxWidth: '90%',
labelTextShadow: 'none',
// 过渡动画
transitionDuration: '0s',
transitionTiming: 'ease'
}
config
类型: Object
默认值: {}
说明: 功能配置
javascript
{
minSize: 2, // 最小尺寸(百分比)
maxZones: 20, // 最大热区数量
showLabel: true, // 是否显示标签
enableResize: true, // 是否允许调整大小
enableMove: true, // 是否允许移动
enableDraw: true, // 是否允许绘制新热区
enableDelete: true, // 是否允许删除热区
showHandles: true, // 是否显示手柄
allowOverlap: true, // 是否允许热区重叠
snapToGrid: false, // 是否吸附到网格
gridSize: 5, // 网格大小(百分比)
useAnimationFrame: true, // 使用RAF优化性能
debounceUpdate: true // 防抖更新
}
事件
事件名 参数 说明
zone-created zone 热区创建时触发
zone-updated zone 热区更新时触发
zone-deleted zone 热区删除时触发
zone-click zone 点击热区时触发
zone-focus zone 热区获得焦点时触发
zone-hover zone 鼠标进入热区时触发
zone-leave zone 鼠标离开热区时触发
draw-start {x, y} 开始绘制时触发
drawing {x, y, width, height} 绘制过程中触发
draw-end {x, y, width, height, valid} 绘制结束时触发
drag-start {zone, handle, type} 开始拖拽时触发
moving {zone, originalZone} 移动过程中触发
resizing {zone, handle, originalZone} 调整大小过程中触发
drag-end {zone, type} 拖拽结束时触发
max-reached max 达到最大数量时触发
selection-cleared - 清除选择时触发
all-cleared - 清除所有热区时触发
zones-imported count 导入热区完成时触发
resize {width, height} 容器尺寸变化时触发
change zones 热区数据变化时触发
方法
通过 ref 调用组件方法:
javascript
const hotzone = this.$refs.hotzone
添加热区
javascript
// 添加单个热区
hotzone.addZone({
x: 20,
y: 20,
width: 30,
height: 30,
label: '新热区',
data: { type: 'custom' }
})
// 批量添加热区
hotzone.addZones([
{ x: 10, y: 10, width: 20, height: 20, label: '热区1' },
{ x: 40, y: 40, width: 25, height: 25, label: '热区2' }
])
更新热区
javascript
// 更新热区属性
hotzone.updateZone('zone_id', {
label: '新名称',
x: 30,
y: 30,
data: { updated: true },
style: {
borderColor: '#48bb78',
fillColor: 'rgba(72, 187, 120, 0.2)'
}
})
删除热区
javascript
// 删除指定热区
hotzone.removeZone('zone_id')
// 清除所有热区
hotzone.clearAll()
获取热区
javascript
// 获取激活的热区
const activeZone = hotzone.getActiveZone()
// 获取指定热区
const zone = hotzone.getZone('zone_id')
// 获取所有热区
const allZones = hotzone.getAllZones()
设置激活热区
javascript
// 激活指定热区
hotzone.setActiveZone('zone_id')
// 取消激活
hotzone.setActiveZone(null)
控制热区状态
javascript
// 禁用/启用
hotzone.disableZone('zone_id')
hotzone.enableZone('zone_id')
// 隐藏/显示
hotzone.hideZone('zone_id')
hotzone.showZone('zone_id')
数据导入导出
javascript
// 导出热区数据
const data = hotzone.exportZones()
console.log(JSON.stringify(data, null, 2))
// 导入热区数据
hotzone.importZones(data)
获取容器尺寸
javascript
const size = hotzone.getSize()
console.log(size.width, size.height)
预设主题
组件内置了几种预设主题供参考:
javascript
// 默认蓝色主题
const defaultTheme = {
drawingLineColor: '#4f6ef7',
zoneBorderColor: '#4f6ef7',
zoneFillColor: 'rgba(79, 110, 247, 0.2)',
zoneActiveBorderColor: '#e53e3e',
zoneActiveFillColor: 'rgba(229, 62, 62, 0.15)',
}
// 暗色主题
const darkTheme = {
drawingLineColor: '#a0aec0',
zoneBorderColor: '#718096',
zoneFillColor: 'rgba(113, 128, 150, 0.2)',
zoneActiveBorderColor: '#fbd38d',
zoneActiveFillColor: 'rgba(251, 211, 141, 0.15)',
labelColor: '#e2e8f0'
}
// 绿色主题
const greenTheme = {
drawingLineColor: '#48bb78',
zoneBorderColor: '#38a169',
zoneFillColor: 'rgba(56, 161, 105, 0.2)',
zoneActiveBorderColor: '#f56565',
zoneActiveFillColor: 'rgba(245, 101, 101, 0.15)',
}
// 海洋主题
const oceanTheme = {
drawingLineColor: '#00b4d8',
zoneBorderColor: '#0077b6',
zoneFillColor: 'rgba(0, 119, 182, 0.2)',
zoneActiveBorderColor: '#ff6b6b',
zoneActiveFillColor: 'rgba(255, 107, 107, 0.15)',
handleBorderRadius: '50%',
handleSize: 12
}
// 日落主题
const sunsetTheme = {
drawingLineColor: '#f6ad55',
zoneBorderColor: '#ed8936',
zoneFillColor: 'rgba(237, 137, 54, 0.2)',
zoneActiveBorderColor: '#9f7aea',
zoneActiveFillColor: 'rgba(159, 122, 234, 0.15)',
zoneBorderWidth: 3,
labelColor: '#744210',
labelFontWeight: 'bold'
}
// 霓虹主题
const neonTheme = {
drawingLineColor: '#00ff00',
zoneBorderColor: '#00ff00',
zoneFillColor: 'rgba(0, 255, 0, 0.1)',
zoneActiveBorderColor: '#ff00ff',
zoneActiveFillColor: 'rgba(255, 0, 255, 0.15)',
zoneActiveShadow: '0 0 10px rgba(255, 0, 255, 0.5)',
handleBorderRadius: '50%',
labelColor: '#00ff00'
}
键盘快捷键
快捷键 说明
Delete / Backspace 删除当前激活的热区
Escape 取消当前选择/取消绘制
性能优化
组件内置了多项性能优化措施:
requestAnimationFrame - 拖拽和绘制使用 RAF 优化,避免过度渲染
防抖更新 - 数据更新使用防抖,减少不必要的事件触发
关闭过渡动画 - 拖拽时关闭 CSS 过渡动画,提升响应速度
Resize 防抖 - 窗口大小变化时防抖更新容器尺寸
可在 config 中调整:
javascript
{
useAnimationFrame: true, // 是否使用 RAF(推荐开启)
debounceUpdate: true // 是否防抖更新(推荐开启)
}
浏览器兼容性
Chrome 60+
Firefox 60+
Safari 12+
Edge 79+
移动端 Safari
移动端 Chrome
License
MIT
更新日志
v1.0.0
✨ 初始版本发布
🎨 支持完整的主题定制
⚡ 使用 RAF 优化拖拽性能
📦 支持热区数据导入导出
⌨️ 支持键盘快捷键
🎯 完善的事件系统
📱 响应式百分比坐标系统