vue实现的选区拖拽

拖拽功能算是JavaScript的入门脚本了吧,原本不想单独发篇文章,但为了照顾新手,还是简单的介绍一下吧。这篇文章就拿我花两个小时左右时间,写的一个通用Vue选区拖拽组件来说吧。

拖拽流程及原理

拖拽流程非常的简单,对于PC端来说:

1、鼠标按下
2、鼠标按下的同时,移动鼠标
3、鼠标抬起,结束拖拽

对应的事件也无非就那三个mousedown,mousemove,mouseup。

移动端也无非就touchstart,touchmove,touchend。

原理也很简单

1、鼠标按下时,记录初始状态,比如,鼠标位置、拖动对象的位置属性等,同时还应该监听鼠标移动和抬起;
2、鼠标按下的同时,移动鼠标时,计算鼠标的位置差、更改拖动对象的位置属性等;
3、鼠标抬起时,移除鼠标移动、抬起的监听函数,防止重复触发事件;

就这几行,很简单吧。

选区拖拽组件的Vue实现

也懒得废话了,直接贴我写好的组件,慢慢品味一下即可

需要注意的几点:

1、mousemove和mouseup都应该监听在根节点上,防止移动过程中拖出选框;

2、组件实现了等比例缩放;

3、拖拽完成后,向外emit的数据是选框在图片的真实位置映射;

组件的使用方法更简单,一行搞定

<Cropper imgUrl="http://wp-content/uploads/image/20180704/1530690513137706.png" :moveWidth="690" :moveHeight="280" @onCropEnd="onCropEnd"></Cropper>

效果如下,点击确定会调用onCropEnd方法

image.png

如下是代码,拷贝过去用吧

<template>
  <div class="cropper-root" :style="{height:winHeight+'px'}">
    <div class="background" :style="{'background-image':'url('+imgUrl+')',width:backWidth*zoomRatio+'px',height:backHeight*zoomRatio+'px'}" id="background">
      <div class="calculator" @mousedown.self="mousedown" id="sx-drag-selector" :style="{left:left+'px',top:top+'px',width:moveWidth+'px',height:moveHeight+'px'}"></div>
    </div>
    <div class="buttons">
      <!--<button>放大</button>-->
      <!--<button>缩小</button>-->
 <button @click="onSureClick">确认</button>
    </div>
  </div>
</template>

<script>
  export  default {
    name:'Cropper',
 props:{
      imgUrl:String,
 // 选框的高宽
 moveWidth:Number,
 moveHeight:Number,
 },
 data() {
      return {
        winHeight:window.innerHeight,
 // 背景图高宽
 backWidth:0,
 backHeight:0,
 //背景图缩放比例
 zoomRatio:1,

 //选框位置
 left:0,
 top:0,

 // 鼠标拖动开始的位置
 startX:0,
 startY:0,

 // 鼠标拖动开始时,选框的位置
 startLeft:0,
 startTop:0
 };
 },
 async mounted() {
      let self=this;
 // 获取图片尺寸
 let res = await this.getImageSize(self.imgUrl);
 if(!res) this.$message.error("获取图片尺寸失败!");

 if(this.moveWidth<=this.backWidth && this.moveHeight<=this.backHeight){
          // 正常
 console.log('正常逻辑');
 }
      if(this.moveWidth>this.backWidth&& this.moveHeight<=this.backHeight){
        this.zoomRatio = (this.moveWidth/this.backWidth);
 console.log('选框宽度超出,高度未超出',this.zoomRatio);

 }
      if(this.moveWidth<=this.backWidth &&this.moveHeight>this.backHeight){
        this.zoomRatio = (this.moveHeight/this.backHeight);
 console.log('选框高度超出,宽度未超出',this.zoomRatio);
 }

      if(this.moveWidth>this.backWidth && this.moveHeight>this.backHeight){
        this.zoomRatio =
 ((this.moveWidth/this.backWidth)>(this.moveHeight/this.backHeight))?
 (this.moveWidth/this.backWidth):(this.moveHeight/this.backHeight);
 console.log('选框高度超出,宽度也超出',this.zoomRatio);
 }

    },
 methods: {
      onSureClick(){
        console.log(this.left,this.top,this.zoomRatio,',',this.moveWidth,this.moveHeight,',',this.backWidth,this.backHeight);
 let emitData = {
          x:parseInt(this.left/this.zoomRatio),
 y:parseInt(this.top/this.zoomRatio),
 width:parseInt(this.moveWidth/this.zoomRatio),
 height:parseInt(this.moveHeight/this.zoomRatio)
        };
 console.log(emitData);
 this.$emit('onCropEnd',emitData);
 },
 mousedown(e){
        console.log('mousedown',e,this);

 this.startX = e.clientX;
 this.startY = e.clientY;
 this.startLeft = this.left;
 this.startTop = this.top;

 var drag = document.getElementById('sx-drag-selector');
 e = e || window.event;
 if(typeof drag.setCapture!='undefined'){
          drag.setCapture();
 }
        document.onmousemove = this.mousemove;
 document.onmouseup = this.mouseup;
 },
 mouseup(){
        var drag = document.getElementById('sx-drag-selector');
 document.onmousemove = null;
 document.onmouseup = null;
 if(typeof drag.releaseCapture!='undefined'){
          drag.releaseCapture();
 }
      },
 mousemove(e){
        let self = this;
 e = e || window.event;
 var diffX = e.clientX - this.startX;
 var diffY = e.clientY - this.startY;
 console.log(diffX,diffY);

 var left=this.startLeft+diffX;
 var top=this.startTop+diffY;

 let backWidth = self.backWidth*self.zoomRatio;
 let backHeight = self.backHeight*self.zoomRatio;

 if(left<0){
          left=0;
 }else if(left >parseInt(backWidth)-parseInt(self.moveWidth)){
          left =parseInt(backWidth)-parseInt(self.moveWidth);
 }
        if(top<0){
          top=0;
 }else if(top >parseInt(backHeight)-parseInt(self.moveHeight)){
          top =parseInt(backHeight)-parseInt(self.moveHeight);
 }
        console.log(left,top);
 self.left = left;
 self.top = top;
 },




 async getImageSize(url){
        let self = this;
 var img = new Image();
 img.src = url;
 return new Promise((resolve,reject)=>{
          img.onerror = function(){
            resolve(false);
 return false;
 };
 img.onload = function() {
            console.log(img.width + " " + img.height);
 self.backWidth = img.width;
 self.backHeight = img.height;
 img.onload = null;//避免重复加载
 resolve(true);
 };
 });
 }
    }
  };
</script>

<style scoped lang="stylus">
  .cropper-root{
    position:fixed;
 left:0;
 top:0;
 width:100%;
 height:100%;
 background-color :rgb(0,0,0);
 z-index:99999;
 }

  .calculator {
    position: absolute;
 display:inline-block;
 width: 200px;
 height: 200px;
 cursor: move;
 background:rgba(255,0,0,0.5);
 left:0;
 top:0;
 }
  .background{
    background-repeat:no-repeat;
 background-size:100% 100%;
 margin:0 auto;
 position: relative;
 margin-top :40px;

 }
  .buttons{
    width:100%;
 text-align :center;
 margin-top:20px;
 }
  .buttons button{
    width:50px;
 height:30px;
 display:inline-block;
 margin-left:10px;
 background-color:rgb(24, 144, 255);
 outline:none;
 border:none;
 border-radius:5px;
 color:white;
 cursor: pointer;
 &:active{
      background-color:rgb(55, 144, 255);
 }
  }
</style>

(本文完)



打赏作者

发表评论

电子邮件地址不会被公开。 必填项已用*标注