拖拽(学习)

拖拽的三大事件:

  onmousedowm / onmousemove / onmouseup

如果把移动事件放在box上,那么当鼠标移动快的时候,鼠标会脱离盒子,导致盒子不跟着鼠标走。

解决:把移动事件放在document上,就能解决

如果把抬起事件放在box上,那么鼠标放到了浏览器的地址栏时,松开鼠标还会导致盒子一直跟着鼠标走。

解决:把抬起事件放在document上

当页面中有文字(图片)并且选中的时候,那么会有浏览器的默认行为(使得拖拽元素拖动和抬起有问题)

解决:在按下的时候阻止默认行为:

  DOM 0:return false

  DOM2 :ev.preventDefault()

解除事件绑定 :

  普通绑定:

     ele.onmouseup = null

  ES6绑定:

    元素 removeEventListener ( "不带on的事件名",函数名)

    ele.removeEventListener("mousemove",this.m)

例子1:拖拽盒子ES6的类+DOM2事件绑定练习

<body>
<div id="box" style=" 100px;height:100px;background-color: red;position: absolute" ></div>
<script>
    class Drag{
        constructor(id){
            this.disX = 0;
            this.disY = 0;
            this.box = document.getElementById(id);
            this.m = this.move.bind(this);
            this.u = this.up.bind(this)
        }
        init(){
            this.box.addEventListener("mousedown",this.down.bind(this))
        }
        down(ev){
            this.disX = ev.pageX - this.box.offsetLeft;
            this.disY = ev.pageY - this.box.offsetTop;
            document.addEventListener("mousemove",this.m);
            document.addEventListener("mouseup",this.u)
        }
        move(ev){
            this.box.style.left = ev.pageX - this.disX + "px";
            this.box.style.top = ev.pageY - this.disY + "px"
        }
        up(){
            document.removeEventListener("mousemove",this.m);
            document.removeEventListener("mouseup",this.u);
        }
    }
    let xxx = new Drag("box");
    xxx.init();
</script>
</body>

 

例子2:react 版本 + 加磁吸效果

 

import React, { Component } from "react";
import "./BaseMoveBox.css";
import PropTypes from "prop-types";
let canRun = true;
let timer = null;
let time = null;
class BaseMoveBox extends Component {
  constructor(props) {
    super(props);
    this.state = {
      boxsInitialTop: null,
      boxsInitialLeft: null
    };
  };

  componentWillUnmount() {
    if (timer) {
      clearTimeout(timer)
    }
    if (time) {
      clearTimeout(time)
    }
  }

  getPosition = (ev) => {
    const box = document.getElementById(this.props.domId);
    let {boxsInitialTop, boxsInitialLeft} = this.state;
    if(!boxsInitialTop) {  //存下一个初始值,为磁吸做准备。
      boxsInitialTop = box.offsetTop;
      boxsInitialLeft = box.offsetLeft;
      this.setState({
        boxsInitialTop,
        boxsInitialLeft
      })
    }
    const disX = ev.clientX - box.offsetLeft;
    const disY = ev.clientY - box.offsetTop;
    return { disX, disY }  //鼠标点击的位置,到盒子的左边距,和下边距的距离。
  }

  onMouseDown = (ev) => {
    if (ev.target.className === "BaseMoveBox_header") { //判断可以拖动的事件源("header"上有其它组件或者按钮,不能触发拖拽)
      ev.target.parentNode.style.zIndex = 20;
      ev.preventDefault(); //阻止默认行为(防止拖拽过程中可能选中文字,"box"错误跟随的尴尬)
      const position = this.getPosition(ev);
      window.onmousemove = this.onMouseMove; //在 window / document 绑定 onmousemove 和 onmouseup
      window.onmouseup = this.onMouseUp;
      this.setState({ disX: position.disX, disY: position.disY });
    }
  }

  onMouseMove = (ev) => {
    if (!canRun) return;   //防抖
    canRun = false;
    timer = setTimeout(() => {
      const { disX, disY } = this.state;
      const x = ev.clientX - disX;
      const y = ev.clientY - disY;
      const { clientWidth, clientHeight } = document.documentElement;
      const box = document.getElementById(this.props.domId);
      if (box) {  //控制 left/top 使"box"不要超出合理的范围
        const maxHeight = clientHeight - box.offsetHeight
        const H = document.getElementsByClassName("page_head")[0].offsetHeight; //减去大标题的高度
        const maxWidth = clientWidth - box.offsetWidth;
        let top = y > H ? (y < maxHeight ? y : maxHeight) : H;
        let left = x > 0 ? (x < maxWidth ? x : maxWidth) : 0;
        this.setState({ pageX: left, pageY: top })
      }
      canRun = true;
    }, 17);
  }

  onMouseUp = async (ev) => {
    window.onmousemove = null;
    window.onmouseup = null;
    //磁吸效果
    const { boxsInitialTop, boxsInitialLeft } = this.state;
    const T = boxsInitialTop;
    const L = boxsInitialLeft;
    const { pageX, pageY } = this.state;
    const dT = Math.pow(Math.abs(pageY - T), 2) + Math.pow(Math.abs(pageX - L), 2);
    const dZ = Math.sqrt(dT);
    if (dZ < 100) {
      time = setTimeout(() => {
        this.setState({ pageX: L, pageY: T })
        ev.target.parentNode.style.zIndex = 10;
      }, 50)
    }
  }

  render() {
    const { title, width, height, domId, renderDom, backgroundColor } = this.props;
    const { pageX, pageY } = this.state;
    // console.log(pageX, pageY)
    return (
      <div className="BaseMoveBox" id={domId} style={{ left: pageX, top: pageY,  width, height: height }}>
        <header className="BaseMoveBox_header" onMouseDown={this.onMouseDown} title={title}>{title}</header>
        <div className="BaseMoveBox_content" style={{backgroundColor: backgroundColor}}>{renderDom}</div>
      </div>
    )
  }
}

BaseMoveBox.propTypes = {
  T: PropTypes.number,
  L: PropTypes.number,
  domId: PropTypes.string,
  title: PropTypes.string,
   PropTypes.string,
  height: PropTypes.string
}

BaseMoveBox.defaultProps = {
  domId: `${new Date().getTime() + Math.floor(Math.random() * 1000)}BaseMoveBox`,
  title: "改变标题,传title",
   "27.8rem",
  height: "18.2rem"
}

export default BaseMoveBox

/*
 * 定位父级很重要 position:"relative"      ,基于Window定位 position:fixed
 *
 */

// CSS 部分
.BaseMoveBox{
  height: 100%;
  100%;
  position: fixed;
  border-style: solid;
  border-color: #ccc;
  border- 0.05rem;
  border-radius: 0.2rem;
  /* overflow: hidden; */
  z-index: 10;
  /*background-color: #E1E3EC */
}
.BaseMoveBox>.BaseMoveBox_header{
  height: 1.5rem;
  line-height: 1.5rem;
  padding: 0.1rem 0.5rem;
  background-color: #E1E3EC;
  cursor: move;
}
.BaseMoveBox>.BaseMoveBox_content{
  height: calc(100% - 1.65rem);
  100%;
}

 

 

 

 更多好玩的效果,看2018年11月20日的课件

原文地址:https://www.cnblogs.com/MrZhujl/p/9988165.html