0
点赞
收藏
分享

微信扫一扫

js 移动元素至父盒子边界外,父滚动条自动跟随移动元素

jjt二向箔 2022-03-30 阅读 24

效果如下

 创建scrollmove.js,内容如下


//获取原生dom对象
export function getDom(name) {
    return document.querySelector(name)
}

//移动元素
// mouseDrag(e,()=>{},()=>{})  //mousedown事件e,move方法,up方法
export function mouseDrag(d, move, up) {
    let dx = d.clientX, dy = d.clientY
    const moveFn = (m) => {
        if (dx == m.clientX && dy == m.clientY) {
            return
        }
        move(m)
    }
    const upFn = (u) => {
        window.removeEventListener("mousemove", moveFn)
        window.removeEventListener("mouseup", upFn)
        if (up) up(u)
    }

    window.addEventListener("mousemove", moveFn)
    window.addEventListener("mouseup", upFn)
}



// 在mousedonw中使用,返回移动偏移
// 滚轮父盒子  边界距离  轴向     每次返回最大移动距离
// node       origin   noX noY  maxSize
export class BindScrollMove {
    constructor(options, cb) {
        //默认配置
        const e = options.e
        this._maxSize = 12
        this._origin = 20
        this._interval = 16
        this.cb = cb
        //滚轮父盒子 , 边界距离,轴向,最大移动距离
        let { node, origin, noX, noY, maxSize, interval } = options
        if (typeof node === 'string') {
            node = getDom(node)
        }
        if (maxSize) this._maxSize = maxSize
        if (interval) this._interval = interval
        if (origin !== undefined) this._origin = origin
        this._noX = noX
        this._noY = noY
        const clientRect = node.getBoundingClientRect();
        //记录存在区域
        this._startX = clientRect.left
        this._startY = clientRect.top
        this._endX = this._startX + node.clientWidth;
        this._endY = this._startY + node.clientHeight;
        //记录按下
        this._downX = e.clientX;
        this._downY = e.clientY;

        mouseDrag(e, this.mouseMove.bind(this), this.mouseUp.bind(this))
    }
    mouseMove(e) {
        if ((Math.abs(e.clientX - this._downX) > 15 && !this._noX) ||
            (Math.abs(e.clientY - this._downY) > 15 && !this._noY)) {
            this.mouseMoveFn(e)
            this._downX = e.clientX;
            this._downY = e.clientY;
        }
    }
    mouseMoveFn(e) {
        const x = e.clientX
        const y = e.clientY
        const bL = x - this._startX
        const bR = this._endX - x
        const bT = y - this._startY
        const bB = this._endY - y
        //编辑开始
        if (!this._noX && !this._noY) {
            //x,y轴都有监听
            if (bL < this._origin || bR < this._origin || bB < this._origin || bT < this._origin) {
                this.addInterVal({ bL, bR, bT, bB })
            } else {
                this.removeInterVal()
            }
        } else if (!this._noX && this._noY) {
            //仅有x轴监听
            if (bL < this._origin || bR < this._origin) {
                this.addInterVal({ bL, bR, bT, bB })
            } else {
                this.removeInterVal()
            }
        } else if (this._noX && !this._noY) {
            //仅有y轴监听
            if (bB < this._origin || bT < this._origin) {
                this.addInterVal({ bL, bR, bT, bB })
            } else {
                this.removeInterVal()
            }
        }
    }
    addInterVal(ops) {
        const { bL, bR, bT, bB } = ops
        //添加移动事件
        clearInterval(this._moveInterval)
        this._moveInterval = setInterval(() => {
            let dx = 0, dy = 0;
            //移动方向和距离
            if (bL < this._origin) {
                dx = (bL - this._origin) / 5;
            } else if (bR < this._origin) {
                dx = -(bR - this._origin) / 5;
            }

            if (bT < this._origin) {
                dy = (bT - this._origin) / 5;
            } else if (bB < this._origin) {
                dy = -(bB - this._origin) / 5;
            }

            dx = Math.round(dx)
            dy = Math.round(dy)

            dx = Math.min(this._maxSize, dx)
            dy = Math.min(this._maxSize, dy)
            dx = Math.max(-this._maxSize, dx)
            dy = Math.max(-this._maxSize, dy)

            if (this.callback) {
                this.callback({ dx, dy }) //将事件发出去
            }
            if (this.cb) {
                this.cb({ dx, dy })
            }

        }, this._interval)
    }
    removeInterVal() {
        clearInterval(this._moveInterval)
        this._moveInterval = null
    }
    mouseUp(e) {
        clearInterval(this._moveInterval)
        this._moveInterval = null
    }

}


