组件化平台画布实现思路

282 阅读1分钟

1.画布实现思路

HTML结构

<template>
  <div id="wrapper" class="wrapper">
    <SketchRule
      :lang="lang"
      :thick="thick"
      :scale="scale"
      :width="width"
      :height="height"
      :start-x="startX"
      :start-y="startY"
      :palette="palette"
      :corner-active="true"
      :is-show-refer-line="isShowReferLine"
      @handleLine="handleLine"
      @onCornerClick="handleCornerClick"
    />
    <div
      id="screens"
      ref="screensRef"
      @wheel="handleWheel"
      @scroll="handleScroll"
    >
      <div id="screen-container" ref="containerRef" class="screen-container">
        <div id="canvas" ref="canvas" :style="canvasStyle" />
      </div>
    </div>
  </div>
</template>
<script>
import { getElements } from '../netWork/editor'
import { pageInfo, screenList, themeColorOption } from '../netWork/page/pageSetting/index'
import SketchRule from 'vue-sketch-ruler'
import bus from '../assets/js/eventBus'
// const rectWidth = 1900
// const rectHeight = 720
export default {
  name: 'Editor',
  components: {
    SketchRule
  },
  data() {
    return {
      timesx: 0.68,
      timesy: 0.6,
      maxScale: 4,
      minScale: 0.3,
      result: null,
      timer: null,
      nowTime: new Date(),
      x: 0,
      y: 0,
      scale: 1, // 658813476562495, //1,
      startX: 0,
      startY: 0,
      lines: {
      },
      palette: {
        bgColor: '#13141c',
        longfgColor: '#ffffff',
        shortfgColor: '#C8CDD0',
        fontColor: '#ffffff',
        shadowColor: '#13141c',
        lineColor: '#EB5648',
        borderColor: '#DADADC',
        cornerActiveColor: 'rgb(235, 86, 72, 0.6)'
      },
      initLeft: '',
      initTop: '',
      thick: 20,
      width: 1319,
      height: 830,
      lang: 'zh-CN', // 中英文
      isShowRuler: true, // 显示标尺
      isShowReferLine: true, // 显示参考线
      pageInfoObj: {},
      screenSelectData: [],
      colorData: [],
      colorDataPlay: [],
      screenName: '',
      screenId: '',
      screenwidth: '',
      screenheight: '',
      themeType: '',
      bgUrl: ''
    }
  },
  computed: {
    shadow() {
      return {
        x: 0,
        y: 0,
        width: this.screenwidth,
        height: this.screenheight
      }
    },
    canvasStyle() {
      return {
        width: this.screenwidth + 'px',
        height: this.screenheight + 'px',
        // transform: `scale(${this.scale})`,
        // width: 10000 + 'px',
        // height: 5000 + 'px',
        backgroundColor: this.pageInfoObj.bgColor + '!important',
        backgroundImage: `url(${this.bgUrl})`,
        opacity: this.pageInfoObj.bgOpacity / 100,
        backgroundSize: this.pageInfoObj.overviewType === 2 ? `100% 100%` : (this.pageInfoObj.overviewType === 3 ? null : 'contain'),
        backgroundRepeat: this.pageInfoObj.overviewType === 3 ? 'repeat' : 'no-repeat'
        // transform: `translate3d(${this.x}, ${this.y}, 0) scale(${(this.scale - 0.005)})`
      }
    }
  },
  created() {
    this.init(this.$store.state.currentPageId, 1)
    // 滚动居中
    this.$nextTick(() => {
      this.scrollInit() // 将鼠标滚动换为拖拽的方式
      this.initSize()
    })
  },
  mounted() {
  },
  methods: {
    scrollInit() {
      // 获取要绑定事件的元素
      const nav = document.getElementById('screens')
      var flag // 鼠标按下
      var downX // 鼠标点击的x下标
      var downY
      var scrollLeft // 当前元素滚动条的偏移量
      var scrollTop
      nav.addEventListener('mousedown', function(event) {
        // event.stopPropagation()
        flag = true
        downX = event.clientX // 获取到点击的x下标
        downY = event.clientY
        scrollLeft = this.scrollLeft // 获取当前元素滚动条的偏移量
        scrollTop = this.scrollTop
      })
      nav.addEventListener('mousemove', function(event) {
        // event.stopPropagation()
        if (flag) { // 判断是否是鼠标按下滚动元素区域
          var moveX = event.clientX // 获取移动的x轴
          var moveY = event.clientY
          var scrollX = moveX - downX // 当前移动的x轴下标减去刚点击下去的x轴下标得到鼠标滑动距离
          var scrollY = moveY - downY
          this.scrollLeft = scrollLeft - scrollX // 鼠标按下的滚动条偏移量减去当前鼠标的滑动距离
          this.scrollTop = scrollTop - scrollY
        }
      })
      // 鼠标抬起停止拖动
      nav.addEventListener('mouseup', function() {
        // event.stopPropagation()
        flag = false
      })
      // 鼠标离开元素停止拖动
      nav.addEventListener('mouseleave', function(event) {
        // event.stopPropagation()
        flag = false
      })
    },
    init(id, params) {
      const _this = this
      if (id && id !== -1) {
      // 获取页面的样式
        pageInfo(id).then(async res => {
          this.pageInfoObj = res.data
          this.pageInfoObj.useBgPic === 1 ? this.bgUrl = this.pageInfoObj.url : this.bgUrl = ''
          await screenList(this.pageInfoObj.id).then(res => {
            this.screenSelectData = res.data
            this.screenSelectData.forEach(ele => {
              if (ele.id === this.pageInfoObj.screenId) {
                this.screenName = ele.screenName
                this.screenId = ele.id
                this.screenwidth = ele.width
                this.screenheight = ele.height
                this.scale = 1
                this.$nextTick(() => {
                  if (_this.screenwidth >= 3000) {
                    _this.timesx = 0.262
                  } else if (_this.screenwidth >= 2000 && _this.screenwidth < 3000) {
                    _this.timesx = 0.85
                  } else {
                    _this.timesx = 0.5
                  }
                  if (_this.screenheight >= 3000) {
                    _this.timesy = 1.52
                  } else if (_this.screenheight >= 1800 && _this.screenheight < 2400) {
                    _this.timesy = 1.1
                  } else {
                    _this.timesy = 0.5
                  }
                  var box = document.getElementById('canvas')
                  this.result = {
                    width: getComputedStyle(box).width.slice(0, -2) * 1,
                    height: getComputedStyle(box).height.slice(0, -2) * 1
                  }
                  // 初始页面的滚动条偏移量
                  _this.$refs.screensRef.scrollLeft =
                    (_this.$refs.containerRef.getBoundingClientRect().width + _this.$refs.canvas.style.width.slice(0, -2) * 1 - 1319) / 2 // 1319 为screen的宽度
                  _this.$refs.screensRef.scrollTop =
                     (_this.$refs.canvas.style.height.slice(0, -2) * 1 - 898) / 2 // 898 为screen的高度加上21
                })
                if (params) {
                  _this.computedScale()
                }
              }
            })
          })
          themeColorOption({ type: this.pageInfoObj.themeType, id: '' }).then(res => {
            this.colorData = res.data
            this.colorData.forEach(ele => {
              if (ele.id === this.pageInfoObj.themeColorId) {
                this.colorDataPlay = ele.children
              }
            })
            this.themeType = this.colorData[0].themeType
          })
        })
      }
    },
    // 初始化计算缩放比展示全页面
    computedScale() {
      var dom = document.getElementById('screens')
      var height = getComputedStyle(dom).height.substr(0, getComputedStyle(dom).height.indexOf('p')) * 1
      var width = getComputedStyle(dom).width.substr(0, getComputedStyle(dom).width.indexOf('p')) * 1
      var dom2 = document.getElementById('canvas')
      if (width < this.screenwidth) {
        this.initScale = this.scale = (width / this.screenwidth).toFixed(2) * 1
      } else if (height < this.screenheight) {
        this.initScale = this.scale = (height / this.screenheight).toFixed(2) * 1
      } else {
        this.initScale = this.scale = 1
        dom2.style.transform = `translate3d(${this.x}px, ${this.y}px, 0) scale(${(this.scale)})`
        return
      }
      dom2.style.transform = `translate3d(${this.x}px, ${this.y}px, 0) scale(${(this.scale - 0.03)})`
      this.$store.commit('changeGlobalScale', this.scale)
      this.initLeft = dom2.style.left
      this.initTop = dom2.style.top
    },
    handleLine(lines) {
      this.lines = lines
    },
    handleCornerClick() {
      return
    },
    // 修改标尺刻度
    handleScroll() {
      const screensRect = document
        .querySelector('#screens')
        .getBoundingClientRect()
      const canvasRect = document
        .querySelector('#canvas')
        .getBoundingClientRect()

      // 标尺开始的刻度
      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.startX = startX
      // this.startY = startY
    },
    // 控制缩放值
    handleWheel(e) {
      var box = document.getElementById('canvas')
      if (e.ctrlKey || e.metaKey) {
        this.flag = true
        e.preventDefault()
        let ratio = 1.1
        // 缩小
        if (e.deltaY > 0) {
          ratio = 1 / 1.1
        }
        // 限制缩放倍数
        const _scale = this.scale * ratio
        if (_scale > this.maxScale) {
          ratio = this.maxScale / this.scale
          this.scale = this.maxScale
        } else if (_scale < this.minScale) {
          ratio = this.minScale / this.scale
          this.scale = this.minScale
        } else {
          this.scale = _scale
        }
        console.log(this.scale, box.getBoundingClientRect())
        const origin = {
          x: (ratio - 1) * this.result.width * this.timesx,
          y: (ratio - 1) * this.result.height * this.timesy
        }
        this.x -= (ratio - 1) * (e.clientX - this.x) - origin.x
        this.y -= (ratio - 1) * (e.clientY - this.y) - origin.y
        box.style.transform = 'translate3d(' + this.x + 'px, ' + this.y + 'px, 0) scale(' + this.scale + ')'
        this.$store.commit('changeGlobalScale', this.scale)
      }
      this.$nextTick(() => {
        this.handleScroll()
      })
    },
    initSize() {
      const wrapperRect = document
        .querySelector('#wrapper')
        .getBoundingClientRect()
      const borderWidth = 1
      this.width = wrapperRect.width - this.thick - borderWidth
      this.height = wrapperRect.height - this.thick - borderWidth
    }
  }
}
</script>
<style scoped lang="scss">
  @import "../assets/css/handle";
  @import '../assets/css/common';
  .wrapper {
    background-color: #f5f5f5;
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    border: 1px solid #DADADC; }

  #screens {
    position: absolute;
    width: 100%;
    height: 100%;
    overflow: auto;
    background-color: #7e7a7a;}

  .screen-container {
    position: absolute;
    width: 8000px;
    height: 3000px;
    background: url('../assets/image/canvasBgPicture.png') repeat;
  }

  .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: 20px;*/
    left: 50%;
    margin-left: 21px;
    /*transform-origin: 50% 0;*/
    transform-origin: center;
  }
  /*定义滚动条样式*/
  ::v-deep ::-webkit-scrollbar {
    width: 0!important;
    height: 100% !important;
    background-color: white !important;
  }
  /*定义滚动条轨道 内阴影+圆角*/
  ::v-deep ::-webkit-scrollbar-track {
    box-shadow: inset 0 0 0 rgba(240, 240, 240, .5);
    border-radius: 0.1rem;
    background-color:#909399;
  }
  /*定义滑块 内阴影+圆角*/
  ::v-deep ::-webkit-scrollbar-thumb {
    box-shadow: inset 0 0 0 rgba(240, 240, 240, .5);
    background-color: #282D38;
    /*border-radius: 15px;*/
  }
</style>

1.借用了标尺插件## vue-sketch-ruler

github地址: github.com/chuxiaoguo/…