代码之家  ›  专栏  ›  技术社区  ›  meds

移动反应可拖动到屏幕的侧面?

  •  5
  • meds  · 技术社区  · 7 年前

    我有一个可拖动的反应组件,我希望它的行为像 assistive touch

    我想在动画开始工作之前,我只需要将X位置设置为窗口的最左侧或最右侧,当用户放开拖动时,我尝试了以下操作:

    import Draggable, { ControlPosition } from 'react-draggable'; // The default
    import * as React from 'react';
    import './style.less'
    
    export default class FloatingScreenSpace extends React.Component<{}, {position:ControlPosition}> {
        state ={
            position: {x:90,y:0}
        }
    
        draggable: React.RefObject<Draggable> = React.createRef();
    
        onDragEnd = (e:MouseEvent)=>{
            if(this.draggable.current){
                this.setState({
                    position:{
                        x: 0,
                        y: e.clientY
                    }
                })
            }
        }
    
        public render() {
            return <Draggable position={this.state.position} ref={this.draggable} onStop={this.onDragEnd}>
                <div className="floatingActionButton" style={{ width: '100px', height: '100px' }}></div>
            </Draggable>
        }
    }
    

    我想在setstate函数中将x的位置设置为0,它会将其设置到屏幕的最左侧,但是这并没有发生。事实上,这似乎一点影响都没有。

    理想情况下,当用户放开时,我希望将按钮动画化到屏幕的最近边缘(顶部、底部、左侧、右侧)。

    1 回复  |  直到 7 年前
        1
  •  6
  •   Jordan Enev    7 年前

    onStop handler听起来是个不错的方法。

    请检查我的实施情况并遵循以下注释:

    const Draggable = ReactDraggable
    
    class Example extends React.Component {
      constructor(props) {
        super(props)
        
        this.state = { position: { x:0, y:0 }}
      }
    
      onStop(event, data) {
        // Draggable element bounding
        const bounding = data.node.getBoundingClientRect()
        
        // Viewport (wrapper)
        const documentElement = document.documentElement
        const wrapperHeight = (window.innerHeight || documentElement.clientHeight)
        const wrapperWidth = (window.innerWidth || documentElement.clientWidth)
        
        // Draggable element center coordinates (x,y)
        // Here we assume that the Draggable Button (from the question)
        // is a rectangle. But we can easily change it with whatever 
        // figure we want and fine-tune the calculation.
        // Credits: https://stackoverflow.com/a/18932029/4312466
        const center = { 
          x: data.x + (data.node.clientWidth / 2),
          y: data.y + (data.node.clientHeight / 2)
        }
        
        // The margin from the draggable's center,
        // to the viewport sides (top, left, bottom, right)
        const margin = {
          top: center.y - 0,
          left: center.x - 0,
          bottom: wrapperHeight - center.y,
          right: wrapperWidth - center.x
        }
        
        // When we get the nearest viewport side (below), then we can 
        // use these metrics to calculate the new draggable sticky `position`
        const position = {
          top: { y: 0, x: data.x },
          left: { y: data.y, x: 0 },
          bottom: { y: (wrapperHeight - data.node.clientHeight), x: data.x },
          right:  { y: data.y, x: (wrapperWidth - data.node.clientWidth)}
        }
       
        // Knowing the draggable's margins to the viewport sides,
        // now we can sort them out and get the smaller one.
        // The smallest margin defines the nearest viewport side to draggable.
        const sorted = Object.keys(margin).sort((a,b) => margin[a]-margin[b])
        const nearestSide = sorted[0]
        
        this.setState({ position: position[nearestSide] })
      }
    
      render() {
        return <Draggable
          position={this.state.position}
          onStop={(event, data) => this.onStop(event, data)}
          >
           <div className='handle'>Drag</div>
          </Draggable>
      }
    }
    
    ReactDOM.render(
      <Example />,
      document.getElementById('container')
    )
    body { 
      overflow-x: hidden; 
      overflow-y: hidden;
      padding: 0;
      margin: 0;
    }
    
    .handle {
      width: 40px;
      height: 40px;
      line-height: 40px;
      background-color: #2662c1;
      color: #fff;
      cursor: move;
      text-align: center;
    }
    
    .handle:not(.react-draggable-dragging) {
      -webkit-transition: -webkit-transform 0.5s ease-out; /* Safari */
      transition: transform 0.5s ease-out;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
    <script src="https://unpkg.com/react-draggable@3.0.5/dist/react-draggable.js"></script>
    
    <div id="container">
        <!-- This element's contents will be replaced with your component. -->
    </div>