vue页面,内容如下

<template>
  <div>
    <div class="pnode">
      <div class="snode">
        <div class="bg" v-for="num in 600" :key="num">{{ num }}</div>
        <div
          id="slider"
          @mousedown="sliderDown"
          :style="{ left: left + 'px', top: top + 'px' }"
        ></div>
      </div>
    </div>
  </div>
</template>

<script>
import { BindScrollMove, mouseDrag } from "@/utils/scrollmove";
export default {
  data() {
    return {
      left: 0,
      top: 0,
    };
  },
  methods: {
    sliderDown(e) {
      const L = e.clientX;
      const T = e.clientY;
      const defaultL = this.left;
      const defaultT = this.top;
      let scrollOffsetX = 0; //记录偏移量
      let scrollOffsetY = 0; //记录偏移量
      let slidL, slidT; //记录移动量

      const pNode = document.querySelector(".pnode");
      const sNode = document.querySelector(".snode");
      const slider = document.querySelector("#slider");
      const maxL = sNode.clientWidth - slider.clientWidth;
      const maxT = sNode.clientHeight - slider.clientHeight;

      // 正常移动滑块事件
      mouseDrag(e, (m) => {
        const X = m.clientX;
        const Y = m.clientY;
        slidL = X - L + defaultL; //滑块的left
        slidT = Y - T + defaultT; //滑块的top
        calcPosition();
      });

      // 移动至边界事件
      const moveC = new BindScrollMove({
        e, //按下事件e,必填
        node: ".pnode", //滚动条父盒子,必填
        origin: 30, //滑块距离边界多少开始计算
        // noX: true, //不计算X方向
        // noY: true, //不计算Y方向
        // interval:10, //10毫秒一次
        maxSize: 15, //callback最大移动距离
      });
      moveC.callback = (move) => {
        // console.log(move); //{dx,dy}
        if (this.left > 0 && this.left < maxL) {
          scrollOffsetX += move.dx;
        }
        if (this.top > 0 && this.top < maxT) {
          scrollOffsetY += move.dy;
        }
        if (move.dy === 0 && move.dx === 0) return;
        pNode.scrollTop += move.dy; //滚动条调用
        pNode.scrollLeft += move.dx;
        calcPosition();
      };

      //计算移动和偏移和叠加的后的位置
      const calcPosition = () => {
        let zL = slidL + scrollOffsetX;
        let zT = slidT + scrollOffsetY;
        if (zL < 0) zL = 0;
        if (zT < 0) zT = 0;
        if (zL > maxL) zL = maxL;
        if (zT > maxT) zT = maxT;
        this.left = zL;
        this.top = zT;
      };
    },
  },
};
</script>

<style lang="less" scoped>
.pnode {
  margin: 200px auto;
  width: 400px;
  height: 400px;
  max-height: 500px;
  border: 1px solid red;
  overflow: auto;
  .snode {
    min-width: 2000px;
    min-height: 2000px;
    text-align: left;
    position: relative;
    .bg {
      display: inline-block;
      font-size: 30px;
      color: #999;
      margin: 20px;
      user-select: none;
    }
    #slider {
      position: absolute;
      top: 0;
      left: 0;
      background: green;
      opacity: 0.8;
      width: 50px;
      height: 50px;
      user-select: none;
    }
  }
}
</style>

说明

        BindScrollMove方法仅根据用户移动事件得到滚动偏移量,最终需要通过calcPosion综合计算

举报

相关推荐

0 条评论