<template>
<!-- 用户选择的区域大小 -->
<div
ondragstart="return false"
:class="['selectedArea', active ? 'active' : '']"
ref="box"
:data-elementUid="uid"
:style="{
transform: `translate(${l}px,${t}px)`,
width: w + 'px',
height: h + 'px',
fontSize: fontSizeChange + 'px',
}"
id="movearea"
data-action="move"
@click.stop.prevent="() => {}"
@mousedown.stop="mousedown"
>
<slot></slot>
<!-- {{ videoWidth }} {{ videoHeight }} -->
<!-- 以下内容为 8 个操作点位 -->
<div data-action="resize" data-direction="top" class="icon top"></div>
<div data-action="resize" data-direction="left" class="icon middleLeft"></div>
<div data-action="resize" data-direction="right" class="icon middleRight"></div>
<div data-action="resize" data-direction="bottom" class="icon bottom"></div>
<div data-action="resize" data-direction="top,left" class="icon topLeft"></div>
<div data-action="resize" data-direction="top,right" class="icon topRight"></div>
<div data-action="resize" data-direction="bottom,left" class="icon bottomLeft"></div>
<div data-action="resize" data-direction="bottom,right" class="icon bottomRight"></div>
</div>
</template>
<script>
import { throttle } from '../../utils/bus';
export default {
name: 'draggable-resizable',
props: {
updateCurrentPage: Function,
activeUid: {
type: String,
require: false,
},
index: {
type: Number,
require: true,
},
scalingRatio: {
type: Number,
default: 1,
required: true,
},
active: {
type: Boolean,
},
element: {
type: Object,
required: true,
},
maxLeft: {
type: Number,
required: true,
},
maxTop: {
type: Number,
required: true,
},
minWidth: {
type: Number,
default: 50,
},
minHeight: {
type: Number,
default: 50,
},
},
data() {
return {
l: 0,
t: 0,
w: 0,
h: 0,
draggingElement: null,
mouseStart: {
x: 0,
y: 0,
offsetX: 0,
offsetY: 0,
prevTop: 0,
prevLeft: 0,
prevRight: 0,
prevBottom: 0,
},
resizeDirection: '',
minSize: 25,
};
},
computed: {
uid() {
return this.element.uid;
},
width() {
return this.element.width;
},
height() {
return this.element.height;
},
top() {
return this.element.top;
},
left() {
return this.element.left;
},
ratio() {
return this.width / this.height;
},
fontSizeChange() {
let ratio = 0.5;
let desiredFontSize = 16;
return (Math.min(this.w * ratio, this.h) * desiredFontSize) / 100;
},
},
watch: {
scalingRatio: {
handler: function (val) {
this.l = this.left * val;
this.t = this.top * val;
this.w = this.width * val;
this.h = this.height * val;
},
immediate: true,
},
left: {
handler: function (val) {
this.l = val * this.scalingRatio;
},
immediate: true,
},
top: {
handler: function (val) {
this.t = val * this.scalingRatio;
},
immediate: true,
},
width: {
handler: function (val) {
this.w = val * this.scalingRatio;
},
immediate: true,
},
height: {
handler: function (val) {
this.h = val * this.scalingRatio;
},
immediate: true,
},
},
mounted() {
document.addEventListener('mousemove', throttle(this.mouseMove, 30));
document.addEventListener('mouseup', this.mouseUp);
},
beforeDestroy() {
document.removeEventListener('mouseup', this.mouseMove);
document.removeEventListener('mousemove', this.mouseUp);
},
methods: {
dbClickElement() {
console.log('dbClickElement');
this.updateCurrentPage(page => {
page.activeElementsUid = this.uid;
});
},
highlightElement() {
this.updateCurrentPage(page => {
page.activeElementsUid = this.uid;
});
},
inputBlurClick() {
console.log('失去焦点');
},
mouseUp() {
if (!this.draggingElement) return;
this.positionChange();
this.draggingElement = null;
this.resizeDirection = '';
this.updateBubbleText();
},
mouseMove(event) {
if (this.element.type == 'avatar') {
return;
}
if (!this.draggingElement) return;
let { action = '' } = this.draggingElement.dataset;
const curAction = event.target.dataset.action;
action = action ? action : curAction;
if (this.$refs.box.contains(this.draggingElement)) {
action = action ? action : 'move';
}
if (action == 'move') {
const { x, y, prevTop, prevLeft, prevWidth, prevHeight } = this.mouseStart;
const minSize = this.minSize;
let newX = event.clientX - x + prevLeft;
let newY = event.clientY - y + prevTop;
let minX = -prevWidth + minSize;
let minY = -prevHeight + minSize;
let maxX = this.maxLeft - minSize;
let maxY = this.maxTop - minSize;
newX = Math.max(minX, Math.min(newX, maxX));
newY = Math.max(minY, Math.min(newY, maxY));
this.l = newX;
this.t = newY;
} else if (action == 'resize') {
this.resizeBox(event);
}
},
resizeBox(e) {
let { direction = '' } = e.target.dataset;
if (this.draggingElement == null || this.draggingElement.dataset.action !== 'resize') return;
if (direction && this.resizeDirection == '') {
this.resizeDirection = direction;
}
direction = this.resizeDirection;
if (!direction) return;
const { clientX: x, clientY: y } = e;
const { x: oldX, y: oldY } = this.mouseStart;
const moveX = x - oldX,
moveY = y - oldY;
if (direction == 'top') {
this.moveTop(moveX, moveY);
} else if (direction == 'bottom') {
this.moveBottom(moveX, moveY);
} else if (direction == 'left') {
this.moveLeft(moveX);
} else if (direction == 'right') {
this.moveRight(moveX);
} else if (direction == 'top,left') {
this.moveTopLeft(moveX, moveY);
} else if (direction == 'top,right') {
this.moveTopRight(moveX, moveY);
} else if (direction == 'bottom,left') {
this.moveBottomLeft(moveX, moveY);
} else if (direction == 'bottom,right') {
this.moveBottomRight(moveX, moveY);
}
this.$emit('resize', this.element);
},
updateBubbleText() {
},
positionChange() {
const { width, height, left, top, index } = this.calculateVideoPos();
this.updateCurrentPage(page => {
for (const el of page.elements) {
if (el.uid == this.uid) {
el.width = width;
el.height = height;
el.left = left;
el.top = top;
}
}
});
this.$emit('change', this.element);
},
moveTop(moveX, moveY) {
const { prevLeft, prevWidth, prevHeight, prevTop } = this.mouseStart;
const prevCenterX = prevLeft + prevWidth / 2,
prevCenterY = prevTop + prevHeight / 2;
const ratio = this.ratio;
this.h = Math.max(prevHeight - moveY, this.minHeight);
this.w = this.h * ratio;
this.t = prevCenterY - this.h / 2;
this.l = prevCenterX - this.w / 2;
this.updateBubbleText();
},
moveBottom(moveX, moveY) {
const { prevLeft, prevWidth, prevHeight, prevTop } = this.mouseStart;
const prevCenterX = prevLeft + prevWidth / 2,
prevCenterY = prevTop + prevHeight / 2;
const ratio = this.ratio;
this.h = Math.max(prevHeight + moveY, this.minHeight);
this.w = this.h * ratio;
this.t = prevCenterY - this.h / 2;
this.l = prevCenterX - this.w / 2;
this.updateBubbleText();
},
getPrevCenterPos() {
const { prevLeft, prevWidth, prevHeight, prevTop } = this.mouseStart;
const prevCenterX = prevLeft + prevWidth / 2,
prevCenterY = prevTop + prevHeight / 2;
return { x: prevCenterX, y: prevCenterY };
},
moveLeft(moveX) {
const { prevLeft, prevWidth, prevHeight, prevTop } = this.mouseStart;
const prevCenterX = prevLeft + prevWidth / 2,
prevCenterY = prevTop + prevHeight / 2;
const ratio = this.ratio;
this.w = Math.max(prevWidth - moveX, this.minWidth);
this.h = this.w / ratio;
this.t = prevCenterY - this.h / 2;
this.l = prevCenterX - this.w / 2;
this.updateBubbleText();
},
moveRight(moveX) {
const { prevLeft, prevWidth, prevHeight, prevTop } = this.mouseStart;
const prevCenterX = prevLeft + prevWidth / 2,
prevCenterY = prevTop + prevHeight / 2;
const ratio = this.ratio;
this.w = Math.max(prevWidth + moveX, this.minWidth);
this.h = this.w / ratio;
this.t = prevCenterY - this.h / 2;
this.l = prevCenterX - this.w / 2;
this.updateBubbleText();
},
moveTopLeft(moveX, moveY) {
const { prevLeft, prevWidth, prevTop, prevHeight } = this.mouseStart;
const ratio = this.ratio;
this.h = Math.max(prevHeight - moveY, this.minHeight);
this.w = this.h * ratio;
this.t = prevTop + prevHeight - this.h;
this.l = prevLeft + prevWidth - this.w;
this.updateBubbleText();
},
moveTopRight(moveX, moveY) {
const { prevTop, prevLeft, prevHeight } = this.mouseStart;
const ratio = this.ratio;
this.h = Math.max(prevHeight - moveY, this.minHeight);
this.w = this.h * ratio;
this.t = prevTop + prevHeight - this.h;
this.l = prevLeft;
this.updateBubbleText();
},
moveBottomLeft(moveX, moveY) {
const { prevLeft, prevWidth, prevHeight } = this.mouseStart;
const ratio = this.ratio;
this.h = Math.max(prevHeight + moveY, this.minHeight);
this.w = this.h * ratio;
this.l = prevLeft + prevWidth - this.w;
this.updateBubbleText();
},
moveBottomRight(moveX, moveY) {
const { prevHeight } = this.mouseStart;
const ratio = this.ratio;
this.h = Math.max(prevHeight + moveY, this.minHeight);
this.w = this.h * ratio;
this.updateBubbleText();
},
mousedown(e) {
this.draggingElement = e.target;
const box = this.$refs.box;
const rect = box.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
this.mouseStart = {
x: e.clientX,
y: e.clientY,
offsetX: x,
offsetY: y,
prevTop: this.t,
prevLeft: this.l,
prevWidth: this.w,
prevHeight: this.h,
};
this.highlightElement();
},
reflectCropperPos(val) {
let { top, left, width, height } = val;
this.l = left * this.scalingRatio;
this.t = top * this.scalingRatio;
this.w = width * this.scalingRatio;
this.h = height * this.scalingRatio;
},
calculateVideoPos() {
let { t, l, w, h, scalingRatio, index } = this;
return {
index,
width: w / scalingRatio,
height: h / scalingRatio,
left: l / scalingRatio,
top: t / scalingRatio,
};
},
},
};
</script>
<style lang="scss" src="./index.scss" scoped></style>