Last active
November 24, 2018 05:36
-
-
Save syrxw/3f14445d29f4df8e85f7d9b766fbcd8e to your computer and use it in GitHub Desktop.
vue+svg 实现缩放
This file contains hidden or 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> | |
| <svg width="800px" height="800px" version="1.1" | |
| xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800"> | |
| <g class="port_output" style="pointer-events: all;"> | |
| <rect fill="#ddd" stroke-width="1.5" | |
| :x="listData['001'].x" :y="listData['001'].y" | |
| :width="listData['001'].width" :height="listData['001'].height" | |
| :transform="listData['001'].transform" | |
| style="vector-effect: non-scaling-stroke" | |
| class="port" @click="elementClick($event,'001')" stroke-dasharray="none" stroke="#000"></rect> | |
| <rect fill="#ddd" stroke-width="1.5" | |
| :x="listData['002'].x" :y="listData['002'].y" | |
| :width="listData['002'].width" :height="listData['002'].height" | |
| :transform="listData['002'].transform" | |
| style="vector-effect: non-scaling-stroke" | |
| class="port" @click="elementClick($event,'002')" stroke-dasharray="none" stroke="#000"></rect> | |
| </g> | |
| <!--<path :d="item" stroke="red" stroke-width="2" fill="none" v-for="(item,index) in listData"--> | |
| <!--:key="index"></path>--> | |
| <g class="selected-group" :display="selectGroup.info.isShow" :transform="selectGroup.transform"> | |
| <path :d="selectGroup.path" stroke="blue" stroke-width="1" fill="none"></path> | |
| <image :xlink:href="require('../assets/rotate.png')" | |
| :x="selectGroup.rotate.x" :y="selectGroup.rotate.y" | |
| :width="selectGroup.rotate.width" :height="selectGroup.rotate.height" | |
| @mousedown="shapeRotate($event)"></image> | |
| <circle :cx="selectGroup.connect.cw.x" :cy="selectGroup.connect.cw.y" r="2.5" | |
| stroke="blue" fill="white"></circle> | |
| <circle :cx="selectGroup.connect.ce.x" :cy="selectGroup.connect.ce.y" r="2.5" | |
| stroke="blue" fill="white"></circle> | |
| <rect style="cursor:nw-resize" | |
| :x="selectGroup.resize.nw.x - 2.5" :y="selectGroup.resize.nw.y - 2.5" | |
| stroke="blue" fill="white" | |
| width="5" height="5" | |
| class="port" | |
| @mousedown="shapeScale($event,type='nw')" | |
| ></rect> | |
| <rect style="cursor:ne-resize" | |
| :x="selectGroup.resize.ne.x - 2.5" :y="selectGroup.resize.ne.y - 2.5" | |
| stroke="blue" fill="white" | |
| width="5" height="5" | |
| class="port" | |
| @mousedown="shapeScale($event,type='ne')" | |
| ></rect> | |
| <rect style="cursor:sw-resize" | |
| :x="selectGroup.resize.sw.x - 2.5" :y="selectGroup.resize.sw.y - 2.5" | |
| stroke="blue" fill="white" | |
| width="5" height="5" | |
| class="port" | |
| @mousedown="shapeScale($event,type='sw')" | |
| ></rect> | |
| <rect style="cursor:se-resize" | |
| :x="selectGroup.resize.se.x - 2.5" :y="selectGroup.resize.se.y - 2.5" | |
| stroke="blue" fill="white" | |
| width="5" height="5" | |
| class="port" | |
| @mousedown="shapeScale($event,type='se')" | |
| ></rect> | |
| <rect :x="selectGroup.center.x - 2.5" :y="selectGroup.center.y - 2.5" | |
| stroke="blue" fill="white" | |
| width="5" height="5" | |
| class="port" | |
| @mousedown="shapeScale" | |
| ></rect> | |
| </g> | |
| </svg> | |
| </template> | |
| <script> | |
| import * as d3 from 'd3'; | |
| export default { | |
| name: 'home', | |
| data() { | |
| return { | |
| clickPoint: { | |
| x: '', | |
| y: '' | |
| }, | |
| listData: { | |
| '001': { | |
| width: 100, | |
| height: 100, | |
| x: 200, | |
| y: 100, | |
| transform: '', | |
| translateX: '', | |
| translateY: '', | |
| rotate: 0, | |
| }, | |
| '002': { | |
| width: 150, | |
| height: 150, | |
| x: 450, | |
| y: 100, | |
| transform: '', | |
| translateX: '', | |
| translateY: '', | |
| rotate: 0 | |
| } | |
| }, | |
| selectGroup: { | |
| info: { | |
| isShow: 'none', | |
| }, | |
| resize: { | |
| nw: { | |
| name: '', | |
| x: '', | |
| y: '' | |
| }, | |
| sw: { | |
| name: '', | |
| x: '', | |
| y: '' | |
| }, | |
| ne: { | |
| name: '', | |
| x: '', | |
| y: '' | |
| }, | |
| se: { | |
| name: '', | |
| x: '', | |
| y: '' | |
| }, | |
| }, | |
| path: "", | |
| rotate: { | |
| x: '0', | |
| y: '0', | |
| width: '20', | |
| height: '20', | |
| angle: 0 | |
| }, | |
| connect: { | |
| cw: { | |
| name: '', | |
| x: '0', | |
| y: '0' | |
| }, | |
| ce: { | |
| name: '', | |
| x: '0', | |
| y: '0' | |
| }, | |
| }, | |
| center: { | |
| name: '', | |
| x: '', | |
| y: '' | |
| }, | |
| transform: '' | |
| }, | |
| selectedItem: { | |
| target: null, | |
| id: "" | |
| }, | |
| scaleValue: { | |
| x: 1, | |
| y: 1 | |
| }, | |
| } | |
| }, | |
| computed: {}, | |
| mounted() { | |
| }, | |
| methods: { | |
| initSelectGroup() { | |
| //初始化数据 | |
| const target = this.listData[this.selectedItem.id] | |
| target.translateX = target.x + target.width | |
| target.translateY = target.y + target.height | |
| this.scaleValue.x = 1 | |
| this.scaleValue.y = 1 | |
| this.selectGroup.rotate.angle = target.rotate | |
| }, | |
| drawSelectPoint(target) { // 画点 | |
| //左上角 | |
| this.selectGroup.resize.nw = { | |
| name: 'nw', | |
| x: target.x, | |
| y: target.y | |
| } | |
| //左下角 | |
| this.selectGroup.resize.sw = { | |
| name: 'sw', | |
| x: target.x, | |
| y: target.y + target.height | |
| } | |
| //右上角 | |
| this.selectGroup.resize.ne = { | |
| name: 'ne', | |
| x: target.x + target.width, | |
| y: target.y | |
| } | |
| //右下角 | |
| this.selectGroup.resize.se = { | |
| name: 'se', | |
| x: target.x + target.width, | |
| y: target.y + target.height | |
| } | |
| //左中角 | |
| this.selectGroup.connect.cw = { | |
| name: 'cw', | |
| x: target.x, | |
| y: target.y + target.height / 2 | |
| } | |
| //右中角 | |
| this.selectGroup.connect.ce = { | |
| name: 'ce', | |
| x: target.x + target.width, | |
| y: target.y + (target.height / 2) | |
| } | |
| //中心点 | |
| this.selectGroup.center = { | |
| name: 'center', | |
| x: target.x + (target.width / 2), | |
| y: target.y + (target.height / 2) | |
| } | |
| }, | |
| drawSelectLine() { // 画线 | |
| const m1 = `M${this.selectGroup.resize.nw.x}, ${this.selectGroup.resize.nw.y}` | |
| const m2 = `L${this.selectGroup.resize.ne.x}, ${this.selectGroup.resize.ne.y}` | |
| const m3 = `L${this.selectGroup.resize.se.x}, ${this.selectGroup.resize.se.y}` | |
| const m4 = `L${this.selectGroup.resize.sw.x}, ${this.selectGroup.resize.sw.y}` | |
| const m5 = `L${this.selectGroup.resize.nw.x}, ${this.selectGroup.resize.nw.y}` | |
| this.selectGroup.path = `${m1}${m2}${m3}${m4}${m5}` | |
| }, | |
| drawSelectRotate(target) {// 画旋转图标 | |
| this.selectGroup.rotate.x = this.selectGroup.center.x - this.selectGroup.rotate.width / 2 | |
| this.selectGroup.rotate.y = this.selectGroup.center.y - (target.height / 2) - this.selectGroup.rotate.height * 1.5 | |
| }, | |
| drawSelectAngle() { | |
| const target = this.listData[this.selectedItem.id] | |
| if (target.rotate !== 0) { | |
| this.selectGroup.transform = `rotate(${target.rotate},${this.selectGroup.center.x},${this.selectGroup.center.y})` | |
| }else{ | |
| this.selectGroup.transform = '' | |
| } | |
| }, | |
| drawSelect(target) { | |
| this.drawSelectPoint(target) | |
| this.drawSelectLine() | |
| this.drawSelectRotate(target) | |
| this.drawSelectAngle() | |
| }, | |
| elementClick(e, id) { | |
| this.selectedItem.id = id; | |
| this.selectedItem.target = e.target | |
| this.initSelectGroup() | |
| const p_data = this.calculatePointer('nw') | |
| this.drawSelect(p_data) | |
| this.selectGroup.info.isShow = 'inline' | |
| }, | |
| calculatePointer(type) { | |
| let return_data = {} | |
| const target = this.listData[this.selectedItem.id] | |
| return_data.width = target.width * Math.abs(this.scaleValue.x) | |
| return_data.height = target.height * Math.abs(this.scaleValue.y) | |
| //计算重绘坐标点 逻辑相对复杂 | |
| if (type === 'nw') { | |
| return_data.x = target.translateX - return_data.width | |
| return_data.y = target.translateY - return_data.height | |
| if (this.scaleValue.x < 0) { | |
| return_data.x = target.x + target.width | |
| } | |
| if (this.scaleValue.y < 0) { | |
| return_data.y = target.y + target.height | |
| } | |
| } | |
| if (type === 'ne') { | |
| return_data.x = target.translateX | |
| return_data.y = target.translateY - return_data.height | |
| if (this.scaleValue.x < 0) { | |
| return_data.x = target.x - return_data.width | |
| } | |
| if (this.scaleValue.y < 0) { | |
| return_data.y = target.y + target.height | |
| } | |
| } | |
| if (type === 'sw') { | |
| return_data.x = target.translateX - return_data.width | |
| return_data.y = target.translateY | |
| if (this.scaleValue.x < 0) { | |
| return_data.x = target.x + target.width | |
| } | |
| if (this.scaleValue.y < 0) { | |
| return_data.y = target.y - return_data.height | |
| } | |
| } | |
| if (type === 'se') { | |
| return_data.x = target.x | |
| return_data.y = target.y | |
| if (this.scaleValue.x < 0) { | |
| return_data.x = target.x - return_data.width | |
| } | |
| if (this.scaleValue.y < 0) { | |
| return_data.y = target.y - return_data.height | |
| } | |
| } | |
| return return_data | |
| }, | |
| shapeScale(e, type) { | |
| const target = this.listData[this.selectedItem.id] | |
| //初始计算位移距离,让scale保持原地缩放 | |
| if (type === 'nw') { | |
| const tx = target.x + target.width | |
| const ty = target.y + target.height | |
| this.scaleFunc(e, tx, ty, 'nw') | |
| } | |
| if (type === 'ne') { | |
| const tx = target.x | |
| const ty = target.y + target.height | |
| this.scaleFunc(e, tx, ty, 'ne') | |
| } | |
| if (type === 'sw') { | |
| const tx = target.x + target.width | |
| const ty = target.y | |
| this.scaleFunc(e, tx, ty, 'sw') | |
| } | |
| if (type === 'se') { | |
| const tx = target.x | |
| const ty = target.y | |
| this.scaleFunc(e, tx, ty, 'se') | |
| } | |
| }, | |
| scaleFunc(e, tx, ty, type) { | |
| const target = this.listData[this.selectedItem.id] | |
| target.translateX = tx | |
| target.translateY = ty | |
| const pos_x = e.clientX - target.translateX | |
| const pos_y = e.clientY - target.translateY | |
| let isMoving = false | |
| document.onmousemove = (e) => { | |
| const move_x = e.clientX - target.translateX | |
| const move_y = e.clientY - target.translateY | |
| this.scaleValue = { | |
| x: move_x / pos_x, | |
| y: move_y / pos_y | |
| } | |
| const p_data = this.calculatePointer(type) | |
| this.drawSelect(p_data) | |
| if (!isNaN(this.scaleValue.x) && !isNaN(this.scaleValue.y)) { | |
| if (target.rotate !== 0) { | |
| target.transform = `rotate(${target.rotate},${this.selectGroup.center.x},${this.selectGroup.center.y}) translate(${target.translateX},${target.translateY}) scale(${this.scaleValue.x},${this.scaleValue.y}) translate(-${target.translateX},-${target.translateY})` | |
| } else { | |
| target.transform = `translate(${target.translateX},${target.translateY}) scale(${this.scaleValue.x},${this.scaleValue.y}) translate(-${target.translateX},-${target.translateY})` | |
| } | |
| } | |
| isMoving = true | |
| }; | |
| document.onmouseup = () => { | |
| document.onmousemove = null; | |
| document.onmouseup = null; | |
| if (isMoving) { | |
| const p_data = this.calculatePointer(type) | |
| const target = this.listData[this.selectedItem.id] | |
| target.x = p_data.x | |
| target.y = p_data.y | |
| target.width = p_data.width | |
| target.height = p_data.height | |
| if (target.rotate !== 0) { | |
| target.transform = `rotate(${target.rotate},${this.selectGroup.center.x},${this.selectGroup.center.y})` | |
| } else { | |
| target.transform = '' | |
| } | |
| } | |
| }; | |
| }, | |
| shapeRotate(e) { | |
| console.log(e) | |
| this.initSelectGroup() | |
| const target = this.listData[this.selectedItem.id] | |
| const center = this.selectGroup.center | |
| const angle = Math.PI / 180 * this.selectGroup.rotate.angle | |
| // const x = (target.x - center.x) * Math.cos(angle) - (target.y - center.y) * Math.sin(angle) + center.x | |
| // const y = (target.x - center.x) * Math.sin(angle) + (target.y - center.y) * Math.cos(angle) + center.y | |
| const p_data = this.calculatePointer('nw') | |
| this.drawSelect(p_data) | |
| // 旋转开始 | |
| const event = window.event, | |
| prevAngle = Math.atan2(event.pageY - center.y, event.pageX - center.x) - angle; | |
| document.onmousemove = () => { | |
| // 旋转 | |
| const event = window.event, | |
| angle = Math.atan2(event.pageY - center.y, event.pageX - center.x); | |
| this.selectGroup.rotate.angle = Math.floor((angle - prevAngle) * 180 / Math.PI); | |
| const transform = `rotate(${this.selectGroup.rotate.angle},${this.selectGroup.center.x},${this.selectGroup.center.y})` | |
| target.rotate = this.selectGroup.rotate.angle | |
| target.transform = transform | |
| this.selectGroup.transform = transform | |
| } | |
| document.onmouseup = () => { | |
| // 旋转结束 | |
| document.onmousemove = null; | |
| document.onmouseup = null; | |
| } | |
| }, | |
| pointDown(e) { | |
| this.clickPoint.x = e.offsetX | |
| this.clickPoint.y = e.offsetY | |
| document.onmousemove = (e) => { | |
| this.drawLine(this.clickPoint.x, this.clickPoint.y, e.offsetX, e.offsetY, '002') | |
| }; | |
| document.onmouseup = () => { | |
| document.onmousemove = null; | |
| document.onmouseup = null; | |
| }; | |
| }, | |
| drawLine(x1, y1, x2, y2, id) { | |
| const path = d3.path() | |
| path.moveTo(x1, y1) | |
| path.bezierCurveTo(x2, y1, x1, y2, x2, y2); | |
| // path.lineTo(x2,y2) | |
| this.$set(this.listData, id, path.toString()) | |
| console.log(this.listData) | |
| }, | |
| } | |
| } | |
| </script> | |
| <style> | |
| :not(svg), :not(foreignObject) > svg { | |
| transform-origin: 0px 0px 0px; | |
| } | |
| </style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment