|
|
<template>
|
|
|
<div
|
|
|
id="wrapper"
|
|
|
class="wrapper vue-ruler-wrapper"
|
|
|
:style="{
|
|
|
width: width + thick + 'px',
|
|
|
height: height + thick + 'px'
|
|
|
}"
|
|
|
>
|
|
|
<i
|
|
|
:class="{
|
|
|
'iconfont-bigscreen': true,
|
|
|
'icon-eye': isShowReferLine,
|
|
|
'icon-eye-close': !isShowReferLine
|
|
|
}"
|
|
|
@click="handleCornerClick"
|
|
|
/>
|
|
|
<SketchRule
|
|
|
:key="scale"
|
|
|
:lang="lang"
|
|
|
:thick="thick"
|
|
|
:scale="scale"
|
|
|
:width="width"
|
|
|
:height="height"
|
|
|
:start-x="startX"
|
|
|
:start-y="startY"
|
|
|
:shadow="shadow"
|
|
|
:hor-line-arr="[...lines.h, ...presetLines.h]"
|
|
|
:ver-line-arr="[...lines.v, ...presetLines.v]"
|
|
|
:palette="Palette"
|
|
|
:is-show-refer-line="isShowReferLine"
|
|
|
:corner-active="cornerActive"
|
|
|
@handleLine="handleLine"
|
|
|
@onCornerClick="handleCornerClick"
|
|
|
/>
|
|
|
<div
|
|
|
id="screens"
|
|
|
ref="screensRef"
|
|
|
:style="{
|
|
|
width: innerWidth + 'px',
|
|
|
height: innerHeight + 'px'
|
|
|
}"
|
|
|
@scroll="throttleScroll"
|
|
|
>
|
|
|
<div
|
|
|
id="screen-container"
|
|
|
ref="containerRef"
|
|
|
class="screen-container grid-bg"
|
|
|
:style="containerRefStyle"
|
|
|
>
|
|
|
<div
|
|
|
id="canvas"
|
|
|
:style="canvasStyle"
|
|
|
>
|
|
|
<slot />
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
<script>
|
|
|
import SketchRule from 'vue-sketch-ruler'
|
|
|
import { mapState, mapMutations } from 'vuex'
|
|
|
import throttle from 'lodash/throttle'
|
|
|
export default {
|
|
|
components: {
|
|
|
SketchRule
|
|
|
},
|
|
|
props: {
|
|
|
width: {
|
|
|
type: Number,
|
|
|
default: 500
|
|
|
},
|
|
|
height: {
|
|
|
type: Number,
|
|
|
default: 400
|
|
|
},
|
|
|
pageWidth: {
|
|
|
type: Number,
|
|
|
default: 1920
|
|
|
},
|
|
|
pageHeight: {
|
|
|
type: Number,
|
|
|
default: 1080
|
|
|
}
|
|
|
},
|
|
|
data () {
|
|
|
return {
|
|
|
canvasLeft: 0, // 存储画布到视口的left距离
|
|
|
canvasTop: 0, // 存储画布到视口的top距离
|
|
|
isDrag: false, // 小地图白块是否拖拽
|
|
|
startX: 0,
|
|
|
startY: 0,
|
|
|
lines: {
|
|
|
h: [],
|
|
|
v: []
|
|
|
},
|
|
|
thick: 20,
|
|
|
lang: 'zh-CN', // 中英文
|
|
|
isShowRuler: true, // 显示标尺
|
|
|
isShowReferLine: true, // 显示参考线
|
|
|
cornerActive: true, // 左上角激活状态
|
|
|
Palette: {
|
|
|
bgColor: 'rgba(225,225,225, 0)',
|
|
|
longfgColor: '#BABBBC',
|
|
|
shortfgColor: '#C8CDD0',
|
|
|
fontColor: '#7D8694',
|
|
|
shadowColor: 'transparent',
|
|
|
lineColor: '#0089d0',
|
|
|
borderColor: '#transparent',
|
|
|
cornerActiveColor: 'rgb(235, 86, 72, 0.6)'
|
|
|
},
|
|
|
|
|
|
containerRefStyle: {
|
|
|
width: this.width + 'px',
|
|
|
height: this.height + 'px'
|
|
|
},
|
|
|
innerHeight: 0,
|
|
|
innerWidth: 0
|
|
|
}
|
|
|
},
|
|
|
watch: {
|
|
|
// 缩放改变的时候,改变startX,startY
|
|
|
scale (scale) {
|
|
|
// 防抖调用方法
|
|
|
this.throttleScroll()
|
|
|
},
|
|
|
pageWidth (pageWidth) {
|
|
|
if (this.fitZoom === this.zoom) {
|
|
|
this.initZoom()
|
|
|
}
|
|
|
},
|
|
|
pageHeight (pageHeight) {
|
|
|
if (this.fitZoom === this.zoom) {
|
|
|
this.initZoom()
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
computed: {
|
|
|
...mapState('bigScreen', {
|
|
|
scale: state => state.zoom / 100,
|
|
|
fitZoom: state => state.fitZoom,
|
|
|
zoom: state => state.zoom
|
|
|
}),
|
|
|
presetLines () {
|
|
|
const presetLine = this.$store.state.bigScreen.presetLine
|
|
|
// { type: 'h', site: y || 0 },
|
|
|
const v = presetLine?.filter(p => p.type === 'h')?.map(p => p.site)
|
|
|
const h = presetLine?.filter(p => p.type === 'v')?.map(p => p.site)
|
|
|
return {
|
|
|
h,
|
|
|
v
|
|
|
}
|
|
|
},
|
|
|
shadow () {
|
|
|
return {
|
|
|
x: 0,
|
|
|
y: 0,
|
|
|
width: this.width,
|
|
|
height: this.height
|
|
|
}
|
|
|
},
|
|
|
canvasStyle () {
|
|
|
return {
|
|
|
width: this.width + 'px',
|
|
|
height: this.height + 'px',
|
|
|
transform: `scale(${this.scale})`,
|
|
|
transformOrigin: '0 0 0'
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
mounted () {
|
|
|
// 初始化canvasLeft canvasTop
|
|
|
const canvasRect = document.querySelector('#canvas').getBoundingClientRect()
|
|
|
this.canvasLeft = canvasRect.left
|
|
|
this.canvasTop = canvasRect.top
|
|
|
// 监听屏幕改变
|
|
|
this.listenSize()
|
|
|
this.initRuleHeight()
|
|
|
this.throttleScroll()
|
|
|
this.throttleDrag()
|
|
|
},
|
|
|
methods: {
|
|
|
...mapMutations('bigScreen', [
|
|
|
'changeZoom',
|
|
|
'changeFitZoom'
|
|
|
]),
|
|
|
throttleDrag () {
|
|
|
throttle(() => {
|
|
|
this.dragSelection()
|
|
|
this.viewMapDrag()
|
|
|
}, 100)()
|
|
|
},
|
|
|
// 绑定滑块拖拽效果
|
|
|
dragSelection () {
|
|
|
const that = this
|
|
|
const draggableElement = document.getElementById('selectionWin')
|
|
|
const dragContainer = document.getElementById('selectWin')
|
|
|
const screenElement = document.getElementById('screens')
|
|
|
const screenContainer = document.getElementById('screen-container')
|
|
|
const maxContainer = document.querySelector('.bs-page-design-wrap')
|
|
|
// 鼠标按下的位置
|
|
|
let posX, posY
|
|
|
// 白色拖拽块相对于父盒子初始位置
|
|
|
let initialX, initialY
|
|
|
// 滚动条初始位置
|
|
|
let scrollTop, scrollLeft
|
|
|
draggableElement.addEventListener('mousedown', function (event) {
|
|
|
that.isDrag = true
|
|
|
posX = event.clientX
|
|
|
posY = event.clientY
|
|
|
initialX = draggableElement.getBoundingClientRect().left - dragContainer.getBoundingClientRect().left
|
|
|
initialY = draggableElement.getBoundingClientRect().top - dragContainer.getBoundingClientRect().top
|
|
|
scrollLeft = screenElement.scrollLeft
|
|
|
scrollTop = screenElement.scrollTop
|
|
|
maxContainer.addEventListener('mousemove', function (event) {
|
|
|
// 在鼠标移动过程中判断出鼠标左键未点击,则停止拖拽
|
|
|
if (event.buttons !== 1) {
|
|
|
that.isDrag = false
|
|
|
}
|
|
|
if (that.isDrag) {
|
|
|
event.preventDefault()
|
|
|
// 鼠标移动距离
|
|
|
let moveX = event.clientX - posX
|
|
|
let moveY = event.clientY - posY
|
|
|
// 避免白色拖拽移出边框
|
|
|
if (moveX < -initialX) {
|
|
|
moveX = -initialX
|
|
|
} else if (moveX > dragContainer.getBoundingClientRect().width - draggableElement.getBoundingClientRect().width - initialX) {
|
|
|
moveX = dragContainer.getBoundingClientRect().width - draggableElement.getBoundingClientRect().width - initialX
|
|
|
}
|
|
|
if (moveY < -initialY) {
|
|
|
moveY = -initialY
|
|
|
} else if (moveY > dragContainer.getBoundingClientRect().height - draggableElement.getBoundingClientRect().height - initialY) {
|
|
|
moveY = dragContainer.getBoundingClientRect().height - draggableElement.getBoundingClientRect().height - initialY
|
|
|
}
|
|
|
const newX = moveX + initialX
|
|
|
const newY = moveY + initialY
|
|
|
// 移动拖拽白块
|
|
|
draggableElement.style.left = newX + 'px'
|
|
|
draggableElement.style.top = newY + 'px'
|
|
|
// 移动比例
|
|
|
const percentageX = moveX / (dragContainer.getBoundingClientRect().width - draggableElement.getBoundingClientRect().width)
|
|
|
const percentageY = moveY / (dragContainer.getBoundingClientRect().height - draggableElement.getBoundingClientRect().height)
|
|
|
// 进度条需要滚动的距离
|
|
|
const scrollTopLength = percentageY * (screenContainer.getBoundingClientRect().height - screenElement.getBoundingClientRect().height)
|
|
|
const scrollLeftLength = percentageX * (screenContainer.getBoundingClientRect().width - screenElement.getBoundingClientRect().width)
|
|
|
screenElement.scrollLeft = scrollLeft + scrollLeftLength
|
|
|
screenElement.scrollTop = scrollTop + scrollTopLength
|
|
|
}
|
|
|
})
|
|
|
})
|
|
|
maxContainer.addEventListener('mouseup', function (event) {
|
|
|
that.isDrag = false
|
|
|
})
|
|
|
draggableElement.addEventListener('mouseup', function () {
|
|
|
that.isDrag = false
|
|
|
})
|
|
|
// 禁止H5自带的拖拽事件
|
|
|
draggableElement.ondragstart = function (ev) {
|
|
|
ev.preventDefault()
|
|
|
}
|
|
|
draggableElement.ondragend = function (ev) {
|
|
|
ev.preventDefault()
|
|
|
}
|
|
|
screenElement.ondragstart = function (ev) {
|
|
|
ev.preventDefault()
|
|
|
}
|
|
|
screenElement.ondragend = function (ev) {
|
|
|
ev.preventDefault()
|
|
|
}
|
|
|
},
|
|
|
// 小地图拖拽
|
|
|
viewMapDrag () {
|
|
|
const mapElement = document.getElementById('minimap')
|
|
|
const mapDragElement = document.getElementById('mapHeader')
|
|
|
const pageElement = document.querySelector('.bs-page-design-wrap')
|
|
|
let mapDrag = false
|
|
|
let curX, curY
|
|
|
let curBottom, curRight
|
|
|
mapDragElement.addEventListener('mousedown', function (event) {
|
|
|
mapDrag = true
|
|
|
curX = event.clientX
|
|
|
curY = event.clientY
|
|
|
curBottom = window.getComputedStyle(mapElement).bottom
|
|
|
curRight = window.getComputedStyle(mapElement).right
|
|
|
pageElement.addEventListener('mousemove', function (event) {
|
|
|
if (mapDrag) {
|
|
|
event.preventDefault()
|
|
|
const dragX = event.clientX - curX
|
|
|
const dragY = event.clientY - curY
|
|
|
mapElement.style.bottom = parseInt(curBottom) - dragY + 'px'
|
|
|
mapElement.style.right = parseInt(curRight) - dragX + 'px'
|
|
|
}
|
|
|
})
|
|
|
})
|
|
|
pageElement.addEventListener('mouseup', function () {
|
|
|
mapDrag = false
|
|
|
})
|
|
|
mapDragElement.addEventListener('mouseup', function () {
|
|
|
mapDrag = false
|
|
|
})
|
|
|
},
|
|
|
listenSize () {
|
|
|
window.onresize = throttle(() => {
|
|
|
this.initRuleHeight()
|
|
|
}, 100)
|
|
|
},
|
|
|
initRuleHeight () {
|
|
|
setTimeout(() => {
|
|
|
const screensRect = document
|
|
|
.querySelector('.grid-wrap-box')
|
|
|
?.getBoundingClientRect()
|
|
|
if (!screensRect) {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// 30是grid-wrap-box的底部工具栏高度
|
|
|
this.innerHeight = screensRect.height
|
|
|
this.innerWidth = screensRect.width
|
|
|
this.diffX = this.width - screensRect.width
|
|
|
this.diffY = this.height - screensRect.height
|
|
|
|
|
|
this.containerRefStyle = {
|
|
|
width: this.diffX > 0 ? ((this.width + this.diffX + this.thick + 30) + 'px') : (this.width + 'px'),
|
|
|
height: this.diffY > 0 ? ((this.height + this.diffY + this.thick + 30) + 'px') : (this.height + 'px')
|
|
|
}
|
|
|
if (this.fitZoom === this.zoom) {
|
|
|
this.initZoom()
|
|
|
}
|
|
|
})
|
|
|
},
|
|
|
handleLine (lines) {
|
|
|
this.lines = lines
|
|
|
},
|
|
|
handleCornerClick () {
|
|
|
this.isShowReferLine = !this.isShowReferLine
|
|
|
this.cornerActive = !this.cornerActive
|
|
|
},
|
|
|
throttleScroll () {
|
|
|
throttle(() => {
|
|
|
this.handleScroll()
|
|
|
}, 100)()
|
|
|
},
|
|
|
handleScroll () {
|
|
|
const screensRect = document
|
|
|
.querySelector('#screens')
|
|
|
.getBoundingClientRect()
|
|
|
const canvasRect = document
|
|
|
.querySelector('#canvas')
|
|
|
.getBoundingClientRect()
|
|
|
// const container = document.querySelector('#selectWin').getBoundingClientRect()
|
|
|
const screenContainer = document.querySelector('#screen-container').getBoundingClientRect()
|
|
|
const draggableElement = document.getElementById('selectionWin')
|
|
|
// 标尺开始的刻度
|
|
|
const startX = (screensRect.left + this.thick - canvasRect.left) / this.scale
|
|
|
const startY = (screensRect.top + this.thick - canvasRect.top) / this.scale
|
|
|
|
|
|
this.startX = startX >> 0
|
|
|
this.startY = startY >> 0
|
|
|
|
|
|
this.$emit('changeStart', {
|
|
|
x: this.startX * this.scale + 50 - this.thick,
|
|
|
y: this.startY * this.scale + 50 - this.thick
|
|
|
})
|
|
|
// 拖动进度条移动小地图
|
|
|
if (!this.isDrag) {
|
|
|
const leftDrag = canvasRect.left - this.canvasLeft
|
|
|
const topDrag = canvasRect.top - this.canvasTop
|
|
|
// 小方块需要移动的距离
|
|
|
const leftLength = leftDrag / (screenContainer.width - screensRect.width - 9) * (150 - 30)
|
|
|
const topLength = topDrag / (screenContainer.height - screensRect.height - 9) * (150 - 30)
|
|
|
draggableElement.style.left = -leftLength + 'px'
|
|
|
draggableElement.style.top = -topLength + 'px'
|
|
|
}
|
|
|
},
|
|
|
// 保证画布能完整展示大屏
|
|
|
initZoom () {
|
|
|
// 横向比例
|
|
|
const xRadio = this.innerWidth / (this.pageWidth + 120)
|
|
|
// 纵向比例
|
|
|
const yRadio = this.innerHeight / (this.pageHeight + 120)
|
|
|
// 取最小的适应比例
|
|
|
const scale = Math.floor(Math.min(xRadio * 100, yRadio * 100))
|
|
|
if (scale < 100) {
|
|
|
this.changeZoom(scale)
|
|
|
this.changeFitZoom(scale)
|
|
|
} else {
|
|
|
this.changeZoom(100)
|
|
|
this.changeFitZoom(100)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
|
<style lang="scss" scoped>
|
|
|
@import '../../BigScreenDesign/fonts/iconfont.css';
|
|
|
.wrapper {
|
|
|
box-sizing: border-box;
|
|
|
position: absolute;
|
|
|
|
|
|
.iconfont-bigscreen {
|
|
|
position: absolute;
|
|
|
left: 0;
|
|
|
top: 0;
|
|
|
font-size: 16px;
|
|
|
color: #fff;
|
|
|
z-index: 999;
|
|
|
cursor: pointer;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#screens {
|
|
|
position: absolute;
|
|
|
overflow: scroll;
|
|
|
|
|
|
// 滚动条美化,始终在最下方和最右方
|
|
|
&::-webkit-scrollbar {
|
|
|
width: 10px;
|
|
|
height: 10px;
|
|
|
}
|
|
|
&::-webkit-scrollbar-thumb {
|
|
|
border-radius: 10px;
|
|
|
background-color: var(--bs-el-background-2) !important;
|
|
|
}
|
|
|
&::-webkit-scrollbar-track {
|
|
|
border-radius: 10px;
|
|
|
background-color: transparent !important;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.screen-container {
|
|
|
position: absolute;
|
|
|
overflow:hidden;
|
|
|
width: 6000px;
|
|
|
height: 6000px;
|
|
|
}
|
|
|
|
|
|
.scale-value {
|
|
|
position: absolute;
|
|
|
left: 0;
|
|
|
bottom: 100%;
|
|
|
}
|
|
|
|
|
|
.button {
|
|
|
position: absolute;
|
|
|
left: 100px;
|
|
|
bottom: 100%;
|
|
|
}
|
|
|
|
|
|
.button-ch {
|
|
|
position: absolute;
|
|
|
left: 200px;
|
|
|
bottom: 100%;
|
|
|
}
|
|
|
.button-en {
|
|
|
position: absolute;
|
|
|
left: 230px;
|
|
|
bottom: 100%;
|
|
|
}
|
|
|
|
|
|
#canvas {
|
|
|
position: absolute;
|
|
|
top: 50px;
|
|
|
left: 50px;
|
|
|
}
|
|
|
::v-deep .line {
|
|
|
border-left: 1px dashed #0089d0 !important;
|
|
|
border-top: 1px dashed #0089d0 !important;
|
|
|
}
|
|
|
::v-deep .action {
|
|
|
.value {
|
|
|
background: var(--bs-el-color-primary);
|
|
|
padding: 4px;
|
|
|
color: #fff;
|
|
|
}
|
|
|
|
|
|
.del {
|
|
|
color: var(--bs-el-color-primary);
|
|
|
}
|
|
|
}
|
|
|
::v-deep .ruler, ::v-deep .corner {
|
|
|
background: var(--bs-background-1);
|
|
|
}
|
|
|
::v-deep .corner {
|
|
|
z-index: 999;
|
|
|
background: var(--bs-background-1) !important;
|
|
|
}
|
|
|
|
|
|
::v-deep .mb-ruler {
|
|
|
z-index: 998
|
|
|
}
|
|
|
.grid-bg {
|
|
|
background-color: #2a2e33 !important;
|
|
|
background-image: url(./images/canvas-bg.png);
|
|
|
background-repeat: repeat;
|
|
|
word-spacing: 10px;
|
|
|
}
|
|
|
|
|
|
::v-deep .lines {
|
|
|
.line {
|
|
|
.action {
|
|
|
.del {
|
|
|
color: var(--bs-el-text);
|
|
|
}
|
|
|
|
|
|
.value {
|
|
|
color: var(--bs-el-text);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
</style>
|