vue可拖拽移动放大缩小的弹框组件

完成一次拖放的事件过程是: dragstart –> dragenter –> dragover –> drop –> dragend
下面是HTML5的拖拽事件
dragstart:开始拖元素触发,作用于拖拽元素
dragenter:元素拖进可drop元素(绑定drop事件的元素)时触发,作用于目标元素
dragover:当元素拖动到drop元素上时触发,作用于目标元素
drop:当元素放下到drop元素触发,作用于目标元素
dragleave :当元素离开drop元素时触发,作用于目标元素
drag:每次元素被拖动时会触发,作用于目标元素
dragend:放开拖动元素时触发,作用于目标元素

 js中获取鼠标滚动量/滚动了多少可以用onwheel事件来实现放大缩小。

html代码如下

<template>
    <div class="back_box" @click.stop="hide"
         v-if="isVisible" ref="back_box"
         :style="{background: isMaskVisible? 'rgba(0,0,0,0.5)' : 'none'}">
      <div
        class="drag_box"
        draggable="true"
        @dragstart="dragstart"
        @dragover="dragover"
        @dragend="dragend"
        @wheel="handleWeel"
        :style="`left:${elLeft}px;top:${elTop}px;width:${elWidth}px;height:${elHeight}px;`"
        @click.stop="show"
      >
        <div class="sotp" @click.stop="hide"><a-icon type="close" style="font-size: 20px;"/></div>
        <div
          class="content"
          :style="`left:${(0 * elWidth) / xWidth}px;top:${(0 * elHeight) / yHeight}px;width:${xWidth}px;height:${yHeight-50}px;-webkit-transform: scale(${meter_zoom} )`"
        >
          <div class="title" v-if="title">{{title}}</div>
          <slot name="content"></slot>
        </div>
      </div>
    </div>
</template>

  css代码主要用到positon:fixed让无素在窗口最上面

<style scoped lang="scss">
  .back_box {
    position: fixed;
    z-index: 9999;
    cursor: move;
  }

  .drag_box {
    position: fixed;
    overflow: hidden;
    background: #fff;
    border-radius: 10px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
    user-select: none; /* 不可选中,为了拖拽时不让文字高亮 */
    -webkit-user-drag: none;
  }

  .content {
    position: absolute;
    z-index: 88;
    width: 100%;
    padding: 0;
    overflow: hidden;
    font-size: 12px;
    transform-origin: 0 0; /* 用作缩放基点 */
    .title {
      height: 40px;
      padding: 10px;
      font-weight: 600;
      font-size: 14px;
      border-bottom: 1px solid #f5f5f5;
    }
  }

  .sotp {
    position: absolute;
    top: 10px;
    right: 20px;
    z-index: 999;
    width: 30px;
    height: 30px;
    line-height: 30px;
    text-align: center;
    background: #fff;
    border-radius: 50%;
    cursor: pointer;
  }
</style>

 1.父元素的宽-自适应值 ini父元素的高-自适应值

2.元素拖拽前距离浏览器的X轴位置

3.元素拖拽前距离浏览器的Y轴位置

4.获取计算偏移量

5.获取元素的左偏移量

6.获取元素的右偏移量

7.设置缩放比例

8.元素宽 元素高

9.子元素缩放比例

