关于react 的拖曳组件

2019-02-13  本文已影响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>

      );

}

}

}
上一篇 下一篇

猜你喜欢

热点阅读