<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>