You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

514 lines
14 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<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: {
// 缩放改变的时候改变startXstartY
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>