一、鼠标事件
- click:按下鼠标(通常是按下主按钮)时触发。
- dblclick:在同一个元素上双击鼠标时触发。
- mousedown:按下鼠标键时触发。
- mouseup:释放按下的鼠标键时触发。
- mousemove:当鼠标在一个节点内部移动时触发。当鼠标持续移动时,该事件会连续触发。为了避免性能问题,建议对该事件的监听函数做一些限定,比如限定一段时间内只能运行一次。
- mouseenter:鼠标进入一个节点时触发,进入子节点不会触发这个事件(详见后文)。
- mouseover:鼠标进入一个节点时触发,进入子节点会再一次触发这个事件(详见后文)。
- mouseout:鼠标离开一个节点时触发,离开父节点也会触发这个事件(详见后文)。
- mouseleave:鼠标离开一个节点时触发,离开父节点不会触发这个事件(详见后文)。
- contextmenu:按下鼠标右键时(上下文菜单出现前)触发,或者按下“上下文菜单键”时触发。
- wheel:滚动鼠标的滚轮时触发,该事件继承的是WheelEvent接口。
click事件:用户在同一个位置先完成mousedown动作,再完成mouseup动作。因此,触发顺序是,mousedown首先触发,mouseup接着触发,click最后触发。
dblclick事件:会在mousedown、mouseup、click之后触发。
mouseover事件和mouseenter事件:都是鼠标进入一个节点时触发。两者的区别是,mouseenter事件只触发一次,而只要鼠标在节点内部移动,mouseover事件会在子节点上触发多次。
mouseout和mouseleave事件:都是鼠标进入一个节点时触发。两者的区别是,mouseleave事件离开节点触发一次而不会触发多次,而mouseout事件如果离开父元素而没有离开子元素则可能会触发多次。
MouseEvent属性
MouseEvent接口继承了Event接口,所以拥有Event的所有属性和方法。它还有自己的属性和方法。
- pageX,pageY:相对页面左上角的距离,不受页面滚动影响,即加上滚动轴滚动的距离。
- clientX,clientY:距离页面左上角距离,受页面滚动的影响,默认值为0,设置该属性不会移动鼠标。
- offsetX,offsetY:针对自己的左上角坐标,从padding开始,计算相对于本元素的左上角,不在乎定位问题,计算的是内交点。是IE浏览器的特有属性。
- layerX,layerY:往上找有定位属性的父元素的左上角,受元素的定位的影响,会从本元素往上找到第一个定位的元素的左上角(自身有定位属性的话就是相对于自身),都没有的话,就是相对于body的左上角。
- offsetWidth,offsetHeight:获取元素的整个的宽度和高度,包括内容区、内边距和边框。
- clientWidth,clientHeight:这两个属性可以获取元素的可见宽度和高度,会获取元素宽度和高度,包括内容区和内边距。
- offsetParent:可以用来获取当前元素的定位父元素的定位(开启了定位的祖先元素),没有开启定位,则返回body。
- offsetLeft,offsetTop:当前元素相对于其定位父元素的水平和垂直偏移量。
- scrollWidth,scrollHeight:可以获取元素整个滚动区域的宽度和高度。
- scrollLeft,scrollTop:可以获取水平和垂直滚动条滚动的距离。
- screenX,screenY:数值,鼠标相对于屏幕的左上角坐标(单位像素),默认值为0,设置该属性不会移动鼠标。
- ctrlKey:布尔值,是否同时按下了 Ctrl 键,默认值为false。
- shiftKey:布尔值,是否同时按下了 Shift 键,默认值为false。
- altKey:布尔值,是否同时按下 Alt 键,默认值为false。
- metaKey:布尔值,是否同时按下 Meta 键,默认值为false。
- button:数值,表示按下了哪一个鼠标按键,默认值为0,表示按下主键(通常是鼠标的左键)或者当前事件没有定义这个属性;1表示按下辅助键(通常是鼠标的中间键),2表示按下次要键(通常是鼠标的右键)。
- buttons:数值,表示按下了鼠标的哪些键,是一个三个比特位的二进制值,默认为0(没有按下任何键)。1(二进制001)表示按下主键(通常是左键),2(二进制010)表示按下次要键(通常是右键),4(二进制100)表示按下辅助键(通常是中间键)。因此,如果返回3(二进制011)就表示同时按下了左键和右键。
- relatedTarget:节点对象,表示事件的相关节点,默认为null。mouseenter和mouseover事件时,表示鼠标刚刚离开的那个元素节点;mouseout和mouseleave事件时,表示鼠标正在进入的那个元素节点。
二、实现逻辑
要实现标签的鼠标选中以及按住鼠标后拖动的效果,首先选中标签可以用mousedown事件,然后按住鼠标的话,就可以在mousedown事件触发的时候,设置一个可以拖动的标示,比如moveabled=true,然后在mousemove事件里,判断这个moveabled是否为true,为true则表示可以拖动,然后就可以根据获取到的坐标更改标签元素的top和left,从而实现移动,为false则什么都不执行,然后如果鼠标松开的话,则会触发mouseup事件,在这个事件里把moveabled设置为false。
1、获取URL图片的宽高
// 创建实例对象
let img = new Image();
// 图片地址
img.src = "图片url链接";
img.onload = function () {
let res = {
width: img.width,
height: img.height
}
console.log(res); // 获取到图片的宽高
}
2、可能会出现的问题
1. pc端拖拽时mouseup事件丢失
描述:在实现pc端拖拽左右移动元素时,使用mousedown+mousemove+mouseup实现左右拖拽移动,操作多次是总会出现不进入mouseup事件,导致无法清除mousemove事件。
解决方案:浏览器鼠标事件有默认行为,例如事件冒泡等其他行为。我们需在mousedown以及mousemove事件中把这些默认行为禁止掉。同时还要在监听元素上加上css样式:user-select: none;
防止其text选中移动影响mouseup事件。
2. 可以触发mousemove事件的区域过小
描述:如果我们把mousemove事件绑定在移动标签元素上,有可能因为标签本身大小不够大,导致在移动的时候,鼠标跑到标签区域外,从而造成事件丢失,移动不顺畅的体验。
解决方案:把mousemove事件绑定在可以移动的最大区域的父标签上。
三、案例代码
<template>
<!-- 视频编辑 -->
<div class="img-canvas"
id="img-canvas"
:style="{
width: width+'px',
height: height+'px'
}"
@mousedown.stop.prevent="wrapStart($event)"
@mousemove.stop.prevent="wrapMove($event)"
@mouseup.stop.prevent="wrapUp($event)"
@mouseleave.stop.prevent="wrapUp($event)">
<template v-for="(item, key) in items">
<img
:key="key"
v-if="item.type == 'bg'"
class="img"
:src="item.content"
:style="{
width: item.width+'px',
height:item.height+'px',
zIndex: item.zIndex
}"
@click.stop.prevent="wraptouchStart($event, item.id)" />
<div
:key="key"
v-else-if="item.show"
:class="item.active ? 'img-wrap touch-active' : 'img-wrap'"
:style="{
transform: 'rotate('+(item.angle ? item.angle : 0)+'deg)',
top: item.top+'px',
left: item.left+'px',
zIndex: item.zIndex
}">
<img
v-if="item.type == 'ai' || item.type == 'tt'"
class="img"
:src="item.content"
:style="{
width: item.width+'px',
height:item.height+'px'
}"
@mousedown.prevent="wraptouchStart($event, item.id)"
/>
<div
v-if="item.type == 'bt' || item.type == 'zm'"
:id="'txt'+item.id"
:class="item.type == 'zm' ? 'slh txt' : 'txt'"
:style="{
width: item.width ? item.width +'px' : 'auto',
height: item.height ? item.height + 'px' : 'auto',
fontSize: item.fontSize+'px',
color: item.fontColor,
textStroke: item.strokeShow ? item.strokeSize + 'px ' + item.strokeColor : 'none',
textAlign: item.align,
background: item.fontBgColor,
fontFamily: item.fontFamily
}"
@mousedown.prevent="wraptouchStart($event, item.id)"
:data-content="item.type == 'zm' ? '此处是字幕' : item.content"
:data-color="item.fontColor"
v-html="item.type == 'zm' ? ('此处是字幕') : item.content"></div>
<!-- 删除按钮 -->
<div
class="x"
v-if="item.active"
@click.stop="deleteItem(item.id)">
<img src="@/assets/x.png" />
</div>
<!-- 缩放按钮 -->
<div
class="s"
v-if="item.active"
style="transform-origin:center;"
@mousedown.prevent="oTouchStart($event, item.id)"
>
</div>
<div
class="s s2"
v-if="item.active"
style="transform-origin:center;"
@mousedown.prevent="oTouchStart($event, item.id)"
>
</div>
<div
class="s s3"
v-if="item.active"
style="transform-origin:center;"
@mousedown.prevent="oTouchStart($event, item.id)"
>
</div>
<!-- 旋转按钮 -->
<div
class="o"
v-if="item.active && item.type == 'tt'"
style="transform-origin:center;"
@mousedown.prevent="oScaleStart($event, item.id)"
>
<img src="@/assets/o.png"/>
</div>
<!-- 拉宽按钮 -->
<div
class="lw"
v-if="item.active && (item.type == 'bt')"
style="transform-origin:center;"
@mousedown.prevent="oLwhStart($event, item.id, 'w')"
>
</div>
<!-- 拉高按钮 -->
<div
class="lh"
v-if="item.active && (item.type == 'bt')"
style="transform-origin:center;"
@mousedown.prevent="oLwhStart($event, item.id, 'h')"
>
</div>
</div>
</template>
</div>
</template>
<style lang="less" scoped>
.img-canvas {
user-select: none;
position: relative;
background: yellowgreen;
display: block;
margin: 0 auto;
width: 360px;
height: 640px;
.img-wrap {
position: absolute;
top: 20px;
left: 20px;
transform-origin: center;
padding: 10px;
box-sizing: border-box;
&.touch-active {
&::after {
position: absolute;
top: 0;
left: 0;
content: '';
width: 100%;
height: 100%;
border: 6px solid #0054D1;
box-sizing: border-box;
pointer-events: none;
}
}
.img {
display: block;
}
.txt {
display: block;
/* 通过属性选择器结合伪元素before 实现文字外描边效果 */
&[data-content]::before {
/* attr()是用来获取被选中元素的某属性值,并且在样式文件中使用 */
content: attr(data-content);
position: absolute;
/* 实现元素外描边的关键 */
-webkit-text-stroke: 0;
/* 文本颜色 */
color: attr(data-color);
}
&.slh {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
&[data-content]::before {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
.x {
z-index: 2;
width: 50px;
height: 50px;
position: absolute;
top: 0;
left: 0;
transform: translate(-50%, -50%);
background: white;
border-radius: 100%;
display: flex;
align-items: center;
justify-content: center;
box-shadow:0 5px 5px 5px rgba(0, 0, 0, 0.2);
image {
width: 100%;
height: 100%;
}
}
.o {
width: 50px;
height: 50px;
position: absolute;
bottom: -20px;
left: 50%;
transform: translate(-50%, 100%);
background: white;
border-radius: 100%;
display: flex;
align-items: center;
justify-content: center;
box-shadow:0 5px 5px 5px rgba(0, 0, 0, 0.2);
image {
width: 100%;
height: 100%;
}
}
.s {
width: 30px;
height: 30px;
position: absolute;
top: 0;
right: 0;
transform: translate(50%, -50%);
background: #0054D1;
border-radius: 100%;
display: flex;
align-items: center;
justify-content: center;
box-shadow:0 5px 5px 5px rgba(0, 0, 0, 0.2);
&.s2 {
top: auto;
bottom: 0;
right: 0;
transform: translate(50%, 50%);
}
&.s3 {
top: auto;
bottom: 0;
left: 0;
right: auto;
transform: translate(-50%, 50%);
}
}
.lw {
z-index: 2;
position: absolute;
top: 50%;
right: 0;
transform: translate(50%, -50%);
width: 15px;
height: 40px;
background: white;
border-radius: 5px;
}
.lh {
z-index: 2;
position: absolute;
left: 50%;
bottom: 0;
transform: translate(-50%, 50%);
width: 40px;
height: 15px;
background: white;
border-radius: 5px;
}
}
}
</style>
<script>
import { getUUID, algorithm } from '@/utils/utils'
export default {
props: {
width: {
type: Number,
default: 360
},
height: {
type: Number,
default: 640
},
templateId: {
type: Number,
default: 304
}
},
data() {
return {
timeFlag: '',
toucheWrap: {}, // 图层容器
index: 0, // 当前点击图片的index
items: [],
firstCopyItems: [],
spItems: [], // 视频对象数组信息
fmItems:[], // 封面对象数组信息
spPart: [], // 视频片段列表
spPartIndex: 0, // 当前选中的视频片段的index
sDpi: 1, // 模版尺寸 720*1280
}
},
created() {
this.sDpi = 720/this.width;
console.log(this.$parent)
},
mounted() {
this.toucheWrap = {
width: this.width,
height: this.height,
top: document.getElementById('img-canvas').offsetTop,
left: document.getElementById('img-canvas').offsetLeft
}
},
onUnmounted() {
if(this.timeFlag) {
clearTimeout(this.timeFlag)
}
},
methods: {
// 设置图层对象的信息
setDropItem(obj, call) {
console.log('setDropItem', obj)
return new Promise((resolve, reject)=> {
let data = {}; // 存储拖拽对象信息
let type = obj.type;
let content = obj.content;
// 背景、AI数字人、贴图
if(type == 'bg' || type == 'ai' || type == 'tt') {
// 获取图片信息
var img = new Image();
// 图片地址
img.src = content;
img.onload = () => {
let res = {
width: img.width,
height: img.height
}
// 初始化数据
let maxWidth = 150, maxHeight = 150; // 设置最大宽高
if(type == 'bg') {
maxWidth = 360
maxHeight = 640
}
if (res.width > maxWidth || res.height > maxHeight) { // 原图宽或高大于最大值就执行
if (res.width / res.height > maxWidth / maxHeight) { // 判断比例使用最大值的宽或高作为基数计算
data.width = maxWidth;
data.height = Math.round(maxWidth * (res.height / res.width));
} else {
data.height = maxHeight;
data.width = Math.round(maxHeight * (res.width / res.height));
}
} else {
data.width = res.width;
data.height = res.height;
}
data.iobsKey = '';
data.show = true; // 显示
data.type = type; // 对象类型 type - [bg, ai, tt, zm]
data.content = content; // 显示地址
data.id = algorithm(); // id
data.top = 0; // top定位
data.left = 0; // left定位
// 圆心坐标
data.x = data.left + data.width / 2;
data.y = data.top + data.height / 2;
data.scale = 1; // scale缩放
data.rotate = 0; // 旋转角度
data.active = false; // 选中状态
console.log(this.items)
if(type == 'bg') {
data.zIndex = 0; // 层级
} else if(this.items.find(it => it.type == 'bg')) {
data.zIndex = this.items.length; // 层级
} else {
data.zIndex = this.items.length+1; // 层级
}
// 覆盖原数据
data = {
...data,
...obj
}
// 封面
if(this.isCoverEdit || obj.isFm) {
this.fmItems.push(data); // 每增加一张图片数据增加一条信息
this.items = this.fmItems;
}
// 视频
else {
if(this.spPart[this.spPartIndex]) {
this.spItems = this.spPart[this.spPartIndex]
} else {
this.spItems = []
}
this.spItems.push(data); // 每增加一张图片数据增加一条信息
this.spPart[this.spPartIndex] = this.spItems
this.items = this.spPart[this.spPartIndex]
}
resolve();
call && call()
}
}
// 标题
else if (type == 'bt' || type == 'zm') {
// 初始化数据
data.width = 0;
data.height = 0;
data.show = true; // 显示
data.fontFamily = 'simhei'; // 字体
data.strokeShow = true; // 显示描边
data.align = 'left'; // 文本对齐方式
data.fontColor = '#000'; // 字体颜色
data.fontSize = 12; // 字号大小
data.fontBgColor = ''; // 文本背景颜色
data.strokeSize = 0; // 描边粗细
data.strokeColor = '#000'; // 描边颜色
data.type = type; // 对象类型 type - [bg, ai, tt, zm]
data.content = content; // 显示内容
data.id = algorithm(); // id
data.scale = 1; // scale缩放
data.rotate = 0; // 旋转角度
data.active = false; // 选中状态
if(type == 'bg') {
data.zIndex = 0; // 层级
} else if(this.items.find(it => it.type == 'bg')) {
data.zIndex = this.items.length; // 层级
} else {
data.zIndex = this.items.length+1; // 层级
}
data.top = 0; // top定位
data.left = 0; // left定位
// 圆心坐标
data.x = data.left + data.width / 2;
data.y = data.top + data.height / 2;
// 字幕
if(type == 'zm') {
// 圆心坐标
data.x = this.toucheWrap.width/2;
data.y = this.toucheWrap.height - 20;
data.ys = obj.ys
data.yl = obj.yl
data.yd = obj.yd
data.ysu = obj.ysu
}
// 覆盖原数据
data = {
...data,
...obj
}
// 封面
if(this.isCoverEdit || obj.isFm) {
this.fmItems.push(data);
this.items = this.fmItems; // 每增加一张图片数据增加一条信息
}
// 视频
else {
if(this.spPart[this.spPartIndex]) {
this.spItems = this.spPart[this.spPartIndex]
} else {
this.spItems = []
}
this.spItems.push(data); // 每增加一张图片数据增加一条信息
this.spPart[this.spPartIndex] = this.spItems;
this.items = this.spPart[this.spPartIndex];
}
let _i = this.items.length - 1;
this.items = JSON.parse(JSON.stringify(this.items))
setTimeout(() => {
let doc = document.getElementById('txt' + this.items[_i].id)
console.log('doc',this.items[_i].id, doc)
this.items[_i].width = doc.offsetWidth;
this.items[_i].height = doc.offsetHeight;
if(type == 'zm') {
this.items[_i].left = this.items[_i].x - this.items[_i].width / 2;
this.items[_i].top = this.items[_i].y - this.items[_i].height / 2
} else {
this.items[_i].x = this.items[_i].left + this.items[_i].width / 2;
this.items[_i].y = this.items[_i].top + this.items[_i].height / 2;
}
// 覆盖原数据
this.items[_i] = {
...this.items[_i],
...obj
}
if(this.isCoverEdit || obj.isFm) {
// 封面编辑
this.fmItems = this.items
} else {
// 视频编辑
this.spPart[this.spPartIndex] = this.items
}
resolve()
call && call()
}, 500)
}
})
},
// 改变图片层级
changeZIndex(isAdd) {
let _zIndex = this.items[this.index].zIndex
isAdd ? _zIndex += 1 : _zIndex -= 1
// 循环图片数组获取点击的图片信息
for (let i = 0; i < this.items.length; i++) {
if (_zIndex == this.items[i].zIndex) {
isAdd ? this.items[i].zIndex -= 1 : this.items[i].zIndex += 1
break;
}
}
// 上移一层 | 下移一层
isAdd ? this.items[this.index].zIndex += 1 : this.items[this.index].zIndex -= 1
if(this.items[this.index].zIndex > this.items.length-1) {
this.items[this.index].zIndex = this.items.length-1;
this.$message('已是最顶层');
}
if(this.items[this.index].zIndex < 1) {
this.items[this.index].zIndex = 1;
this.$message('已是最底层');
}
if(this.isCoverEdit) {
this.fmItems = this.items
} else {
this.spPart[this.spPartIndex] = this.items
}
},
wrapStart(e) {
console.log('wrapStart')
this.drag = true;
},
wrapMove(e) {
if(this.drag) {
console.log('wrapMove')
if(this.items[this.index].active){
if(this.items[this.index].sMoveabled) {
console.log('wrapMove-sMoveabled')
this.oTouchMove(e);
} else if (this.items[this.index].moveabled) {
console.log('wrapMove-moveabled')
this.wraptouchMove(e);
} else if (this.items[this.index].lMoveabled) {
console.log('wrapMove-lMoveabled')
this.oLwhMove(e, this.items[this.index].id, this.items[this.index].lMoveType);
}
}
}
},
wrapUp(e) {
console.log('wrapUp')
this.drag = false;
this.oTouchUp();
},
// 点击图层
wraptouchStart (e, id) {
console.log('点击图层',e)
// 循环图片数组获取点击的图片信息
for (let i = 0; i < this.items.length; i++) {
this.items[i].active = false;
this.items[i].moveabled = false;
this.items[i].sMoveabled = false;
if (id == this.items[i].id) {
console.log(id)
this.index = i;
this.items[this.index].active = true;
this.items[this.index].moveabled = true;
}
}
let editType = '';
if(this.items[this.index].type == 'bg') {
editType = 'bg'
}
else if(this.items[this.index].type == 'ai') {
editType = 'ai'
}
else if(this.items[this.index].type == 'tt') {
editType = 'tt'
}
else if(this.items[this.index].type == 'bt') {
editType = 'bt'
this.btAttribute = {
color: this.items[this.index].fontColor,
align: this.items[this.index].align,
size: this.items[this.index].fontSize,
family: this.items[this.index].fontFamily,
bgColor: this.items[this.index].fontBgColor,
showStroke:this.items[this.index].strokeShow,
strokeSize: this.items[this.index].strokeSize,
strokeColor: this.items[this.index].strokeColor
}
}
else if(this.items[this.index].type == 'zm') {
editType = 'zm'
this.zmAttribute = {
color: this.items[this.index].fontColor,
align: this.items[this.index].align,
size: this.items[this.index].fontSize,
family: this.items[this.index].fontFamily,
bgColor: this.items[this.index].fontBgColor,
showStroke: this.items[this.index].strokeShow,
strokeSize: this.items[this.index].strokeSize,
strokeColor: this.items[this.index].strokeColor,
show: this.items[this.index].show
}
}
if(this.isCoverEdit) {
this.fmItems = this.items
} else {
this.spPart[this.spPartIndex] = this.items
}
this.editType = editType
this.isShowEditInput = false
this.isInitEdit = false
if(this.items[this.index].type == 'bg') {
return
}
// 获取点击的坐标值
this.items[this.index].lx = e.pageX;
this.items[this.index].ly = e.pageY;
this.items = JSON.parse(JSON.stringify(this.items))
},
// 拖动图层
wraptouchMove (e, id) {
let { items, index } = this;
if(!items[index].moveabled) {
return
}
console.log('拖动图层', e, id)
items[index]._lx = e.pageX;
items[index]._ly = e.pageY;
// if(items[index].type == 'zm') {
// items[index].top += items[index]._ly - items[index].ly;
// items[index].y += items[index]._ly - items[index].ly;
// } else {
items[index].left += items[index]._lx - items[index].lx;
items[index].top += items[index]._ly - items[index].ly;
items[index].x += items[index]._lx - items[index].lx;
items[index].y += items[index]._ly - items[index].ly;
// }
items[index].lx = e.pageX;
items[index].ly = e.pageY;
this.items = items
if(this.isCoverEdit) {
this.fmItems = this.items
} else {
this.spPart[this.spPartIndex] = this.items
}
},
// 点击旋转图标
oScaleStart (e, id) {
// 找到点击的那个图片对象,并记录
for (let i = 0; i < this.items.length; i++) {
this.items[i].active = false;
if (id == this.items[i].id) {
this.index = i;
this.items[this.index].active = true;
}
}
// 获取作为移动前角度的坐标
this.items[this.index].tx = e.pageX - this.toucheWrap.left;
this.items[this.index].ty = e.pageY - this.toucheWrap.top;
// 移动前的角度
this.items[this.index].anglePre = this.countDeg(
this.items[this.index].x,
this.items[this.index].y,
this.items[this.index].tx,
this.items[this.index].ty
);
},
// 移动旋转图标
oScaleMove (e, id) {
let { items, index } = this;
// 记录移动后的位置
items[index]._tx = e.pageX - this.toucheWrap.left;
items[index]._ty = e.pageY - this.toucheWrap.top;
// 移动的点到圆心的距离
items[index].disPtoO = this.getDistancs(items[index].x, items[index].y, items[index]._tx, items[index]._ty - 10)
// 移动后位置的角度
items[index].angleNext = this.countDeg(items[index].x, items[index].y, items[index]._tx, items[index]._ty)
// 角度差
items[index].new_rotate = items[index].angleNext - items[index].anglePre;
//叠加的角度差
items[index].rotate += items[index].new_rotate;
items[index].angle = items[index].type == 'tt' ? items[index].rotate : 0; //赋值
//用过移动后的坐标赋值为移动前坐标
items[index].tx = e.pageX - this.toucheWrap.left;
items[index].ty = e.pageY - this.toucheWrap.top;
// 下次移动前的角度
items[index].anglePre = this.countDeg(items[index].x, items[index].y, items[index].tx, items[index].ty)
this.items = items;
if(this.isCoverEdit) {
this.fmItems = items
} else {
this.spPart[this.spPartIndex] = items
}
},
// 点击伸缩图标
oTouchStart (e, id) {
console.log('点击伸缩图标')
// 找到点击的那个图片对象,并记录
for (let i = 0; i < this.items.length; i++) {
this.items[i].active = false;
this.items[i].moveabled = false;
this.items[i].sMoveabled = false;
if (id == this.items[i].id) {
this.index = i;
this.items[this.index].active = true;
this.items[this.index].sMoveabled = true;
}
}
// 获取作为移动前的坐标
this.items[this.index].tx = e.pageX - this.toucheWrap.left;
this.items[this.index].ty = e.pageY - this.toucheWrap.top;
// 获取图片半径
this.items[this.index].r = this.getDistancs(
this.items[this.index].x,
this.items[this.index].y,
this.items[this.index].tx,
this.items[this.index].ty
);
},
// 移动伸缩图标
oTouchMove (e, id) {
console.log('移动伸缩图标')
let { items, index, toucheWrap, sDpi } = this;
if(!items[index].sMoveabled) {
return
}
// 记录移动后的位置
items[index]._tx = e.pageX - toucheWrap.left;
items[index]._ty = e.pageY - toucheWrap.top;
// 移动的点到圆心的距离
items[index].disPtoO = this.getDistancs(items[index].x, items[index].y, items[index]._tx, items[index]._ty - 10);
let _s = items[index].disPtoO / items[index].r;
if(items[index].type == 'bt' || items[index].type == 'zm') {
let _oldFontSize = items[index].fontSize;
items[index].fontSize = items[index].fontSize * _s;
let maxFontSize = items[index].type == 'zm' ? 40/sDpi : 100/sDpi;
if(items[index].fontSize > maxFontSize) {
items[index].fontSize = maxFontSize;
_s = 100/_oldFontSize
} else if(items[index].fontSize < 12/sDpi) {
items[index].fontSize = 12/sDpi;
_s = 12/(sDpi*_oldFontSize)
}
if(items[index].type == 'bt') {
this.btAttribute = {
color: items[index].fontColor,
align: items[index].align,
size: items[index].fontSize,
family: items[index].fontFamily,
bgColor: items[index].fontBgColor,
showStroke: items[index].strokeShow,
strokeSize: items[index].strokeSize,
strokeColor: items[index].strokeColor
}
} else {
this.zmAttribute = {
color: items[index].fontColor,
align: items[index].align,
size: items[index].fontSize,
family: items[index].fontFamily,
bgColor: items[index].fontBgColor,
showStroke: items[index].strokeShow,
strokeSize: items[index].strokeSize,
strokeColor: items[index].strokeColor,
show: items[index].show
}
}
}
// 使用缩放
// items[index].scale = items[index].disPtoO / items[index].r;
// 不使用缩放
items[index].width = _s * items[index].width
items[index].height = _s * items[index].height
items[index].top = items[index].y - items[index].height/2
items[index].left = items[index].x - items[index].width/2
// 获取图片半径
items[index].r = items[index].disPtoO;
this.items = items;
if(this.isCoverEdit) {
this.fmItems = items
} else {
this.spPart[this.spPartIndex] = items
}
},
// 鼠标取消
oTouchUp (e, id) {
console.log('oTouchUp')
this.items[this.index].lMoveabled = false;
this.items[this.index].sMoveabled = false;
this.items[this.index].moveabled = false;
},
// 点击文字拉宽高图标
oLwhStart (e, id, _type) {
// 找到点击的那个图片对象,并记录
for (let i = 0; i < this.items.length; i++) {
this.items[i].active = false;
this.items[i].moveabled = false;
this.items[i].sMoveabled = false;
this.items[i].lMoveabled = false;
if (id == this.items[i].id) {
this.index = i;
this.items[this.index].active = true;
this.items[this.index].lMoveabled = true;
this.items[this.index].lMoveType = _type;
}
}
// 获取作为移动前的坐标
this.items[this.index].tx = e.pageX - this.toucheWrap.left;
this.items[this.index].ty = e.pageY - this.toucheWrap.top;
// 获取触摸点到圆心距离
this.items[this.index].r = this.getDistancs(
this.items[this.index].x,
this.items[this.index].y,
this.items[this.index].tx,
this.items[this.index].ty
);
},
// 移动文字拉宽高图标
oLwhMove (e, id, _type) {
let { items, index, toucheWrap } = this;
if(!items[index].lMoveabled) {
return
}
// 记录移动后的位置
items[index]._tx = e.pageX - toucheWrap.left;
items[index]._ty = e.pageY - toucheWrap.top;
// 移动的点到圆心的距离
items[index].disPtoO = this.getDistancs(items[index].x, items[index].y, items[index]._tx, items[index]._ty - 10)
let _s = items[index].disPtoO / items[index].r;
// 不使用缩放
if(_type == 'w') {
items[index].width = _s * items[index].width
items[index].left = items[index].x - items[index].width/2
} else {
items[index].height = _s * items[index].height
items[index].top = items[index].y - items[index].height/2
}
// 获取触摸点到圆心距离
items[index].r = items[index].disPtoO;
this.items = items;
if(this.isCoverEdit) {
this.fmItems = items
} else {
this.spPart[this.spPartIndex] = items
}
},
// 删除图层对象
deleteItem (id) {
let newList = [];
for (let i = 0; i < this.items.length; i++) {
// 更新层级
if(this.items[i].zIndex > this.items[this.index].zIndex) {
this.items[i].zIndex -= 1
}
if (id != this.items[i].id) {
newList.push(this.items[i])
}
}
if (newList.length > 0) {
newList[newList.length - 1].active = true; // 剩下图片组最后一个选中
this.index = newList.length - 1
} else {
this.index = 0
}
this.items = newList;
if(this.isCoverEdit) {
this.fmItems = this.items
} else {
this.spPart[this.spPartIndex] = this.items
}
},
// 计算坐标点到圆心的距离
getDistancs (cx, cy, pointer_x, pointer_y) {
var ox = pointer_x - cx;
var oy = pointer_y - cy;
return Math.sqrt(
ox * ox + oy * oy
);
},
/*
* 参数cx和cy为图片圆心坐标
* 参数pointer_x和pointer_y为手点击的坐标
* 返回值为手点击的坐标到圆心的角度
*/
countDeg (cx, cy, pointer_x, pointer_y) {
var ox = pointer_x - cx;
var oy = pointer_y - cy;
var to = Math.abs(ox / oy);
var angle = Math.atan(to) / (2 * Math.PI) * 360;
// 相对在左上角,第四象限,js中坐标系是从左上角开始的,这里的象限是正常坐标系
if (ox < 0 && oy < 0) {
angle = -angle;
}
// 左下角,3象限
else if (ox <= 0 && oy >= 0) {
angle = -(180 - angle)
}
// 右上角,1象限
else if (ox > 0 && oy < 0) {
angle = angle;
}
// 右下角,2象限
else if (ox > 0 && oy > 0) {
angle = 180 - angle;
}
return angle;
},
fetchError (msg) {
this.loading.close();
if(msg) {
this.$message.error(msg);
}
}
}
}
</script>