美文网首页
关于react 的拖曳组件

关于react 的拖曳组件

作者: 贩卖日落的他 | 来源:发表于2019-02-13 11:14 被阅读0次

    关于React的拖曳组件

    1.使用的时候

     import buildDraggableArea from '../Drag/DraggableAreaBuilder';
    const DraggableArea = buildDraggableArea();
    

    2.然后用DraggableArea 组件包裹你 的元素

    <DraggableArea
    
        initialTags={this.state.leftList}
    
        className="left"
    
        render={({tag}) => (
    
        <div>
    
            <Card.Grid style={gridStyle}>
    
                {tag.name}
    
            </Card.Grid>
    
        </div>
    
        )}
    
        onChange={(tags)=>this.onChange(tags)}
    
    />
    你的组件就能拖动了
    

    3.下面是源码

    import Reactfrom 'react';
    
    import ReactDOMfrom "react-dom";
    
    import { fromJS, List, is} from 'immutable';
    
    import stylesfrom './style.less';
    
    const isMobile = (typeof window.orientation!== "undefined") || (navigator.userAgent.indexOf('IEMobile') !== -1);
    
    export default function buildDraggableArea({isInAnotherArea = () => {}, passAddFunc = () => {}} = {}) {
    
      return class DraggableArea extends React.Component {
    
        constructor() {
    
          super();
    
          this.state = {
    
            tags: List([]),
    
          }
    
          this.draggableTagEles = {};
    
          this.tagEles = {};
    
          this.positions = [];
    
          this.rect = {};
    
          this.dragStart = {};
    
          this.tagChanged = false;
    
    }
    
        componentDidMount() {
    
          if (this.props.initialTags) {
    
            this.setTags(List(this.props.initialTags));
    
          } else {
    
            this.setTags(List(this.props.tags));
    
    }
    
          passAddFunc(this.container, this.addTag.bind(this));
    
          this.props.getAddTagFunc&& this.props.getAddTagFunc(this.addTag.bind(this));
    
    }
    
        componentWillReceiveProps({tags}) {
    
          if (!tags) return;
    
          console.log()
    
          if (tags.length !== this.props.tags.length|| tags.some((tag, i) => tag.id !== ((this.state.tags&&this.state.tags.size>0)&&this.state.tags.get(i).id))) {
    
            this.setTags(List(tags));
    
    }
    
    }
    
        componentDidUpdate(prevProps, {tags}) {
    
          this.tagChanged = this.tagChanged || this.state.tags.some((tag, i) => !tags.get(i) || tag.id !== tags.get(i).id);
    
    }
    
        dragElement(elmnt, id, parent) {
    
          const isList = this.props.isList;
    
          let prevX = 0, prevY = 0;
    
          let rect = {};
    
          let index;
    
          this.positions.forEach((p, i) => {
    
            if (p.id === id) index = i;
    
    });
    
          const dragStart = (e) => {
    
            // e.preventDefault();
    
            this.tagChanged = false;
    
            if (window.dragMouseDown) return;
    
            window.dragMouseDown = true;
    
            rect = this.container.getBoundingClientRect();
    
            e = e || window.event;
    
            prevX = e.clientX || e.touches[0].clientX;
    
            prevY = e.clientY || e.touches[0].clientY;
    
            elmnt.style.zIndex = 2;
    
            window.parentDragTag = elmnt.parentElement;
    
            while (window.parentDragTag && !window.parentDragTag.classList.contains('DraggableTags-tag-drag')) {
    
              window.parentDragTag = window.parentDragTag.parentElement;
    
    }
    
            if (window.parentDragTag) window.parentDragTag.style.zIndex = 2;
    
            document.addEventListener("mouseup", closeDragElement, false);
    
            document.addEventListener("mousemove", elementDrag, false);
    
            elmnt.addEventListener("touchend", closeDragElement, false);
    
            elmnt.addEventListener("touchcancel", closeDragElement, false);
    
            elmnt.addEventListener("touchmove", elementDrag, false);
    
            this.positions.forEach((p, i) => {
    
              if (p.id === id) index = i;
    
    });
    
    }
    
          const elementDrag = (e) => {
    
            if (isMobile) this.container.style.overflowY = 'visible';
    
            // Prevent scrolling on mobile devices
    
            e.type=== 'touchmove' &&  e.preventDefault();
    
            // Figure out the new position of tag
    
            e = e || window.event;
    
            let clientX = e.clientX || e.touches[0].clientX;
    
            let clientY = e.clientY || e.touches[0].clientY;
    
            let movedX = clientX - prevX;
    
            let movedY = clientY - prevY;
    
            prevX = clientX;
    
            prevY = clientY;
    
            let t = elmnt.offsetTop + movedY;
    
            let l = elmnt.offsetLeft+ movedX;
    
            elmnt.style.top = t + "px";
    
            elmnt.style.left = l + "px";
    
            let baseCenterTop= parent.offsetTop + elmnt.offsetHeight/ 2;
    
            let baseCenterLeft = parent.offsetLeft+ elmnt.offsetWidth/ 2;
    
            // The center position of the tag
    
            let ctop = baseCenterTop + t;
    
            let cleft = baseCenterLeft + l;
    
            let i; // safari 10 bug
    
    // Check if the tag could be put into a new position
    
            for (i = 0; i < this.positions.length- 1; i++) {
    
              // Do not check its left-side space and right-side space
    
              if ((index !== i || (index === this.positions.length- 2 && i === this.positions.length- 2)) && !(index - 1 === i && i !== 0)) {
    
                const p1 = this.positions[i];
    
                const p2 = this.positions[i+1];
    
                let isHead = false;
    
                let isTail = false;
    
                let between2Tags = false;
    
                let endOfLine = false;
    
                let startOfLine = false;
    
                if (!isList) {
    
                  // Is not "list view"
    
                  if (
    
                    // Head of tag list
    
                    i === 0 &&
    
                    ctop > p1.top &&
    
                    ctop < p1.bottom &&
    
                    cleft < p1.left + 8
    
                  ) isHead = true;
    
                  if (
    
                    // Tail of tag list
    
                    i === this.positions.length- 2 && ((
    
                    ctop > p2.top &&
    
                    cleft > p2.left - 8) || ctop > p2.bottom)
    
                  ) isTail = true;
    
                  if (
    
                    // Between two tags
    
                    ctop > p1.top &&
    
                    ctop < p1.bottom &&
    
                    cleft > p1.right - 8 &&
    
                    cleft < p2.left + 8
    
                  ) between2Tags = true;
    
                  if (
    
                    // Start of line
    
                    ctop > p2.top &&
    
                    ctop < p2.bottom &&
    
                    cleft < p2.left + 8 &&
    
                    p1.top < p2.top
    
                  ) startOfLine = true;
    
                  if (
    
                    // End of line
    
                    ctop > p1.top &&
    
                    ctop < p1.bottom &&
    
                    cleft > p1.right - 8 &&
    
                    p1.top < p2.top
    
                  ) endOfLine = true;
    
                } else {
    
                  // Is "list view"
    
                  if (
    
                    // Between two tags
    
                    ctop < p1.bottom + 6 &&
    
                    ctop > p2.top - 6
    
                  ) between2Tags = true;
    
                  if (
    
                    // Head of tag list
    
                    i === 0 &&
    
                    ctop < p1.top + 4
    
                  ) isHead = true;
    
                  if (
    
                    // Tail of tag list
    
                    i === this.positions.length- 2 &&
    
                    ctop > p2.bottom - 4
    
                  ) isTail = true;
    
    }
    
                if (
    
                  (!isList && (isHead || isTail || between2Tags || startOfLine || endOfLine))
    
                  ||
    
                  (isList && (isHead || isTail || between2Tags))
    
    ) {
    
                  let cur = this.state.tags.get(index);
    
                  let tags = this.state.tags.splice(index, 1);
    
                  if ((index < i || isHead) && !isTail) {
    
                    tags = tags.splice(i, 0, cur);
    
                    index = i;
    
                  } else {
    
                    tags = tags.splice(i+1, 0, cur);
    
                    index = i + 1;
    
    }
    
                  this.positions = [];
    
                  const prevBaseTop = this.tagEles[cur.id].offsetTop;
    
                  const prevBaseLeft = this.tagEles[cur.id].offsetLeft;
    
                  this.setState({tags}, () => {
    
                    let curBaseTop;
    
                    let curBaseLeft;
    
                    tags.forEach((t, i) => {
    
                      const tag = this.tagEles[t.id];
    
                      if (i === index) {
    
                        curBaseLeft = tag.offsetLeft;
    
                        curBaseTop= tag.offsetTop;
    
    }
    
                      this.positions.push({
    
                        id: t.id,
    
                        top: tag.offsetTop,
    
                        left: tag.offsetLeft,
    
                        bottom: tag.offsetTop + tag.offsetHeight,
    
                        right: tag.offsetLeft+ tag.offsetWidth,
    
                      });
    
    });
    
                    // 根据不同情况计算移动后的坐标
    
                    if (curBaseLeft > prevBaseLeft) {
    
                      elmnt.style.left = `${l - (curBaseLeft - prevBaseLeft)}px`;
    
                    } else {
    
                      elmnt.style.left = `${l + (prevBaseLeft - curBaseLeft)}px`;
    
    }
    
                    if (prevBaseTop > curBaseTop) {
    
                      elmnt.style.top = `${t + (prevBaseTop - curBaseTop)}px`;
    
                    } else {
    
                      elmnt.style.top = `${t - (curBaseTop - prevBaseTop)}px`;
    
    }
    
    });
    
                  break;
    
    }
    
    }
    
    }
    
    }
    
          const closeDragElement = (e) => {
    
            if (isMobile) this.container.style.overflowY = 'auto';
    
            window.dragMouseDown = false;
    
            document.removeEventListener("mouseup", closeDragElement, false);
    
            document.removeEventListener("mousemove", elementDrag, false);
    
            elmnt.removeEventListener("touchend", closeDragElement, false);
    
            elmnt.removeEventListener("touchcancel", closeDragElement, false);
    
            elmnt.removeEventListener("touchmove", elementDrag, false);
    
            if (window.parentDragTag) window.parentDragTag.style.zIndex = 1;
    
            let eRect = elmnt.getBoundingClientRect();
    
            let x = eRect.left+ eRect.width/ 2;
    
            let y = eRect.top+ eRect.height/ 2;
    
            if (x < rect.left || x > rect.right || y < rect.top || y > rect.bottom) {
    
              if (isInAnotherArea(elmnt.getBoundingClientRect(), this.state.tags.get(index))) {
    
                this.positions.splice(index, 1);
    
                this.setState({tags: this.state.tags.splice(index, 1)}, () => {
    
                  this.props.onChange && this.props.onChange(this.state.tags.toJS());
    
    });
    
                return;
    
    }
    
    }
    
            elmnt.style.top = 0;
    
            elmnt.style.left = 0;
    
            elmnt.style.zIndex = 1;
    
            if (this.tagChanged  && this.props.onChange) {
    
              this.tagChanged  = false;
    
              this.props.onChange(this.state.tags.toJS());
    
    }
    
    }
    
          elmnt.addEventListener("mousedown", dragStart, false);
    
          elmnt.addEventListener("touchstart", dragStart, false);
    
    }
    
        setTags(tags, callback) {
    
          this.setState({tags}, () => {
    
            callback && callback();
    
            this.positions = [];
    
            this.state.tags.forEach((t, i) => {
    
              const draggableTag = this.draggableTagEles[t.id];
    
              const tag = this.tagEles[t.id];
    
              this.positions.push({
    
                id: t.id,
    
                top: tag.offsetTop,
    
                left: tag.offsetLeft,
    
                bottom: tag.offsetTop + tag.offsetHeight,
    
                right: tag.offsetLeft+ tag.offsetWidth,
    
              });
    
              this.dragElement(draggableTag, t.id, tag);
    
    });
    
    });
    
    }
    
        addTag(tag) {
    
          this.setTags(this.state.tags.push(tag), () => {
    
            this.props.onChange && this.props.onChange(this.state.tags.toJS());
    
    });
    
    }
    
        buildDeleteTagFunc(tag) {
    
          return () => {
    
            const tags = this.state.tags.filter(t => tag.id !== t.id);
    
            this.setTags(tags, () => {
    
              this.props.onChange && this.props.onChange(this.state.tags.toJS());
    
    });
    
    }
    
    }
    
        render() {
    
          let {render, build, style, className, isList, tagMargin = '5px', tagStyle} = this.props;
    
          if (!render) render = build;
    
          const tags = this.state.tags.toJS().map((tag) => (
    
            <div
    
              key={tag.id}
    
              className="DraggableTags-tag"
    
              ref={(target) => {
    
                this.tagEles[tag.id] = target;
    
    }}
    
              style={isList ? {display: 'block', ...tagStyle} : tagStyle}
    
            >
    
              <div
    
                className="DraggableTags-tag-drag"
    
                ref={(target) => this.draggableTagEles[tag.id] = target}
    
              >
    
                {render({tag, deleteThis: this.buildDeleteTagFunc(tag)})}
    
              </div>
    
              <div style={{opacity: 0, overflow: 'hidden'}}>
    
                {render({tag, deleteThis: this.buildDeleteTagFunc(tag)})}
    
              </div>
    
            </div>
    
          ))
    
          return (
    
            <div
    
              ref={r => this.container = r}
    
              className={`DraggableTags ${className || ''}`}
    
              style={isMobile ? { overflowY: 'auto', ...style} : style}
    
            >
    
            {
    
              // To prevent body scroll on mobile device when dragging tags
    
              isMobile ? (<div style={{height: '101%'}}>{tags}</div>) : tags
    
            }
    
            </div>
    
          );
    
    }
    
    }
    
    }
    

    相关文章

      网友评论

          本文标题:关于react 的拖曳组件

          本文链接:https://www.haomeiwen.com/subject/gqvreqtx.html