123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499 |
- <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;
- }
- </style>
|