Skip to content

Commit

Permalink
Better strength control, remove buffer prop
Browse files Browse the repository at this point in the history
  • Loading branch information
nickclaw committed Jul 23, 2016
1 parent 6dab3ba commit 8de7862
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 33 deletions.
3 changes: 2 additions & 1 deletion .eslintrc
Expand Up @@ -7,6 +7,7 @@
},

"rules": {
"no-unused-vars": [0]
"no-unused-vars": [0],
"react/prop-types": [0]
}
}
116 changes: 110 additions & 6 deletions README.md
Expand Up @@ -2,7 +2,7 @@

Cross browser compatible scrolling containers for drag and drop interactions.

### Example
### Basic Example

```js
import React from 'react';
Expand All @@ -19,7 +19,7 @@ export default App(props) {
return (
<main>
<header />
<Scrollzone tag="section" buffer={150} speed={50} style={scrollStyle}>
<Scrollzone style={scrollStyle} >
<DraggableItem />
<DraggableItem />
<DraggableItem />
Expand All @@ -29,12 +29,116 @@ export default App(props) {
}
```

### Easing Example

```js
import React from 'react';
import Scrollzone, { defaultHorizontalStrength, defaultVerticalStrength } from 'react-dnd-scrollzone';

import DraggableItem from './path/to/DraggableItem';

const scrollStyle = {
overflowX: 'scroll',
overflowY: 'scroll',
}

/**
* @param {Number} val - from -1 to 1
* @return {Number} val - from -1 to 1
*/
function ease(val) {
// this easing function is from https://gist.github.com/gre/1650294 and
// expects/returns a number between [0, 1], however strength functions
// expects/returns a value between [-1, 1]

const t = val / 2 + 1; // [-1, 1] -> [0, 1]
const easedT = t<.5 ? 2*t*t : -1+(4-2*t)*t;
return easedT * 2 - 1; // [0, 1] -> [-1, 1]
}

function horizontalStrength(box, point) {
return ease(defaultHorizontalStrength(box, point));
}

function verticalStrength(box, point) {
return ease(defaultVerticalStrength(box, point));
}

export default App(props) {
return (
<main>
<header />
<Scrollzone style={scrollStyle} verticalStrength={verticalStrength} horizontalStrength={horizontalStrength} >
<DraggableItem />
<DraggableItem />
<DraggableItem />
</Scrollzone>
</main>
);
}
```


### API

#### `Scrollzone`

A React component with the following properties:

```js
<Scrollzone
buffer={Number} // distance from edge to start scrolling (default 150)
speed={Number} // top speed, play around with this (default 30)
tag={String} // tag to render as (default "div")
/>
verticalStrength={Function}
horizontalStrength={Function}
speed={Number}
tag={String|Component} >

{children}

</Scrollzone>
```
* `verticalStrength` - a function that returns the strength of the vertical scroll direction
* `horizontalStrength` a function that returns the strength of the horizontal scroll direction
* `speed` - strength multiplier, play around with this (default 30)
* `tag` - tag to render as (default "div")

The strength functions are both called with two arguments. An object representing the rectangle occupied by the Scrollzone, and an object representing the coordinates of mouse.

They should return a value between -1 and 1.
* Negative values scroll up or left.
* Positive values scroll down or right.
* 0 stops all scrolling.

#### `defaultVerticalStrength(box, point) -> Number`
The default vertical strength generating function. It linearly scales the
strength of vertical scroll as the cursor approaches a boundary.

Returns between -1 and 0 if the cursor is within 150 pixels of the top, and
returns between 0 and 1 if the cursor is within 150 pixels of the bottom.

#### `defaultHorizontalStrength(box, point) -> Number`
The default horizontal strength generating function. It linearly scales the
strength of horizontal scroll as the cursor approaches a boundary.

Returns between -1 and 0 if the cursor is within 150 pixels of the left, and
returns between 0 and 1 if the cursor is within 150 pixels of the right.

#### `createVerticalStrength(buffer)` and `createHorizontalStrength(buffer)`

These allow you to create their respective strength functions using a sensitivity different than 150px. This replaces the old `buffer` prop.

##### Example

