//html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script src="./drag.js"></script>
<link
rel="stylesheet"
href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"
/>
<style>
.cert-wrap {
position: relative;
width: 375px;
min-width: 375px;
}
.bg {
width: 100%;
pointer-events: none;
user-select: none;
}
.word {
position: absolute;
}
.el-form-item__content {
display: flex;
align-items: center;
}
.el-form-item__content > div {
margin-right: 10px;
}
.vdr {
position: absolute;
box-sizing: border-box;
cursor: move;
}
.vdr.active:before {
content: "";
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
box-sizing: border-box;
outline: 1px dashed #d6d6d6;
}
.vdr-stick {
box-sizing: border-box;
position: absolute;
font-size: 1px;
background: #ffffff;
border: 1px solid #6c6c6c;
box-shadow: 0 0 2px #bbb;
}
.inactive .vdr-stick {
display: none;
}
.vdr-stick-tl,
.vdr-stick-br {
cursor: nwse-resize;
}
.vdr-stick-tm,
.vdr-stick-bm {
left: 50%;
cursor: ns-resize;
}
.vdr-stick-tr,
.vdr-stick-bl {
cursor: nesw-resize;
}
.vdr-stick-ml,
.vdr-stick-mr {
top: 50%;
cursor: ew-resize;
}
.vdr-stick.not-resizable {
display: none;
}
</style>
</head>
<body>
<div id="root">
<el-row style="margin-bottom: 20px">
<el-radio-group v-model="template" @change="onChange">
<el-radio label="0">预设模板1</el-radio>
<el-radio label="1">预设模板2</el-radio>
<el-radio label="2">预设模板3</el-radio>
<el-radio label="3">预设模板4</el-radio>
<el-radio label="4">预设模板5</el-radio>
<el-radio label="custom">自定义</el-radio>
</el-radio-group>
</el-row>
<el-row type="flex" v-if="Object.keys(form).length">
<div class="cert-wrap" v-if="template === 'custom'">
<img :src="form.empower_template" alt="" class="bg" />
<vue-drag-resize
@dragging="onDragging"
@clicked="onActivated"
w="80"
:h="form.wx_nickname.style.fontSize * 2"
:x="form.wx_nickname.style.x"
:y="form.wx_nickname.style.y"
>
<div
class="word wx_nickname"
data-key="wx_nickname"
:style="{...form.wx_nickname.style, fontSize: form.wx_nickname.style.fontSize + 'px'}"
>
{{form.wx_nickname.value}}
</div>
</vue-drag-resize>
<vue-drag-resize
@dragging="onDragging"
w="80"
:h="form.agent_name.style.fontSize * 2"
:x="form.agent_name.style.x"
:y="form.agent_name.style.y"
>
<div
class="word agent_name"
:style="{...form.agent_name.style, fontSize: form.agent_name.style.fontSize + 'px'}"
>
{{form.agent_name.value}}
</div>
</vue-drag-resize>
<vue-drag-resize
@dragging="onDragging"
w="80"
:h="form.time.style.fontSize * 2"
:x="form.time.style.x"
:y="form.time.style.y"
>
<div
class="word time"
:style="{...form.time.style, fontSize: form.time.style.fontSize + 'px'}"
>
{{form.time.value}}
</div>
</vue-drag-resize>
<vue-drag-resize
@dragging="onDragging"
w="80"
:h="form.certificate_no.style.fontSize * 2"
:x="form.certificate_no.style.x"
:y="form.certificate_no.style.y"
>
<div
class="word certificate_no"
:style="{...form.certificate_no.style, fontSize: form.certificate_no.style.fontSize + 'px'}"
>
{{form.certificate_no.value}}
</div>
</vue-drag-resize>
<vue-drag-resize
@dragging="onDragging"
w="80"
:h="form.company_name.style.fontSize * 2"
:x="form.company_name.style.x"
:y="form.company_name.style.y"
>
<div
class="word company_name"
:style="{...form.company_name.style, fontSize: form.company_name.style.fontSize + 'px'}"
>
{{form.company_name.value}}
</div>
</vue-drag-resize>
</div>
<div class="cert-wrap" v-else>
<img :src="form.empower_template" alt="" class="bg" />
<div
class="word wx_nickname"
data-key="wx_nickname"
:style="{...form.wx_nickname.style, fontSize: form.wx_nickname.style.fontSize + 'px', top:form.wx_nickname.style.y+'px',left:form.wx_nickname.style.x+'px'}"
>
{{form.wx_nickname.value}}
</div>
<div
class="word agent_name"
:style="{...form.agent_name.style, fontSize: form.agent_name.style.fontSize + 'px',top:form.agent_name.style.y+'px',left:form.agent_name.style.x+'px'}"
>
{{form.agent_name.value}}
</div>
<div
class="word time"
:style="{...form.time.style, fontSize: form.time.style.fontSize + 'px',top:form.time.style.y+'px',left:form.time.style.x+'px'}"
>
{{form.time.value}}
</div>
<div
class="word certificate_no"
:style="{...form.certificate_no.style, fontSize: form.certificate_no.style.fontSize + 'px',top:form.certificate_no.style.y+'px',left:form.certificate_no.style.x+'px'}"
>
{{form.certificate_no.value}}
</div>
<div
class="word company_name"
:style="{...form.company_name.style, fontSize: form.company_name.style.fontSize + 'px',top:form.company_name.style.y+'px',left:form.company_name.style.x+'px'}"
>
{{form.company_name.value}}
</div>
</div>
<el-form
v-if="template === 'custom'"
ref="form"
:model="form.empower_template"
label-width="6em"
>
<el-form-item label="背景:">
<el-upload
class="upload"
drag
action="http://test.lojangcc.com/api/common/upload"
name="file"
headers="{
token: '27a4a2de-ac63-4f9b-948d-76e5d07be416',
appid: '1001'
}"
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">
将jpg/png文件拖到此处,或<em>点击上传</em>
</div>
</el-upload>
</el-form-item>
<el-form-item label="被授予人">
<el-input-number
size="mini"
v-model="form.wx_nickname.style.fontSize"
></el-input-number>
<el-select
size="mini"
v-model="form.wx_nickname.style.fontWeight"
placeholder="请选择"
>
<el-option v-for="item in fontWeight" :key="item" :value="item">
</el-option>
</el-select>
<el-color-picker
size="mini"
v-model="form.wx_nickname.style.color"
></el-color-picker>
</el-form-item>
<el-form-item label="授予等级">
<el-input-number
size="mini"
v-model="form.agent_name.style.fontSize"
></el-input-number>
<el-select
size="mini"
v-model="form.agent_name.style.fontWeight"
placeholder="请选择"
>
<el-option v-for="item in fontWeight" :key="item" :value="item">
</el-option>
</el-select>
<el-color-picker
size="mini"
v-model="form.agent_name.style.color"
></el-color-picker>
</el-form-item>
<el-form-item label="授权日期">
<el-input-number
size="mini"
v-model="form.time.style.fontSize"
></el-input-number>
<el-select
size="mini"
v-model="form.time.style.fontWeight"
placeholder="请选择"
>
<el-option v-for="item in fontWeight" :key="item" :value="item">
</el-option>
</el-select>
<el-color-picker
size="mini"
v-model="form.time.style.color"
></el-color-picker>
</el-form-item>
<el-form-item label="授权编号">
<el-input-number
size="mini"
v-model="form.certificate_no.style.fontSize"
></el-input-number>
<el-select
size="mini"
v-model="form.certificate_no.style.fontWeight"
placeholder="请选择"
>
<el-option v-for="item in fontWeight" :key="item" :value="item">
</el-option>
</el-select>
<el-color-picker
size="mini"
v-model="form.certificate_no.style.color"
></el-color-picker>
</el-form-item>
<el-form-item label="公司名称">
<el-input-number
size="mini"
v-model="form.company_name.style.fontSize"
></el-input-number>
<el-select
size="mini"
v-model="form.company_name.style.fontWeight"
placeholder="请选择"
>
<el-option v-for="item in fontWeight" :key="item" :value="item">
</el-option>
</el-select>
<el-color-picker
size="mini"
v-model="form.company_name.style.color"
></el-color-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">确认</el-button>
</el-form-item>
</el-form>
</el-row>
</div>
</body>
<script>
const templates = [
{
wx_nickname: {
value: "被授予人",
style: {
fontSize: "14",
fontWeight: "normal",
color: "#333",
y: 200,
x: 166
}
},
agent_name: {
value: "授予等级",
style: {
fontSize: "14",
fontWeight: "normal",
color: "#333",
y: 225,
x: 166
}
},
certificate_no: {
value: "授权编号",
style: {
fontSize: "10",
fontWeight: "normal",
color: "#333",
y: 332,
x: 150
}
},
time: {
value: "授权日期",
style: {
fontSize: "10",
fontWeight: "normal",
color: "#333",
y: 354,
x: 150
}
},
company_name: {
value: "公司名称",
style: {
fontSize: "14",
fontWeight: "normal",
color: "#333",
y: 460,
x: 155
}
},
empower_template:
"https://lejiangkeji.oss-cn-beijing.aliyuncs.com/uploads/sqmb/1.png"
},
{
wx_nickname: {
value: "被授予人",
style: {
fontSize: "14",
fontWeight: "normal",
color: "#333",
y: 200,
x: 166
}
},
agent_name: {
value: "授予等级",
style: {
fontSize: "14",
fontWeight: "normal",
color: "#333",
y: 235,
x: 166
}
},
certificate_no: {
value: "授权编号",
style: {
fontSize: "10",
fontWeight: "normal",
color: "#333",
y: 310,
x: 135
}
},
time: {
value: "授权日期",
style: {
fontSize: "10",
fontWeight: "normal",
color: "#333",
y: 330,
x: 135
}
},
company_name: {
value: "公司名称",
style: {
fontSize: "14",
fontWeight: "normal",
color: "#333",
y: 460,
x: 155
}
},
empower_template:
"https://lejiangkeji.oss-cn-beijing.aliyuncs.com/uploads/sqmb/2.png"
},
{
wx_nickname: {
value: "被授予人",
style: {
fontSize: "10",
fontWeight: "normal",
color: "#fff",
y: 200,
x: 166
}
},
agent_name: {
value: "授予等级",
style: {
fontSize: "14",
fontWeight: "normal",
color: "#fff",
y: 222,
x: 166
}
},
certificate_no: {
value: "授权编号",
style: {
fontSize: "10",
fontWeight: "normal",
color: "#fff",
y: 337,
x: 140
}
},
time: {
value: "授权日期",
style: {
fontSize: "10",
fontWeight: "normal",
color: "#fff",
y: 359,
x: 140
}
},
company_name: {
value: "公司名称",
style: {
fontSize: "14",
fontWeight: "normal",
color: "#fff",
y: 460,
x: 155
}
},
empower_template:
"https://lejiangkeji.oss-cn-beijing.aliyuncs.com/uploads/sqmb/3.jpg"
},
{
wx_nickname: {
value: "被授予人",
style: {
fontSize: "14",
fontWeight: "normal",
color: "#f0e9b0",
y: 210,
x: 166
}
},
agent_name: {
value: "授予等级",
style: {
fontSize: "14",
fontWeight: "normal",
color: "#f0e9b0",
y: 249,
x: 166
}
},
certificate_no: {
value: "授权编号",
style: {
fontSize: "10",
fontWeight: "normal",
color: "#f0e9b0",
y: 335,
x: 130
}
},
time: {
value: "授权日期",
style: {
fontSize: "10",
fontWeight: "normal",
color: "#f0e9b0",
y: 355,
x: 130
}
},
company_name: {
value: "公司名称",
style: {
fontSize: "14",
fontWeight: "normal",
color: "#f0e9b0",
y: 440,
x: 160
}
},
empower_template:
"https://lejiangkeji.oss-cn-beijing.aliyuncs.com/uploads/sqmb/4.png"
},
{
wx_nickname: {
value: "被授予人",
style: {
fontSize: "14",
fontWeight: "normal",
color: "#333",
y: 225,
x: 166
}
},
agent_name: {
value: "授予等级",
style: {
fontSize: "14",
fontWeight: "normal",
color: "#333",
y: 251,
x: 166
}
},
certificate_no: {
value: "授权编号",
style: {
fontSize: "10",
fontWeight: "normal",
color: "#333",
y: 303,
x: 155
}
},
time: {
value: "授权日期",
style: {
fontSize: "10",
fontWeight: "normal",
color: "#333",
y: 320,
x: 155
}
},
company_name: {
value: "公司名称",
style: {
fontSize: "14",
fontWeight: "normal",
color: "#333",
y: 460,
x: 155
}
},
empower_template:
"https://lejiangkeji.oss-cn-beijing.aliyuncs.com/uploads/sqmb/5.png"
},
{
wx_nickname: {
value: "被授予人",
style: {
fontSize: "14",
fontWeight: "normal",
color: "#333",
y: 225,
x: 155
}
},
agent_name: {
value: "授予等级",
style: {
fontSize: "14",
fontWeight: "normal",
color: "#333",
y: 251,
x: 155
}
},
certificate_no: {
value: "授权编号",
style: {
fontSize: "10",
fontWeight: "normal",
color: "#333",
y: 303,
x: 155
}
},
time: {
value: "授权日期",
style: {
fontSize: "10",
fontWeight: "normal",
color: "#333",
y: 320,
x: 155
}
},
company_name: {
value: "公司名称",
style: {
fontSize: "14",
fontWeight: "normal",
color: "#333",
y: 460,
x: 155
}
},
empower_template: ""
}
];
new Vue({
el: "#root",
data() {
return {
template: "0",
fontWeight: ["lighter", "normal", "bold"],
form: {},
activeEle: ""
};
},
mounted() {
this.form = templates[this.template];
},
methods: {
onChange(index) {
if (index === "custom") {
this.form = templates[5];
} else {
this.form = templates[Number(index)];
}
document.querySelector(".cert-wrap").style.background =
index === "custom" && !this.form.empower_template ? "skyblue" : "";
},
onActivated(ele) {
this.activeEle = ele.target.dataset.key;
},
onDragging(ele) {
this.$nextTick().then(() => {
this.form[this.activeEle].style.x = ele.left;
this.form[this.activeEle].style.y = ele.top;
});
},
onSubmit() {
// 提交保存自定义内容,this.form
console.log(this.form);
}
}
});
</script>
</html>
/单页面无法npm装包,又引不进来,所以扒源码搬了进来
var VueDragResize = `
<div class="vdr"
:style="style"
:class="active || isActive ? 'active' : 'inactive'"
@mousedown.stop.prevent="bodyDown($event)"
@touchstart.stop.prevent="bodyDown($event)">
<slot></slot>
<div
v-for="stick in sticks"
class="vdr-stick"
:class="['vdr-stick-' + stick, isResizable ? '' : 'not-resizable']"
@mousedown.stop.prevent="stickDown(stick, $event)"
@touchstart.stop.prevent="stickDown(stick, $event)"
:style="vdrStick(stick)">
</div>
</div>
`
const stickSize = 8;
const styleMapping = {
y: {
t: 'top',
m: 'marginTop',
b: 'bottom',
},
x: {
l: 'left',
m: 'marginLeft',
r: 'right',
}
};
Vue.component('vue-drag-resize', {
template: VueDragResize,
props: {
parentScaleX: {
type: Number, default: 1,
},
parentScaleY: {
type: Number, default: 1,
},
isActive: {
type: Boolean, default: false
},
preventActiveBehavior: {
type: Boolean, default: false
},
isDraggable: {
type: Boolean, default: true
},
isResizable: {
type: Boolean, default: false
},
aspectRatio: {
type: Boolean, default: false
},
parentLimitation: {
type: Boolean, default: true
},
parentW: {
type: Number,
default: 0,
validator: function (val) {
return val >= 0
}
},
parentH: {
type: Number,
default: 0,
validator: function (val) {
return val >= 0
}
},
w: {
type: Number,
default: 100,
validator: function (val) {
return val > 0
}
},
h: {
type: Number,
default: 100,
validator: function (val) {
return val > 0
}
},
minw: {
type: Number,
default: 50,
validator: function (val) {
return val > 0
}
},
minh: {
type: Number,
default: 50,
validator: function (val) {
return val > 0
}
},
x: {
type: Number,
default: 0,
validator: function (val) {
return typeof val === 'number'
}
},
y: {
type: Number,
default: 0,
validator: function (val) {
return typeof val === 'number'
}
},
z: {
type: [String, Number],
default: 'auto',
validator: function (val) {
let valid = (typeof val === 'string') ? val === 'auto' : val >= 0;
return valid
}
},
dragHandle: {
type: String,
default: null
},
dragCancel: {
type: String,
default: null
},
sticks: {
type: Array,
default: function () {
return ['tl', 'tm', 'tr', 'mr', 'br', 'bm', 'bl', 'ml']
}
},
axis: {
type: String,
default: 'both',
validator: function (val) {
return ['x', 'y', 'both', 'none'].indexOf(val) !== -1
}
}
},
data: function () {
return {
active: this.isActive,
rawWidth: this.w,
rawHeight: this.h,
rawLeft: this.x,
rawTop: this.y,
rawRight: null,
rawBottom: null,
zIndex: this.z,
aspectFactor: this.w / this.h,
parentWidth: null,
parentHeight: null,
left: this.x,
top: this.y,
right: null,
bottom: null,
minWidth: this.minw,
minHeight: this.minh
}
},
created: function () {
this.stickDrag = false;
this.bodyDrag = false;
this.stickAxis = null;
this.stickStartPos = {mouseX: 0, mouseY: 0, x: 0, y: 0, w: 0, h: 0};
this.limits = {
minLeft: null,
maxLeft: null,
minRight: null,
maxRight: null,
minTop: null,
maxTop: null,
minBottom: null,
maxBottom: null
};
this.currentStick = [];
},
mounted: function () {
this.parentElement = this.$el.parentNode;
this.parentWidth = this.parentW ? this.parentW : this.parentElement.clientWidth;
this.parentHeight = this.parentH ? this.parentH : this.parentElement.clientHeight;
this.rawRight = this.parentWidth - this.rawWidth - this.rawLeft;
this.rawBottom = this.parentHeight - this.rawHeight - this.rawTop;
document.documentElement.addEventListener('mousemove', this.move);
document.documentElement.addEventListener('mouseup', this.up);
document.documentElement.addEventListener('mouseleave', this.up);
document.documentElement.addEventListener('mousedown', this.deselect);
document.documentElement.addEventListener('touchmove', this.move, true);
document.documentElement.addEventListener('touchend touchcancel', this.up, true);
document.documentElement.addEventListener('touchstart', this.up, true);
if (this.dragHandle) {
let dragHandles = Array.prototype.slice.call(this.$el.querySelectorAll(this.dragHandle));
for (let i in dragHandles) {
dragHandles[i].setAttribute('data-drag-handle', this._uid);
}
}
if (this.dragCancel) {
let cancelHandles = Array.prototype.slice.call(this.$el.querySelectorAll(this.dragCancel));
for (let i in cancelHandles) {
cancelHandles[i].setAttribute('data-drag-cancel', this._uid);
}
}
},
beforeDestroy: function () {
document.documentElement.removeEventListener('mousemove', this.move);
document.documentElement.removeEventListener('mouseup', this.up);
document.documentElement.removeEventListener('mouseleave', this.up);
document.documentElement.removeEventListener('mousedown', this.deselect);
document.documentElement.removeEventListener('touchmove', this.move, true);
document.documentElement.removeEventListener('touchend touchcancel', this.up, true);
document.documentElement.removeEventListener('touchstart', this.up, true);
},
methods: {
deselect() {
if (this.preventActiveBehavior) {
return
}
this.active = false
},
move(ev) {
if (!this.stickDrag && !this.bodyDrag) {
return
}
ev.stopPropagation();
if (this.stickDrag) {
this.stickMove(ev);
}
if (this.bodyDrag) {
this.bodyMove(ev)
}
},
up(ev) {
if (this.stickDrag) {
this.stickUp(ev);
}
if (this.bodyDrag) {
this.bodyUp(ev)
}
},
bodyDown: function (ev) {
let target = ev.target || ev.srcElement;
if (!this.preventActiveBehavior) {
this.active = true;
}
if (ev.button && ev.button !== 0) {
return
}
this.$emit('clicked', ev);
if (!this.isDraggable || !this.active) {
return
}
if (this.dragHandle && target.getAttribute('data-drag-handle') !== this._uid.toString()) {
return
}
if (this.dragCancel && target.getAttribute('data-drag-cancel') === this._uid.toString()) {
return
}
this.bodyDrag = true;
this.stickStartPos.mouseX = ev.pageX || ev.touches[0].pageX;
this.stickStartPos.mouseY = ev.pageY || ev.touches[0].pageY;
this.stickStartPos.left = this.left;
this.stickStartPos.right = this.right;
this.stickStartPos.top = this.top;
this.stickStartPos.bottom = this.bottom;
if (this.parentLimitation) {
this.limits = this.calcDragLimitation();
}
},
calcDragLimitation() {
const parentWidth = this.parentWidth;
const parentHeight = this.parentHeight;
return {
minLeft: 0,
maxLeft: parentWidth - this.width,
minRight: 0,
maxRight: parentWidth - this.width,
minTop: 0,
maxTop: parentHeight - this.height,
minBottom: 0,
maxBottom: parentHeight - this.height
}
},
bodyMove(ev) {
const stickStartPos = this.stickStartPos;
let delta = {
x: (this.axis !== 'y' && this.axis !== 'none' ? stickStartPos.mouseX - (ev.pageX || ev.touches[0].pageX) : 0) / this.parentScaleX,
y: (this.axis !== 'x' && this.axis !== 'none' ? stickStartPos.mouseY - (ev.pageY || ev.touches[0].pageY) : 0) / this.parentScaleY
};
this.rawTop = stickStartPos.top - delta.y;
this.rawBottom = stickStartPos.bottom + delta.y;
this.rawLeft = stickStartPos.left - delta.x;
this.rawRight = stickStartPos.right + delta.x;
this.$emit('dragging', this.rect);
},
bodyUp() {
this.bodyDrag = false;
this.$emit('dragging', this.rect);
this.$emit('dragstop', this.rect);
this.stickStartPos = {mouseX: 0, mouseY: 0, x: 0, y: 0, w: 0, h: 0};
this.limits = {
minLeft: null,
maxLeft: null,
minRight: null,
maxRight: null,
minTop: null,
maxTop: null,
minBottom: null,
maxBottom: null
};
},
stickDown: function (stick, ev) {
if (!this.isResizable || !this.active) {
return
}
this.stickDrag = true;
this.stickStartPos.mouseX = ev.pageX || ev.touches[0].pageX;
this.stickStartPos.mouseY = ev.pageY || ev.touches[0].pageY;
this.stickStartPos.left = this.left;
this.stickStartPos.right = this.right;
this.stickStartPos.top = this.top;
this.stickStartPos.bottom = this.bottom;
this.currentStick = stick.split('');
this.stickAxis = null;
switch (this.currentStick[0]) {
case 'b':
this.stickAxis = 'y';
break;
case 't':
this.stickAxis = 'y';
break;
}
switch (this.currentStick[1]) {
case 'r':
this.stickAxis = this.stickAxis === 'y' ? 'xy' : 'x';
break;
case 'l':
this.stickAxis = this.stickAxis === 'y' ? 'xy' : 'x';
break;
}
this.limits = this.calcResizeLimitation();
},
calcResizeLimitation() {
let minw = this.minWidth;
let minh = this.minHeight;
const aspectFactor = this.aspectFactor;
const width = this.width;
const height = this.height;
const bottom = this.bottom;
const top = this.top;
const left = this.left;
const right = this.right;
const stickAxis = this.stickAxis;
const parentLim = this.parentLimitation ? 0 : null;
if (this.aspectRatio) {
if (minw / minh > aspectFactor) {
minh = minw / aspectFactor;
} else {
minw = aspectFactor * minh;
}
}
let limits = {
minLeft: parentLim,
maxLeft: left + (width - minw),
minRight: parentLim,
maxRight: right + (width - minw),
minTop: parentLim,
maxTop: top + (height - minh),
minBottom: parentLim,
maxBottom: bottom + (height - minh)
};
if (this.aspectRatio) {
const aspectLimits = {
minLeft: left - (Math.min(top, bottom) * aspectFactor) * 2,
maxLeft: left + ((((height - minh) / 2) * aspectFactor) * 2),
minRight: right - (Math.min(top, bottom) * aspectFactor) * 2,
maxRight: right + ((((height - minh) / 2) * aspectFactor) * 2),
minTop: top - (Math.min(left, right) / aspectFactor) * 2,
maxTop: top + ((((width - minw) / 2) / aspectFactor) * 2),
minBottom: bottom - (Math.min(left, right) / aspectFactor) * 2,
maxBottom: bottom + ((((width - minw) / 2) / aspectFactor) * 2)
};
if (stickAxis === 'x') {
limits = {
minLeft: Math.max(limits.minLeft, aspectLimits.minLeft),
maxLeft: Math.min(limits.maxLeft, aspectLimits.maxLeft),
minRight: Math.max(limits.minRight, aspectLimits.minRight),
maxRight: Math.min(limits.maxRight, aspectLimits.maxRight)
}
} else if (stickAxis === 'y') {
limits = {
minTop: Math.max(limits.minTop, aspectLimits.minTop),
maxTop: Math.min(limits.maxTop, aspectLimits.maxTop),
minBottom: Math.max(limits.minBottom, aspectLimits.minBottom),
maxBottom: Math.min(limits.maxBottom, aspectLimits.maxBottom)
}
}
}
return limits;
},
stickMove(ev) {
const stickStartPos = this.stickStartPos;
const delta = {
x: (stickStartPos.mouseX - (ev.pageX || ev.touches[0].pageX)) / this.parentScaleX,
y: (stickStartPos.mouseY - (ev.pageY || ev.touches[0].pageY)) / this.parentScaleY
};
switch (this.currentStick[0]) {
case 'b':
this.rawBottom = stickStartPos.bottom + delta.y;
break;
case 't':
this.rawTop = stickStartPos.top - delta.y;
break;
}
switch (this.currentStick[1]) {
case 'r':
this.rawRight = stickStartPos.right + delta.x;
break;
case 'l':
this.rawLeft = stickStartPos.left - delta.x;
break;
}
this.$emit('resizing', this.rect);
},
stickUp() {
this.stickDrag = false;
this.stickStartPos = {
mouseX: 0,
mouseY: 0,
x: 0,
y: 0,
w: 0,
h: 0
};
this.limits = {
minLeft: null,
maxLeft: null,
minRight: null,
maxRight: null,
minTop: null,
maxTop: null,
minBottom: null,
maxBottom: null
};
this.rawTop = this.top;
this.rawBottom = this.bottom;
this.rawLeft = this.left;
this.rawRight = this.right;
this.stickAxis = null;
this.$emit('resizing', this.rect);
this.$emit('resizestop', this.rect);
},
aspectRatioCorrection() {
if (!this.aspectRatio) {
return
}
const bottom = this.bottom;
const top = this.top;
const left = this.left;
const right = this.right;
const width = this.width;
const height = this.height;
const aspectFactor = this.aspectFactor;
const currentStick = this.currentStick;
if (width / height > aspectFactor) {
let newWidth = aspectFactor * height;
if (currentStick[1] === 'l') {
this.left = left + width - newWidth;
} else {
this.right = right + width - newWidth;
}
} else {
let newHeight = width / aspectFactor;
if (currentStick[0] === 't') {
this.top = top + height - newHeight;
} else {
this.bottom = bottom + height - newHeight;
}
}
},
},
computed: {
style() {
return {
top: this.top + 'px',
left: this.left + 'px',
width: this.width + 'px',
height: this.height + 'px',
zIndex: this.zIndex
}
},
vdrStick() {
return (stick) => {
const stickStyle = {
width: `${stickSize / this.parentScaleX}px`,
height: `${stickSize / this.parentScaleY}px`,
};
stickStyle[styleMapping.y[stick[0]]] = `${stickSize / this.parentScaleX / -2}px`;
stickStyle[styleMapping.x[stick[1]]] = `${stickSize / this.parentScaleX / -2}px`;
return stickStyle;
}
},
width() {
return this.parentWidth - this.left - this.right;
},
height() {
return this.parentHeight - this.top - this.bottom;
},
rect() {
return {
left: Math.round(this.left),
top: Math.round(this.top),
width: Math.round(this.width),
height: Math.round(this.height)
}
}
},
watch: {
rawLeft(newLeft) {
const limits = this.limits;
const stickAxis = this.stickAxis;
const aspectFactor = this.aspectFactor;
const aspectRatio = this.aspectRatio;
const left = this.left;
const bottom = this.bottom;
const top = this.top;
if (limits.minLeft !== null && newLeft < limits.minLeft) {
newLeft = limits.minLeft;
} else if (limits.maxLeft !== null && limits.maxLeft < newLeft) {
newLeft = limits.maxLeft;
}
if (aspectRatio && stickAxis === 'x') {
const delta = left - newLeft;
this.rawTop = top - (delta / aspectFactor) / 2;
this.rawBottom = bottom - (delta / aspectFactor) / 2;
}
this.left = newLeft;
},
rawRight(newRight) {
const limits = this.limits;
const stickAxis = this.stickAxis;
const aspectFactor = this.aspectFactor;
const aspectRatio = this.aspectRatio;
const right = this.right;
const bottom = this.bottom;
const top = this.top;
if (limits.minRight !== null && newRight < limits.minRight) {
newRight = limits.minRight;
} else if (limits.maxRight !== null && limits.maxRight < newRight) {
newRight = limits.maxRight;
}
if (aspectRatio && stickAxis === 'x') {
const delta = right - newRight;
this.rawTop = top - (delta / aspectFactor) / 2;
this.rawBottom = bottom - (delta / aspectFactor) / 2;
}
this.right = newRight;
},
rawTop(newTop) {
const limits = this.limits;
const stickAxis = this.stickAxis;
const aspectFactor = this.aspectFactor;
const aspectRatio = this.aspectRatio;
const right = this.right;
const left = this.left;
const top = this.top;
if (limits.minTop !== null && newTop < limits.minTop) {
newTop = limits.minTop;
} else if (limits.maxTop !== null && limits.maxTop < newTop) {
newTop = limits.maxTop;
}
if (aspectRatio && stickAxis === 'y') {
const delta = top - newTop;
this.rawLeft = left - (delta * aspectFactor) / 2;
this.rawRight = right - (delta * aspectFactor) / 2;
}
this.top = newTop;
},
rawBottom(newBottom) {
const limits = this.limits;
const stickAxis = this.stickAxis;
const aspectFactor = this.aspectFactor;
const aspectRatio = this.aspectRatio;
const right = this.right;
const left = this.left;
const bottom = this.bottom;
if (limits.minBottom !== null && newBottom < limits.minBottom) {
newBottom = limits.minBottom;
} else if (limits.maxBottom !== null && limits.maxBottom < newBottom) {
newBottom = limits.maxBottom;
}
if (aspectRatio && stickAxis === 'y') {
const delta = bottom - newBottom;
this.rawLeft = left - (delta * aspectFactor) / 2;
this.rawRight = right - (delta * aspectFactor) / 2;
}
this.bottom = newBottom;
},
width() {
this.aspectRatioCorrection();
},
height() {
this.aspectRatioCorrection();
},
active(isActive) {
if (isActive) {
this.$emit('activated');
} else {
this.$emit('deactivated');
}
},
isActive(val) {
this.active = val;
},
z(val) {
if (val >= 0 || val === 'auto') {
this.zIndex = val
}
},
aspectRatio(val) {
if (val) {
this.aspectFactor = this.width / this.height;
}
},
minw(val) {
if (val > 0 && val <= this.width) {
this.minWidth = val
}
},
minh(val) {
if (val > 0 && val <= this.height) {
this.minHeight = val
}
},
x() {
if (this.stickDrag || this.bodyDrag) {
return
}
if (this.parentLimitation) {
this.limits = this.calcDragLimitation();
}
let delta = this.x - this.left;
this.rawLeft = this.x;
this.rawRight = this.right - delta;
},
y() {
if (this.stickDrag || this.bodyDrag) {
return
}
if (this.parentLimitation) {
this.limits = this.calcDragLimitation();
}
let delta = this.y - this.top;
this.rawTop = this.y;
this.rawBottom = this.bottom - delta;
},
w() {
if (this.stickDrag || this.bodyDrag) {
return
}
this.currentStick = ['m', 'r'];
this.stickAxis = 'x';
if (this.parentLimitation) {
this.limits = this.calcResizeLimitation();
}
let delta = this.width - this.w;
this.rawRight = this.right + delta;
},
h() {
if (this.stickDrag || this.bodyDrag) {
return
}
this.currentStick = ['b', 'm'];
this.stickAxis = 'y';
if (this.parentLimitation) {
this.limits = this.calcResizeLimitation();
}
let delta = this.height - this.h;
this.rawBottom = this.bottom + delta;
},
parentW(val) {
this.right = val - this.width - this.left;
this.parentWidth = val;
},
parentH(val) {
this.bottom = val - this.height - this.top;
this.parentHeight = val;
}
}
})
vue中使用参考文档https://www.npmjs.com/package/vue-draggable-resizable