<script>
  export default {
    name: 'HelloWorld',
    props: {
      msg: String,
      xWidth: {
        type: Number,
        default: 400
      },
      yHeight: {
        type: Number,
        default: 400
      },
      xLeft: { // 左侧偏移
        type: Number,
        default: 20
      },
      yTop: { // top偏移
        type: Number,
        default: 20
      },
      title: {
        type: String,
        default: ''
      },
      visible: {
        type: Boolean,
        default: false
      },
      maskVisible: {
        type: Boolean,
        default: false
      }
    },
    data () {
      return {
        initWidth: 0, // 父元素的宽-自适应值
        initHeight: 0, // 父元素的高-自适应值
        startclientX: 0, // 元素拖拽前距离浏览器的X轴位置
        startclientY: 0, // 元素拖拽前距离浏览器的Y轴位置
        endlientX: 0, // 计算偏移量
        endlientY: 0,
        elLeft: 0, // 元素的左偏移量
        elTop: 0, // 元素的右偏移量
        zoom: 1, // 缩放比例
        elWidth: 0, // 元素宽
        elHeight: 0, // 元素高
        meter_zoom: 0, // 子元素缩放比例

        isVisible: false,
        isMaskVisible: false
      }
    },
    watch: {
      visible: {
        deep: true,
        handler (val) {
          if (val) {
            setTimeout(() => {
              this.initBodySize() // 页面初始化
            }, 50)
          }
          this.isVisible = val
        }
      },
      maskVisible: {
        deep: true,
        handler (val) {
          this.isMaskVisible = val
        }
      }
    },
    methods: {
      // 页面初始化
      initBodySize () {
        // this.initWidth = this.$refs.back_box.clientWidth // 拿到父元素宽
        // this.initHeight = this.initWidth * (1080 / 1920);
        this.initWidth = window.innerWidth
        this.initHeight = this.initWidth * ((1080 * 0.88) / (1920 - 1080 * 0.02)) // 根据宽计算高实现自适应
        this.elWidth = this.initWidth * (this.xWidth / (1920 / 2))
        this.elHeight = this.initHeight * (this.yHeight / (1080 / 2))
        this.meter_zoom = this.elWidth / this.xWidth // 计算子元素缩放比例
        this.elLeft = this.elLeft
        this.elTop = this.elTop
      },
      // 拖拽开始事件
      dragstart (e) {
        this.startclientX = e.layerX // 记录拖拽元素初始位置
        this.startclientY = e.layerY
      },
      dragover (e) {
        let x = e.layerX - this.startclientX // 计算偏移量
        let y = e.layerY - this.startclientY
        this.elLeft += x // 实现拖拽元素随偏移量移动
        this.elTop += y
      },
      // 拖拽完成事件
      dragend (e) {
        let x = e.layerX - this.startclientX // 计算偏移量
        let y = e.layerY - this.startclientY
        this.elLeft += x // 实现拖拽元素随偏移量移动
        this.elTop += y
      },
      // 滚轮放大缩小事件
      handleWeel (e) {
        if (e.wheelDelta < 0) {
          this.zoom -= 0.05
        } else {
          this.zoom += 0.05
        }
        if (this.zoom >= 3) {
          this.zoom = 3
          return
        }
        if (this.zoom <= 0.5) {
          this.zoom = 0.5
          return
        }
        this.elWidth = this.initWidth * (this.xWidth / (1920 / 2)) * this.zoom
        this.elHeight = this.initHeight * (this.yHeight / (1080 / 2)) * this.zoom
        this.meter_zoom = this.elWidth / this.xWidth
      },
      show () { // 显示
        this.isVisible = true
        this.initBodySize()
      },
      hide () { // 隐藏
        this.isVisible = false
        this.$emit('close')
      }
    },
    mounted () {
      // 监听键盘Esc按键事件
      this.$nextTick(() => {
        // 按下键盘
        document.addEventListener('keydown', (e) => {
          // Esc按键是27
          if (e.keyCode === 27) {
            // 在这里写你的操作逻辑
            this.hide()
          }
        })

        // 去除默认的预览图
        // 虽然setDragImage比较鸡肋,但是我们可以设置一张透明的图片就可以实现去除默认的预览图的效果了
        document.addEventListener('dragstart', function (ev) {
          let img = new Image()
          img.src = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' %3E%3Cpath /%3E%3C/svg%3E"
          ev.dataTransfer.setDragImage(img, 0, 0)
        })
      })
    }

  }
</script>

监听键盘Esc按键事件关闭弹框

  // 监听键盘Esc按键事件
      this.$nextTick(() => {
        // 按下键盘
        document.addEventListener('keydown', (e) => {
          // Esc按键是27
          if (e.keyCode === 27) {
            // 在这里写你的操作逻辑
            this.hide()
          }
        })

 需注意:去除默认的预览图

虽然setDragImage比较鸡肋,但是我们可以设置一张透明的图片就可以实现去除默认的预览图的效果了
      document.addEventListener('dragstart', function (ev) {
          let img = new Image()
          img.src = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' %3E%3Cpath /%3E%3C/svg%3E"
          ev.dataTransfer.setDragImage(img, 0, 0)
        })
      })

全部源代码

<template>
    <div class="back_box" @click.stop="hide"
         v-if="isVisible" ref="back_box"
         :style="{background: isMaskVisible? 'rgba(0,0,0,0.5)' : 'none'}">
      <div
        class="drag_box"
        draggable="true"
        @dragstart="dragstart"
        @dragover="dragover"
        @dragend="dragend"
        @wheel="handleWeel"
        :style="`left:${elLeft}px;top:${elTop}px;width:${elWidth}px;height:${elHeight}px;`"
        @click.stop="show"
      >
        <div class="sotp" @click.stop="hide"><a-icon type="close" style="font-size: 20px;"/></div>
        <div
          class="content"
          :style="`left:${(0 * elWidth) / xWidth}px;top:${(0 * elHeight) / yHeight}px;width:${xWidth}px;height:${yHeight-50}px;-webkit-transform: scale(${meter_zoom} )`"
        >
          <div class="title" v-if="title">{{title}}</div>
          <slot name="content"></slot>
        </div>
      </div>
    </div>
</template>

<script>
  export default {
    name: 'HelloWorld',
    props: {
      msg: String,
      xWidth: {
        type: Number,
        default: 400
      },
      yHeight: {
        type: Number,
        default: 400
      },
      xLeft: { // 左侧偏移
        type: Number,
        default: 20
      },
      yTop: { // top偏移
        type: Number,
        default: 20
      },
      title: {
        type: String,
        default: ''
      },
      visible: {
        type: Boolean,
        default: false
      },
      maskVisible: {
        type: Boolean,
        default: false
      }
    },
    data () {
      return {
        initWidth: 0, // 父元素的宽-自适应值
        initHeight: 0, // 父元素的高-自适应值
        startclientX: 0, // 元素拖拽前距离浏览器的X轴位置
        startclientY: 0, // 元素拖拽前距离浏览器的Y轴位置
        endlientX: 0, // 计算偏移量
        endlientY: 0,
        elLeft: 0, // 元素的左偏移量
        elTop: 0, // 元素的右偏移量
        zoom: 1, // 缩放比例
        elWidth: 0, // 元素宽
        elHeight: 0, // 元素高
        meter_zoom: 0, // 子元素缩放比例

        isVisible: false,
        isMaskVisible: false
      }
    },
    watch: {
      visible: {
        deep: true,
        handler (val) {
          if (val) {
            setTimeout(() => {
              this.initBodySize() // 页面初始化
            }, 50)
          }
          this.isVisible = val
        }
      },
      maskVisible: {
        deep: true,
        handler (val) {
          this.isMaskVisible = val
        }
      }
    },
    methods: {
      // 页面初始化
      initBodySize () {
        // this.initWidth = this.$refs.back_box.clientWidth // 拿到父元素宽
        // this.initHeight = this.initWidth * (1080 / 1920);
        this.initWidth = window.innerWidth
        this.initHeight = this.initWidth * ((1080 * 0.88) / (1920 - 1080 * 0.02)) // 根据宽计算高实现自适应
        this.elWidth = this.initWidth * (this.xWidth / (1920 / 2))
        this.elHeight = this.initHeight * (this.yHeight / (1080 / 2))
        this.meter_zoom = this.elWidth / this.xWidth // 计算子元素缩放比例
        this.elLeft = this.elLeft
        this.elTop = this.elTop
      },
      // 拖拽开始事件
      dragstart (e) {
        this.startclientX = e.layerX // 记录拖拽元素初始位置
        this.startclientY = e.layerY
      },
      dragover (e) {
        let x = e.layerX - this.startclientX // 计算偏移量
        let y = e.layerY - this.startclientY
        this.elLeft += x // 实现拖拽元素随偏移量移动
        this.elTop += y
      },
      // 拖拽完成事件
      dragend (e) {
        let x = e.layerX - this.startclientX // 计算偏移量
        let y = e.layerY - this.startclientY
        this.elLeft += x // 实现拖拽元素随偏移量移动
        this.elTop += y
      },
      // 滚轮放大缩小事件
      handleWeel (e) {
        if (e.wheelDelta < 0) {
          this.zoom -= 0.05
        } else {
          this.zoom += 0.05
        }
        if (this.zoom >= 3) {
          this.zoom = 3
          return
        }
        if (this.zoom <= 0.5) {
          this.zoom = 0.5
          return
        }
        this.elWidth = this.initWidth * (this.xWidth / (1920 / 2)) * this.zoom
        this.elHeight = this.initHeight * (this.yHeight / (1080 / 2)) * this.zoom
        this.meter_zoom = this.elWidth / this.xWidth
      },
      show () { // 显示
        this.isVisible = true
        this.initBodySize()
      },
      hide () { // 隐藏
        this.isVisible = false
        this.$emit('close')
      }
    },
    mounted () {
      // 监听键盘Esc按键事件
      this.$nextTick(() => {
        // 按下键盘
        document.addEventListener('keydown', (e) => {
          // Esc按键是27
          if (e.keyCode === 27) {
            // 在这里写你的操作逻辑
            this.hide()
          }
        })

        // 去除默认的预览图
        // 虽然setDragImage比较鸡肋,但是我们可以设置一张透明的图片就可以实现去除默认的预览图的效果了
        document.addEventListener('dragstart', function (ev) {
          let img = new Image()
          img.src = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' %3E%3Cpath /%3E%3C/svg%3E"
          ev.dataTransfer.setDragImage(img, 0, 0)
        })
      })
    }

  }
</script>

<style scoped lang="scss">
  .back_box {
    position: fixed;
    z-index: 9999;
    cursor: move;
  }

  .drag_box {
    position: fixed;
    overflow: hidden;
    background: #fff;
    border-radius: 10px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
    user-select: none; /* 不可选中,为了拖拽时不让文字高亮 */
    -webkit-user-drag: none;
  }

  .content {
    position: absolute;
    z-index: 88;
    width: 100%;
    padding: 0;
    overflow: hidden;
    font-size: 12px;
    transform-origin: 0 0; /* 用作缩放基点 */
    .title {
      height: 40px;
      padding: 10px;
      font-weight: 600;
      font-size: 14px;
      border-bottom: 1px solid #f5f5f5;
    }
  }

  .sotp {
    position: absolute;
    top: 10px;
    right: 20px;
    z-index: 999;
    width: 30px;
    height: 30px;
    line-height: 30px;
    text-align: center;
    background: #fff;
    border-radius: 50%;
    cursor: pointer;
  }
</style>

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dogface07

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值