拖拽缩放热区vue组件代码解决方案

可移动热区组件

 /* Movable/index.js */
export { default as MovableArea } from './Components/Area'
export { default as MovableView } from './Components/View'

可视化区域

/* Movable/Components/View.vue */
<script>
const MovableView = {
  props: {
    height  : { type: Number, required: true },
    width   : { type: Number, required: true },
    left    : { type: Number, required: true },
    top     : { type: Number, required: true },
    overflow: { type: Boolean },
    ratio   : { type: Boolean },
    minSize : { type: Number, default: 40 },
    maxSize : { type: Number },
    imgSrc  : { type: String },
    styles  : { type: Object, default: () => ({}) },
    resize  : { type: Boolean },
    onChange: { type: Function },
    visible: { type: Boolean, default: true }
  },
  methods: {
    handleMoveMouseDown(e) {
      this.parseMove(e, (val) => {
        let top = val.y
        let left = val.x

        if (!this.$props.overflow) {
          const parentWidth = parseFloat(this.$refs.viewDomRef.parentElement.style.width)
          const parentHeight = parseFloat(this.$refs.viewDomRef.parentElement.style.height)
          top = Math.max(0, top)
          top = Math.min(top, parentHeight - this.$props.height)

          left = Math.max(0, left)
          left = Math.min(left, parentWidth - this.$props.width)
        }
        this.$props.onChange && this.$props.onChange({
          width: this.$props.width,
          height: this.$props.height,
          top,
          left
        })
      })
    },
    handleResizeMouseDown(e) {
      e.stopPropagation()
      this.parseMove(e, (val) => {
        let width = val.x + 12
        let height = val.y + 12

        // 是否允许超出
        if (!this.$props.overflow) {
          const parentWidth = parseFloat(this.$refs.viewDomRef.parentElement.style.width)
          const parentHeight = parseFloat(this.$refs.viewDomRef.parentElement.style.height)
          width = Math.min(width, parentWidth - this.$props.left)
          height = Math.min(height, parentHeight - this.$props.top)
        }

        // 等比缩放
        if (this.$props.ratio) {
          height = width = Math.min(width, height)
        }

        // 最小缩放值

        width = Math.max(width, this.$props.minSize)
        height = Math.max(height, this.$props.minSize)

        // 最大缩放值
        if (this.$props.maxSize) {
          width = Math.min(width, this.$props.maxSize)
          height = Math.min(height, this.$props.maxSize)
        }

        this.$props.onChange && this.$props.onChange({
          width,
          height,
          top: this.$props.top,
          left: this.$props.left
        })
      })
    },
    parseMove(e, cb) {
      const x = e.clientX - e.currentTarget.offsetLeft
      const y = e.clientY - e.currentTarget.offsetTop
      this.moveEventcache = document.onmousemove
      document.onmousemove = (ee) => {
        cb && cb({ x: ee.clientX - x, y: ee.clientY - y })
      }
      document.onmouseup = () => {
        document.onmousemove = this.moveEventcache
      }
    }

  },

  render() {
    if (this.$props.visible === false) return null
    const styles = {
      position: 'absolute',
      width: this.$props.width + 'px',
      height: this.$props.height + 'px',
      top: this.$props.top + 'px',
      left: this.$props.left + 'px',
      cursor: 'grab',
      userSelect: 'none',
      backgroundImage: 'url("' + this.$props.imgSrc + '")',
      backgroundSize: 'contain',
      backgroundPosition: 'center',
      backgroundRepeat: 'no-repeat',
      ...(this.$props.styles)
    }
    const resizeStyle = {
      position: 'absolute',
      right: 0,
      bottom: 0,
      width: 12 + 'px',
      height: 12 + 'px',
      opacity: '0.9',
      cursor: 'nwse-resize',
      backgroundSize: '100%',
      userSelect: 'none',
      backgroundImage: 'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAVFBMVEUAAADd3d3b29vd3d3e3t7k5OT19fXd3d3n5+eZmZnHx8e4uLioqKjv7++urq7z8/Ps7OzX19e0tLSrq6vk5OTi4uLQ0NCwsLDp6enKysq/v7++vr57c1IlAAAABnRSTlMAf4D2qROyT4+oAAABJElEQVRYw6XV3U7CUBBFYVvQOadAEQF/3/89NTayEieZKXv2/bc6aS/6UNxY9Vbzg1nVW9FfreZbs5p/IqB5AponoHkCmiegeQKaJ6B5AponIHoCoicgegKC1wJ4LYDXAngtgNcCeC2A1wJ4LYAXA3g9MOKFAF4I4LUAXgvgtQBeDHjf5s8kkPh9n+4LDKknkPtnvAus8Yc/38gQyP3x5vfdsgCe3fy5Bxfk/h1PYJX/WF7kjCewxr/+87uvMDCmvk8EUn/up+VDvjlPIPBz4H3A+1O3xb94T8B7tjz/6D0BhmeBJ5D6nfcEcg/DE/Deb3KeQOQZPggMgicgegL5/Q3vAvhoFvwXuH/tCODFAPdLu1jNH2zz669N28Vs++ML22wfvwHZ2TVJ7XC9dwAAAABJRU5ErkJggg==")'
    }
    return (
      <div style={styles} ref="viewDomRef" onmousedown={this.handleMoveMouseDown.bind(this)}>
        {this.$slots?.default}
        {this.$props.resize && <div style={resizeStyle} onmousedown={this.handleResizeMouseDown.bind(this)} />}
      </div>
    )
  }
}

export default MovableView
</script>

可拖拽组件

/* Movable/Components/Area.vue */
<script>
const MovableArea = {
  props: {
    width: {
      type: Number,
      default: '100%'
    },
    height: {
      type: Number,
      default: '100%'
    },
    imgSrc: {
      type: String,
      default: ''
    }
  },
  render() {
    const styles = {
      position: 'relative',
      width: this.width ? this.width + 'px' : '100%',
      height: this.height ? this.height + 'px' : '100%',
      backgroundImage: 'url(' + this.imgSrc + ')',
      backgroundSize: '100%'
    }
    return (
      <div className="movable-area-block" style={styles}>
        {this.$slots?.default}
      </div>
    )
  }
}

export default MovableArea

</script>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue中,热区装饰是一种通过监听鼠标事件来实现交互效果的技术。可以用来实现一些鼠标悬停、点击、拖拽等操作,达到改变某个元素样式或触发某个事件的效果。 实现热区装饰的一种常见方式是通过绑定事件监听器来实现。例如,在Vue中可以通过v-on指令将鼠标悬停事件绑定到特定元素上,然后在Vue实例中定义一个对应的方法来处理这个事件。当鼠标悬停在该元素上时,方法会被调用,我们可以在方法中添加对应的样式类或其他操作逻辑。 另一种方式是使用Vue的emit机制。我们可以在子组件中定义一些自定义事件,然后在父组件中使用v-on指令监听这些事件。当鼠标触发了某个动作时,在子组件中使用this.$emit来触发自定义事件,然后在父组件中的对应方法中进行处理。 除了事件监听方式,Vue中还可以使用计算属性来实现热区装饰。计算属性是一个依赖于其他数据的属性,当依赖的数据发生变化时,计算属性会重新计算并返回新的值。我们可以根据鼠标位置以及其他条件计算出一个属性值,并将该属性绑定到特定元素上,从而实现热区装饰。 总的来说,Vue热区装饰提供了多种方式来实现交互效果,开发者可以根据具体需求选择合适的方式。无论是通过事件监听、emit机制还是计算属性,都能够实现视觉上的装饰或触发特定逻辑的效果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值