vue-moveable 上手笔记
1. 简介
moveable
是一个强大的开源web前端交互工具库,github 地址 https://github.com/daybrush/moveable,官方也提供了详尽的 api 文档:文档地址,对于刚接触的新用户可以通过官方提供的 demo 对其有一个直观的了解:demo地址。
moveable
非常适合用来开发一些前端交互项目,如可视化的 UI 编辑工具等,它主要有 12 个功能模块:
- Draggable:拖拽位移
- Resizable:改变元素尺寸大小
- Scalable:元素缩放
- Rotatable:旋转
- Warpable:透视扭曲变形
- Pinchable:捏合交互
- Groupable:分组操作
- Snapable:捕捉吸附
- Clippable:裁切
- Roundable:圆角设置
- OriginDraggable:原点拖拽
- Selecto:多选框选
而 vue-moveable 则是 moveable 的 vue 版本,适合用于 vue 项目中。
2. 基本操作
在 vue 项目中,可以在 data 中定义元素数据用于数据驱动渲染:
data() { return { // elements elements: [{ uuid: 'aaa', width: 0, height: 0, top: 0, left: 0, rotate: 0 }, { uuid: 'bbb', width: 0, height: 0, top: 0, left: 0, rotate: 0 }, { uuid: 'ccc', width: 0, height: 0, top: 0, left: 0, rotate: 0 }] } },
而配置参数则可以在 computed 计算属性中动态生成:
computed: { moveableConfig() { return { // 【 draggable 】 draggable: true, throttleDrag: 1, // 步长,默认 0 throttleDragRotate: 0, // 拖拽角度,默认 0 不限制 startDragRotate: 0, // 初始拖拽角度 edgeDraggable: false, // 是否通过拖动边缘线移动,默认 false // 【 resizable 】 resizable: true, throttleResize: 1, // 【 scalable 】 // scalable: true, throttleScale: 0, // 【 rotatable 】 rotatable: true, throttleRotate: 0, // 【 snappable 】 snappable: true, // 是否自动吸附,默认 false // snapCenter: true, // 中心吸附 snapHorizontal: true, // 使用水平参考线 horizontalGuidelines: [100, 400, 700], snapVertical: true, // 使用垂直参考线 verticalGuidelines: [100, 400, 700], bounds: { // 限制外框范围 left: 0, right: 700, top: 0, bottom: 700 }, // innerBounds: [], // 限制内框范围 snapElement: true, snapGap: true, snapThreshold: 5, // default 5 snapDigit: 50, isDisplaySnapDigit: true, snapDistFormat: distance = > `距离: $ {distance}`, // 【 pinchable 】 pinchable: true, // ["draggable", "resizable", "scalable", "rotatable"] // 【 groupable 】 // groupable: true, // target: [], // 【 ... 】 // 【 others 】 keepRatio: true, // 保持比例 className: 'test-control', // 指定控制箱 class origin: false, // 原点控制箱是否可见 zoom: 1, // 控制箱手柄大小 padding: { // 设置目标边距,以增加可拖动区域 top: 0, right: 0, bottom: 0, left: 0 }, dragArea: false, // 将事件添加到可移动区域,而不是将目标添加到 stopPropagation 的目标 (默认值:false,组中为true) // container: '', edge: true } } }
最后是 template 模板,可以在 <Moveable>
组件上绑定所有 api 文档中的事件,如 api 文档中的 onDrag
事件可以绑定为 @drag
。
<template> <div class="moveable"> <Moveable ref="moveable" v-for="element in elements" :key="element.uuid" class="wrapper" v-bind="moveableConfig" @dragStart="handleTestMoveableEvent" @drag="handleDrag" @dragEnd="handleDragEnd" @dragGroupStart="handleTestMoveableEvent" @dragGroup="handleTestMoveableEvent" @dragGroupEnd="handleTestMoveableEvent" ... ... ... :style="{ width: element.width + 'px', height: element.height + 'px', top: element.top + 'px', left: element.left + 'px', transform: `rotate(${element.rotate}deg)` }" > <div class="element" :data-uuid="element.uuid"> <h3>{{element.uuid}}</h3> <p>{{JSON.stringify(element)}}</p> </div> </Moveable> </div> </template> <style scoped> .moveable { width: 100%; height: 100%; position: relative; } .wrapper { position: absolute; background-color: #cccccc; } .element { width: 100%; height: 100%; position: relative; overflow: scroll; word-break: break-all; text-align: left; } </style>
关于数据处理,在 demo 中,可以使用 localStorage 进行缓存
created() { this.elements.forEach((element) = > { element.left = parseInt(localStorage.getItem('moveable_left_' + element.uuid)) || 0 element.top = parseInt(localStorage.getItem('moveable_top_' + element.uuid)) || 0 // ... // ... // ... }); }, methods: { /** * @name: getElementByTarget * @desc: 根据 target 对象获取 element 实例 */ getElementByTarget(target) { if (target.getElementsByClassName('element').length === 0) { return null } const uuid = target.getElementsByClassName('element')[0].dataset.uuid const element = this.elements.find((element) = > { return element.uuid === uuid }) return element }, /** * @name: handleDrag */ handleDrag({ target, left, top }) { console.log('onDrag') const element = this.getElementByTarget(target) if (!element) { return } // 基础数据处理 const newTop = Math.round(top); const newLeft = Math.round(left); // 设置 data 数据 element.top = newTop element.left = newLeft // 存储数据到缓存 localStorage.setItem('moveable_top_' + element.uuid, element.top); localStorage.setItem('moveable_left_' + element.uuid, element.left); } // ... // ... // ... }
3. 常用参数
3.1 开关参数
moveable
中,各个功能模块需要通过开关参数进行启用或禁用,如:
draggable: true, resizable: true, scalable: true, rotatable: true, snappable: true, pinchable: true, groupable: true, ...
3.2 全局参数
keepRatio: true, // 保持比例 className: 'test-control', // 指定控制箱 class origin: false, // 原点控制箱是否可见 zoom: 1, // 控制箱手柄大小 padding: { // 设置目标边距,以增加可拖动区域 top: 0, right: 0, bottom: 0, left: 0 }
3.3 draggable
draggable: true, throttleDrag: 1, // 步长,默认 0 throttleDragRotate: 0, // 拖拽角度,默认 0 不限制 startDragRotate: 0, // 初始拖拽角度 edgeDraggable: false, // 是否通过拖动边缘线移动,默认 false
3.4 snappable
snappable: true, // 是否自动吸附,默认 false // snapCenter: true, // 中心吸附 snapHorizontal: true, // 使用水平参考线 horizontalGuidelines: [100, 400, 700], snapVertical: true, // 使用垂直参考线 verticalGuidelines: [100, 400, 700], bounds: { left: 0, right: 700, top: 0, bottom: 700 }, // 限制外框范围 // innerBounds: [], // 限制内框范围,格式同 bounds snapElement: true, snapGap: true, snapThreshold: 5, // default 5 snapDigit: 50, isDisplaySnapDigit: true, snapDistFormat: distance = > `距离: $ { distance }`
3.5 Groupable
当使用分组时,需要设置 targets
参数为数组 Array<HTMLElement | SVGElement>
,并且事件也要进行相应转换,如 dragStart
=> dragGroupStart
,pinchStart
=> pinchGroupStart