Last active
December 20, 2018 02:29
-
-
Save syrxw/290bbe89893473b225fc848e31de2db2 to your computer and use it in GitHub Desktop.
缩放插件
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<div class="container"> | |
<div class="wrap"> | |
<div class="d1" @mousedown="eleDown" :style="style"> | |
</div> | |
</div> | |
<!--drr--> | |
<div class="drr" :style="style" :class="{active: active}" ref="drr"> | |
<div v-if="resizable"> | |
<div | |
v-for="name in handle.handles" | |
class="handle" | |
:key="name" | |
:class="'handle-' + name" | |
:style="{ display: active ? 'block' : 'none', cursor: cursorStyle[name]}" | |
@mousedown.stop.prevent.self="handleDown(name, $event)" | |
></div> | |
</div> | |
</div> | |
</div> | |
</template> | |
<script> | |
export default { | |
name: 'home', | |
created(){ | |
document.documentElement.addEventListener('mousemove', (e) => { | |
this.flag = true | |
this.eleHandleMove(e) | |
}, true) | |
document.documentElement.addEventListener('mouseup', (e) => { | |
if (this.flag) { | |
this.eleHandleUp(e) | |
} else { | |
this.dragging = false | |
this.resizing = false | |
this.rotating = false | |
} | |
document.documentElement.removeEventListener('mousemove', this.eleHandleMove, true) | |
document.documentElement.removeEventListener('mousedown', this.eleHandleMove, true) | |
}, true) | |
}, | |
computed:{ | |
style() { //组件样式 | |
return { | |
top: this.top + 'px', | |
left: this.left + 'px', | |
width: this.width + 'px', | |
height: this.height + 'px', | |
transform: 'rotate(' + this.rotate + 'deg)', | |
zIndex:10, | |
} | |
}, | |
/** | |
* 根据旋转方向来设置操作条 | |
* @returns {default.props.cursors|{default, type}|({} & default.props.cursors) | ({} & {default, type})} | |
*/ | |
cursorStyle() { | |
const cursors = this.handle.cursors | |
const names = ['tl', 'tm', 'tr', 'mr', 'br', 'bm', 'bl', 'ml'] | |
const ARR_LENGTH = 8 | |
const STEP = 45 | |
const rotate = this.rotate | |
const startIndex = rotate ? Math.round(rotate / STEP) : 0 | |
let newCursors = cursors | |
if (startIndex > 1) { | |
newCursors = Object.assign({}, cursors) | |
names.forEach((v, i) => { | |
newCursors[v] = cursors[names[(startIndex + i) % ARR_LENGTH]] | |
}) | |
} | |
return newCursors | |
} | |
}, | |
data(){ | |
return { | |
active:false, | |
width:200, | |
height:200, | |
top:0, | |
left:0, | |
rotate:0, | |
draggable:true, | |
dragging:false, | |
resizable:true, | |
resizing: false, | |
rotatable:true, | |
rotating: false, | |
container:{ | |
x:'', | |
y:'', | |
w:'', | |
h:'' | |
}, | |
ele:{ | |
x:'', | |
y:'', | |
w:'', | |
h:'' | |
}, | |
current:{ | |
mouseX:'', | |
mouseY:'', | |
centerX:'', | |
centerY:'', | |
x:'', | |
y:'', | |
w:'', | |
h:'' | |
}, | |
handle:{ | |
x:'', | |
y:'', | |
w:'', | |
h:'', | |
name:'', | |
handles:['tl', 'tm', 'tr', 'mr', 'br', 'bm', 'bl', 'ml', 'rot'], | |
cursors:{ | |
tl: 'nwse-resize', | |
tm: 'ns-resize', | |
tr: 'nesw-resize', | |
mr: 'ew-resize', | |
br: 'nwse-resize', | |
bm: 'ns-resize', | |
bl: 'nesw-resize', | |
ml: 'ew-resize' | |
} | |
}, | |
fixed:{ | |
xName:'', | |
yName:'', | |
x:'', | |
y:'' | |
} | |
} | |
}, | |
methods:{ | |
/** | |
* 元素按下事件处理 | |
*/ | |
eleDown(e){ | |
this.active = true | |
if (this.draggable) { | |
this.eleDragDown(e) | |
} | |
}, | |
/** | |
* 元素和操作条移动 | |
*/ | |
eleHandleMove(e){ | |
if (this.dragging) { | |
this.eleDrag(e) | |
} | |
if(this.resizing){ | |
this.handleResize(e) | |
} | |
if(this.rotating){ | |
this.handleRotate(e) | |
} | |
}, | |
/** | |
* 元素按下 type:拖动 | |
*/ | |
eleDragDown(e){ | |
this.dragging = true | |
let {x: mouseX, y: mouseY} = this.getMouseCoordinate(e) | |
// 鼠标当前位置 | |
this.current.mouseX = mouseX | |
this.current.mouseY = mouseY | |
this.recordElePosition(e) | |
this.recordHandlePosition(e) | |
// 获取父级画布信息 | |
// this.getContainerInfo() | |
//记录点击初始位置 | |
this.current.x = this.ele.x | |
this.current.y = this.ele.y | |
}, | |
/** | |
* 元素拖动 | |
*/ | |
eleDrag(e){ | |
let {x: mouseX, y: mouseY} = this.getMouseCoordinate(e) | |
let diffX = mouseX - this.current.mouseX | |
let diffY = mouseY - this.current.mouseY | |
// 每一次先复原初始位置 | |
this.ele.x = this.current.x | |
this.ele.y = this.current.y | |
this.ele.x += diffX | |
this.ele.y += diffY | |
this.left = Math.round(this.ele.x) | |
this.top = Math.round(this.ele.y) | |
}, | |
/** | |
* 元素或操作条停止操作 | |
*/ | |
eleHandleUp(e){ | |
let {x: mouseX, y: mouseY} = this.getMouseCoordinate(e) | |
this.current.mouseX = mouseX | |
this.current.mouseY = mouseY | |
this.dragging = false | |
this.resizing = false | |
this.rotating = false | |
}, | |
/** | |
* 操作条按下 | |
*/ | |
handleDown(handleName,e){ | |
this.handleName = handleName | |
if (e.stopPropagation) e.stopPropagation() | |
if (e.preventDefault) e.preventDefault() | |
this.recordHandlePosition() | |
if (handleName === 'rot') { | |
this.handleRotateDown(e) | |
}else{ | |
this.handleResizeDown(handleName, e) | |
} | |
}, | |
/** | |
* 操作条缩放按下 | |
*/ | |
handleResizeDown(handleName,e){ | |
let {x: mouseX, y: mouseY} = this.getMouseCoordinate(e) | |
// 鼠标当前位置 | |
this.current.mouseX = mouseX | |
this.current.mouseY = mouseY | |
let fixedName = { | |
x: handleName[1] === 'l' ? 'right' : 'left', | |
y: handleName[0] === 't' ? 'bottom' : 'top' | |
} | |
let rect = { | |
top: this.top, | |
right: this.left + this.width, | |
bottom: this.top + this.height, | |
left: this.left | |
} | |
// 计算固定点坐标 | |
let fixedCoordinate = this.rotatedPoint(rect, this.rotate, fixedName) | |
// 计算固定点名称 | |
this.fixed.xName = fixedName.x | |
this.fixed.yName = fixedName.y | |
// 固定点坐标赋值 | |
this.fixed.x = fixedCoordinate.x | |
this.fixed.y = fixedCoordinate.y | |
this.current.x = this.handle.x | |
this.current.y = this.handle.y | |
this.current.w = this.handle.w | |
this.current.h = this.handle.h | |
this.resizing = true | |
}, | |
/** | |
* 操作条缩放 | |
*/ | |
handleResize(e){ | |
if(this.resizing){ | |
let {x: mouseX, y: mouseY} = this.getMouseCoordinate(e) | |
let rotate = this.rotate | |
let width = mouseX - this.current.mouseX | |
let height = mouseY - this.current.mouseY | |
let c = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2)) | |
let angle = this.getAngle(width, height) | |
let rad = Math.PI / 180 * (angle - rotate) | |
let diffY = Math.round(Math.sin(rad) * c) | |
let diffX = Math.round(Math.cos(rad) * c) | |
this.handle.x = this.current.x | |
this.handle.y = this.current.y | |
this.handle.w = this.current.w | |
this.handle.h = this.current.h | |
// 缩放计算 | |
let [handleY, handleX] = this.handleName | |
if (handleX !== 'm') { | |
this.handle.w += diffX * (handleX === 'r' ? 1 : -1) | |
} | |
if (handleY !== 'm') { | |
this.handle.h += diffY * (handleY === 'b' ? 1 : -1) | |
} | |
// 边界处理 | |
if (this.handle.w < 50) { | |
this.handle.w = 50 | |
} | |
if (this.handle.h < 50) { | |
this.handle.h = 50 | |
} | |
if (this.handle.w < 0) { | |
this.handle.w = -this.handle.w | |
this.fixedXExchange = true | |
} | |
if (this.handle.h < 0) { | |
this.handle.h = -this.handle.h | |
this.fixedYExchange = true | |
} | |
this.handleFixed() | |
this.left = Math.round(this.handle.x) | |
this.top = Math.round(this.handle.y) | |
this.width = Math.round(this.handle.w) | |
this.height = Math.round(this.handle.h) | |
} | |
}, | |
/** | |
* 操作条旋转按下 | |
*/ | |
handleRotateDown(e) { | |
let {x, y, w, h} = this.handle | |
this.current.centerX = window.pageXOffset + x + w / 2 | |
this.current.centerY = window.pageYOffset + y + h / 2 | |
this.rotating = true | |
}, | |
/** | |
* 操作条旋转 | |
*/ | |
handleRotate(e){ | |
console.log(e) | |
if(this.rotating){ | |
let {x: mouseX, y: mouseY} = this.getMouseCoordinate(e) | |
let x = mouseX - this.current.centerX | |
let y = mouseY - this.current.centerY | |
this.rotate = (this.getAngle(x, y) + 90) % 360 | |
console.log(this.rotate) | |
} | |
}, | |
/** | |
* 操作条固定点位置 | |
*/ | |
handleFixed() { | |
// 获取元素区域 | |
let rect = { | |
top: this.handle.y, | |
right: this.handle.x + this.handle.w, | |
bottom: this.handle.y + this.handle.h, | |
left: this.handle.x | |
} | |
let fixedXName, fixedYName | |
if (this.fixedXExchange) { | |
fixedXName = this.fixed.xName === 'right' ? 'left' : 'right' | |
} else { | |
fixedXName = this.fixed.xName | |
} | |
if (this.fixedYExchange) { | |
fixedYName = this.fixed.yName === 'top' ? 'bottom' : 'top' | |
} else { | |
fixedYName = this.fixed.yName | |
} | |
let fixed = { | |
x: fixedXName, | |
y: fixedYName | |
} | |
let {x: fixedX, y: fixedY} = this.rotatedPoint(rect, this.rotate, fixed) | |
let dX = Math.round(this.fixed.x - fixedX) | |
let dY = Math.round(this.fixed.y - fixedY) | |
this.handle.x += dX | |
this.handle.y += dY | |
}, | |
/** | |
* 获取元素基本信息 | |
*/ | |
recordElePosition(e){ | |
const info = e.target | |
this.ele.x = parseInt(info.style.left) | |
this.ele.y = parseInt(info.style.top) | |
this.ele.w = info.offsetWidth ||info.clientWidth | |
this.ele.h = info.offsetHeight || info.clientHeight | |
}, | |
/** | |
* 获取操作条基本信息 | |
*/ | |
recordHandlePosition(){ | |
const info = this.$refs.drr | |
this.handle.x = parseInt(info.style.left) | |
this.handle.y = parseInt(info.style.top) | |
this.handle.w = info.offsetWidth ||info.clientWidth | |
this.handle.h = info.offsetHeight || info.clientHeight | |
}, | |
/** | |
* 获取鼠标位置 | |
* @param e | |
* @returns {*} | |
*/ | |
getMouseCoordinate(e) { | |
return { | |
x: e.pageX || e.clientX + document.documentElement.scrollLeft, | |
y: e.pageY || e.clientY + document.documentElement.scrollTop | |
} | |
}, | |
/** | |
* 旋转点计算 | |
* @param rect | |
* @param rotate | |
* @param point | |
* @returns {{x: *, y: *}} | |
*/ | |
rotatedPoint(rect, rotate, point) { | |
let {top, right, bottom, left} = rect | |
let rad = Math.PI / 180 * rotate | |
let cos = Math.cos(rad) | |
let sin = Math.sin(rad) | |
let originX = (right - left + 1) / 2 + left | |
let originY = (bottom - top + 1) / 2 + top | |
let x = rect[point.x] | |
let y = rect[point.y] | |
x -= originX | |
y -= originY | |
return { | |
x: x * cos - y * sin + originX, | |
y: x * sin + y * cos + originY | |
} | |
}, | |
/** | |
* 旋转角度 | |
* @param x | |
* @param y | |
* @returns {number} | |
*/ | |
getAngle(x, y) { | |
let theta = Math.atan2(y, x) // range (-PI, PI] | |
theta = Math.round(180 / Math.PI * theta) // rads to degs, range (-180, 180] | |
if (theta < 0) theta = 360 + theta // range [0, 360) | |
return theta | |
}, | |
} | |
} | |
</script> | |
<style> | |
.container{ | |
position: fixed; | |
top:0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
} | |
.wrap{ | |
position: relative; | |
width:800px; | |
height:600px; | |
background-color: #5B6B73; | |
} | |
.d1{ | |
position: absolute; | |
top: 116px; | |
left: 128px; | |
width: 160px; | |
height: 160px; | |
z-index: 7; | |
background-color: #108ee9; | |
} | |
.d2{ | |
position: absolute; | |
top: 216px; | |
left: 328px; | |
width: 200px; | |
height: 200px; | |
z-index: 8; | |
background-color: #108ee9; | |
} | |
/*drr*/ | |
.drr { | |
position: absolute; | |
box-sizing: border-box; | |
display: none; | |
pointer-events: none; | |
} | |
.drr.dragging { | |
user-select: none; | |
} | |
.drr.active{ | |
display: block; | |
} | |
.drr.active:before { | |
content: ''; | |
position: absolute; | |
top: -1px; | |
right: -1px; | |
bottom: -1px; | |
left: -1px; | |
border: 1px solid #f00; | |
z-index: -1; | |
} | |
.handle { | |
box-sizing: border-box; | |
display: none; | |
position: absolute; | |
width: 10px; | |
height: 10px; | |
font-size: 1px; | |
border-radius: 5px; | |
background: #eee; | |
border: 1px solid #1baee1; | |
z-index: 100; | |
pointer-events: auto; | |
} | |
.handle-tl { | |
top: -5px; | |
left: -5px; | |
} | |
.handle-tm { | |
top: -5px; | |
left: 50%; | |
margin-left: -5px; | |
} | |
.handle-tr { | |
top: -5px; | |
right: -5px; | |
} | |
.handle-ml { | |
top: 50%; | |
margin-top: -5px; | |
left: -5px; | |
} | |
.handle-mr { | |
top: 50%; | |
margin-top: -5px; | |
right: -5px; | |
} | |
.handle-bl { | |
bottom: -5px; | |
left: -5px; | |
} | |
.handle-bm { | |
bottom: -5px; | |
left: 50%; | |
margin-left: -5px; | |
} | |
.handle-br { | |
bottom: -5px; | |
right: -5px; | |
} | |
.handle-rot { | |
position: absolute; | |
top: 0; | |
left: 50%; | |
margin-top: -20px; | |
width: 10px; | |
height: 10px; | |
transform: translate(-50%, 0); | |
cursor: url(./img/rotate.svg) 20 20, move; | |
z-index: -1; | |
pointer-events: auto; | |
} | |
</style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment