既然说到编辑器,那就肯定脱离不了几个功能:拖拽、放大、缩小、辅助线等功能,本节,我就以我的编辑器的思路讲讲组件的行为功能
我们简单回忆一下,我在H5编辑器整体思路(一):前言中提到的组件的代码
<EditElement
v-for="item in activePage.elements"
:key="item.uuid + 'i'"
:uuid="item.uuid"
:defaultStyle="item.commonStyle"
:style="getCommonStyle(item.commonStyle)"
@handleElementClick="handleElementClick(item.uuid, item)"
v-show="!item.isHide"
@resize="handleElementResize"
:active="item.uuid === activeElementUUID"
:isPrivew="false"
>
<component
:is="item.elName"
class="element-on-edit-pane"
v-bind="{
...item.propsValue,
value: item.value,
uuid: item.uuid
}"
:textS.sync="item.propsValue.text"
/>
</EditElement>
每个元素的组件被一个EditElement组件包在里面,这里做的目的是在每个组件外层包一个壳子,这个壳子的功能就是来承载每个组件的点击事件、拖拽、放大、缩小等动作行为。大概的效果图如下:
1、拆解editElement
先说思路在上代码。
思路:当每个组件生成之后,动态给每个组件外新增个壳子,壳子里主要包含了一个旋转的div,四个位于元素的左上、右上、左下、右下四个div。旋转就不用多说了,点击旋转按钮后进行旋转,四个div分别控制四个角的缩放功能,外层最大的的壳子来控制它的移动
整体代码:
<div
class="edit-element"
@click="handleTopWrapperClick"
@mousedown="handleMouseDownOnElement"
:class="{ active: active && !isPrivew }"
>
<div class="yincang" ref="yincang"></div>
<template v-if="!isPrivew">
<div class="edit-shape-rotate" :style="getPosition" v-show="this.active">
<div class="edit-shape-point-rotate" @mousedown="handleMoseRotate">
<svg-icon
iconName="rotate"
:iconWidth="16"
:iconHeight="16"
></svg-icon>
</div>
</div>
<div
class="edit-shape-point"
v-for="item in active ? pointList : []"
:key="item"
:id="item"
@mousedown="handleMouseDownOnPoint(item, $event)"
:style="{
...getPointStyle(item),
transfrom: 'rotate(' + rotate + 'deg)'
}"
></div>
</template>
<slot></slot>
</div>
旋转按钮代码:
<div class="edit-shape-rotate" :style="getPosition" v-show="this.active">
<div class="edit-shape-point-rotate" @mousedown="handleMoseRotate">
<svg-icon
iconName="rotate"
:iconWidth="16"
:iconHeight="16"
></svg-icon>
</div>
</div>
四个角的按钮:
<div
class="edit-shape-point"
v-for="item in active ? pointList : []"
:key="item"
:id="item"
@mousedown="handleMouseDownOnPoint(item, $event)"
:style="{
...getPointStyle(item),
transfrom: 'rotate(' + rotate + 'deg)'
}"
></div>
2、元素的拖拽
元素拖拽是比较简单的,就是使用了鼠标的点击事件和鼠标移动事件来计算它的位置,具体思路如下
代码如下:
pos.top = Math.round(currY - startY + Number(startTop));
pos.left = Math.round(currX - startX + Number(startLeft));
3、放大、缩小
基于鼠标的移动事件来计算等比例放大缩小,多说无益直接上思路图
从上述的图看的出来这次有点点复杂,先说下需求,有四个角可以点击拖拽缩放,leftTop、rightTop、leftBottom、rightBottom
鼠标往右的话 鼠标的clientX 值越大,鼠标向下的时候 鼠标的clientY 值越大,这里就不细说,可以自己打印鼠标事件,所以这里就有个结论:
四个角都有自己的名字:lt、rt、lb、rb
按上述我画的图,所以接下来的代码是这样的
let disX = currX - startX;
let disY = currY - startY;
let hasT = /t/.test(str);
let hasB = /b/.test(str);
let hasL = /l/.test(str);
let hasR = /r/.test(str);
let newHeight = +height + (hasT ? -disY : hasB ? disY : 0);
let newWidth = +width + (hasL ? -disX : hasR ? disX : 0);
hasT代表的是是否是从上开始拖拽
hasB代表的是否是从下开始拖拽
hasL代表的是是否从左开始拖拽
hasR代表的是是否从右开始拖拽
现在拖拽的部分我们大致了解了,这样就可以获取到拖拽后元素的宽高,但是元素的定位还没出来,我们需要做出如下的需求:
大概解题思路:
因为要等比例缩放所以代码如下:
pos.width = newWidth;
pos.height = Math.round(((pos.width * oldheight) / oldwidth) * 100) / 100;
因为缩放过程中需要动态更改它的定位,根据上面的思路图可以得到以下代码
if (hasT) {
pos.top = top + (oldheight - pos.height);
}
if (hasL) {
pos.left = left + (oldwidth - pos.width);
}
简单的等比例缩放就完结。以为就这样完了?
还有这样的
这里我简单介绍下文本的拖拽缩放,视频的拖拽缩放丢给大家自己思考一下哈
可以根据效果图,我们的需求是:文本只可以左右横向拖拽,当拖拽到宽度为单个文字的时候就不能再缩小,所以高度不变,只考虑宽度
pos.width = newWidth > firstW ? Math.round(newWidth) : firstW;
firstW在这里指的是单个文字的 fontSize 的1.5倍,大家不要以为是初始宽度
当newWidth大于最小宽度 firstW则以新宽度为准否则按照 firstW为准
let newDisX
// 原始宽度 - 鼠标移动宽度 = 剩余的宽度
let diffXFlag = oldwidth - disX;
//每次移动都记录下,当前剩余宽度是否大于 firstW,如果大于 就将鼠标移动宽度赋值给 newDisX,直到 diffXFlag == firstW的时候 newDisX停止赋值,这个时候的newDisX代表缩放时鼠标的最大移动距离
diffXFlag > firstW && (newDisX = disX);
conentL = hasL ? (diffXFlag <= fw ? newDisX : disX) : 0;
pos.left = Math.round(left + conentL);
本节组件的拖拽、放大、缩小分享结束,有问题可以发评论一起讨论。
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 3 天,点击查看活动详情