```js
import Scrollzone, { createVerticalStrength, createHorizontalStrength } from 'react-dnd-scrollzone';

const vStrength = createVerticalStrength(500);
const hStrength = createHorizontalStrength(300);

// zone will scroll when the cursor drags within
// 500px of the top/bottom and 300px of the left/right
const zone = (
<Scrollzone verticalStrength={vStrength} horizontalStrength={hStrength}>

</Scrollzone>
);
```
63 changes: 37 additions & 26 deletions src/index.js
Expand Up @@ -2,44 +2,54 @@ import React from 'react';
import throttle from 'lodash.throttle';
import raf from 'raf';

function getHorizontalStrength({ left, width }, { clientX }, buffer) {
if (clientX >= left && clientX <= left + width) {
if (clientX < left + buffer) {
return (clientX - left - buffer) / buffer;
} else if (clientX > (left + width - buffer)) {
return -(left + width - clientX - buffer) / buffer;
const DEFAULT_BUFFER = 150;

export function createHorizontalStrength(buffer) {
return function defaultHorizontalStrength({ x, w }, point) {
if (point.x >= x && point.x <= x + w) {
if (point.x < x + buffer) {
return (point.x - x - buffer) / buffer;
} else if (point.x > (x + w - buffer)) {
return -(x + w - point.x - buffer) / buffer;
}
}
}

return 0;
return 0;
};
}

function getVerticalStrength({ top, height }, { clientY }, buffer) {
if (clientY >= top && clientY <= top + height) {
if (clientY < top + buffer) {
return (clientY - top - buffer) / buffer;
} else if (clientY > (top + height - buffer)) {
return -(top + height - clientY - buffer) / buffer;
export function createVerticalStrength(buffer) {
return function defaultVerticalStrength({ y, h }, point) {
if (point.y >= y && point.y <= y + h) {
if (point.y < y + buffer) {
return (point.y - y - buffer) / buffer;
} else if (point.y > (y + h - buffer)) {
return -(y + h - point.y - buffer) / buffer;
}
}
}

return 0;
return 0;
};
}

export const defaultHorizontalStrength = createHorizontalStrength(DEFAULT_BUFFER);

export const defaultVerticalStrength = createVerticalStrength(DEFAULT_BUFFER);

export default class Scrollzone extends React.Component {

static propTypes = {
verticalStrength: React.PropTypes.func,
horizontalStrength: React.PropTypes.func,
tag: React.PropTypes.string,
buffer: React.PropTypes.number,
speed: React.PropTypes.number,
onDragOver: React.PropTypes.func,
className: React.PropTypes.string,
}

static defaultProps = {
tag: 'div',
buffer: 150,
verticalStrength: defaultVerticalStrength,
horizontalStrength: defaultHorizontalStrength,
speed: 30,
tag: 'div',
};

constructor(props, ctx) {
Expand Down Expand Up @@ -85,13 +95,14 @@ export default class Scrollzone extends React.Component {
// Update scaleX and scaleY every 100ms or so
// and start scrolling if necessary
updateScrolling = throttle(evt => {
const { buffer } = this.props;
const { container } = this.refs;
const rect = container.getBoundingClientRect();
const { left: x, top: y, width: w, height: h } = container.getBoundingClientRect();
const box = { x, y, w, h };
const coords = { x: evt.clientX, y: evt.clientY };

// calculate strength
this.scaleX = getHorizontalStrength(rect, evt, buffer);
this.scaleY = getVerticalStrength(rect, evt, buffer);
this.scaleX = this.props.horizontalStrength(box, coords);
this.scaleY = this.props.verticalStrength(box, coords);

// start scrolling if we need to
if (!this.frame && (this.scaleX || this.scaleY)) this.startScrolling();
Expand Down Expand Up @@ -133,7 +144,7 @@ export default class Scrollzone extends React.Component {
}

render() {
const { tag: Tag, buffer, speed, className, ...props } = this.props;
const { tag: Tag, speed, className, ...props } = this.props;
const compClass = className ? `${className} scrollzone` : 'scrollzone';

return (
Expand Down

0 comments on commit 8de7862

Please sign in to comment.