|
1 | 1 | import React, { PropTypes } from 'react';
|
2 |
| -import animateScroll from './animatescroll'; |
| 2 | +import warning from 'fbjs/lib/warning'; |
| 3 | +import { getScrollTop, setScrollTop, getOffsetTop } from './utils'; |
3 | 4 |
|
4 | 5 | export default class Scrollchor extends React.Component {
|
| 6 | + constructor(props) { |
| 7 | + super(props); |
| 8 | + this._to = props.to && props.to.replace(/^#/, '') || ''; |
| 9 | + const { |
| 10 | + offset = 0, duration = 400, easing = easeOutQuad, |
| 11 | + beforeHook = () => {}, |
| 12 | + afterHook = () => {} |
| 13 | + } = props.animate || {}; |
| 14 | + this._beforeHook = beforeHook; |
| 15 | + this._afterHook = afterHook; |
| 16 | + this._animate = { offset, duration, easing }; |
| 17 | + } |
| 18 | + |
5 | 19 | static propTypes = {
|
6 | 20 | to: PropTypes.string.isRequired,
|
7 |
| - animate: PropTypes.object |
| 21 | + animate: PropTypes.shape({ |
| 22 | + offset: PropTypes.number, |
| 23 | + duration: PropTypes.number, |
| 24 | + easing: PropTypes.func, |
| 25 | + beforeHook: PropTypes.func, |
| 26 | + afterHook: PropTypes.func |
| 27 | + }) |
8 | 28 | };
|
9 | 29 |
|
10 | 30 | handleClick = (event) => {
|
11 |
| - event.preventDefault(); |
| 31 | + this._beforeHook(event); |
12 | 32 |
|
13 |
| - const { animate } = this.props; |
14 |
| - animateScroll(this._to, animate); |
15 |
| - }; |
| 33 | + event.preventDefault(); |
| 34 | + animateScroll(this._to, this._animate); |
16 | 35 |
|
17 |
| - componentWillMount() { |
18 |
| - const { to = '' } = this.props; |
19 |
| - this._to = to.replace(/^#/, ''); |
| 36 | + this._afterHook(event); |
20 | 37 | }
|
21 | 38 |
|
22 | 39 | render() {
|
23 | 40 | const { to, animate, ...props } = this.props; // eslint-disable-line no-unused-vars
|
24 | 41 | return <a {...props} href={'#' + this._to} onClick={this.handleClick} />;
|
25 | 42 | }
|
26 | 43 | }
|
| 44 | + |
| 45 | +function animateScroll(id, animate) { |
| 46 | + const element = id ? document.getElementById(id) : document.body; |
| 47 | + |
| 48 | + warning(element, `Cannot find element: #${id}`); |
| 49 | + |
| 50 | + scrollTo(element, animate); |
| 51 | +} |
| 52 | + |
| 53 | +function scrollTo(element, { offset, duration, easing }) { |
| 54 | + const start = getScrollTop(); |
| 55 | + const to = getOffsetTop(element) + offset; |
| 56 | + const change = to - start; |
| 57 | + const increment = 20; |
| 58 | + |
| 59 | + function animate(elapsedTime) { |
| 60 | + const elapsed = elapsedTime + increment; |
| 61 | + const position = easing(null, elapsed, start, change, duration); |
| 62 | + |
| 63 | + setScrollTop(position); |
| 64 | + |
| 65 | + if (elapsed < duration) { |
| 66 | + setTimeout(function() { |
| 67 | + animate(elapsed); |
| 68 | + }, increment); |
| 69 | + } |
| 70 | + } |
| 71 | + |
| 72 | + animate(0); |
| 73 | +} |
| 74 | + |
| 75 | +// Default easing function |
| 76 | +// jQuery easing 'swing' |
| 77 | +function easeOutQuad(x, t, b, c, d) { |
| 78 | + return -c * (t /= d) * (t - 2) + b; |
| 79 | +} |
0 commit comments