Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moving react-draggable to side of screen?

I have a react-draggable component which I'd like to behave like the assistive touch

I figured before getting animations working I just need to set the x position to either the left or right most extend of the window when the user lets go of the drag, I tried the following:

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>
    }
}

I thought setting the x position to 0 in the setState function it would set it to the left most side of the screen, however that did not happen. In fact it didn't seem to have an affect at all.

Ideally I'd like to animate the button to the nearest edge of the screen (top, bottom, left, right) when the user lets go.

like image 826
meds Avatar asked Feb 04 '23 22:02

meds


1 Answers

Implementing the assistive touch behavior in onStop handler sounds a good approach.

Please check my implementation and follow the comments:

const Draggable = ReactDraggable

class Example extends React.Component {
  constructor(props) {
    super(props)
    
    this.state = { position: { x:0, y:0 }}
  }

  onStop(event, data) {
    // 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/[email protected]/dist/react-draggable.js"></script>

<div id="container">
    <!-- This element's contents will be replaced with your component. -->
</div>
like image 68
Jordan Enev Avatar answered Feb 06 '23 12:02

Jordan Enev