function _(props) { _classCallCheck(this, _); var _this = _possibleConstructorReturn(this, (_.__proto__ || Object.getPrototypeOf(_)).call(this, props)); _this.scrollTo = _this.scrollTo.bind(_this); _this.handleClick = _this.handleClick.bind(_this); _this.spyHandler = _this.spyHandler.bind(_this); return _this; }
n/a
function _(props) { _classCallCheck(this, _); var _this2 = _possibleConstructorReturn(this, (_.__proto__ || Object.getPrototypeOf(_)).call(this, props)); _this2.registerElems = _this2.registerElems.bind(_this2); return _this2; }
...
<div {...this.props}>
{this.props.children}
</div>
);
}
});
module.exports = Helpers.Element(Element);
var Link = React.createClass({
render: function () {
return (
<a {...this.props}>
{this.props.children}
</a>
...
function _(props) { _classCallCheck(this, _); var _this = _possibleConstructorReturn(this, (_.__proto__ || Object.getPrototypeOf(_)).call(this, props)); _this.scrollTo = _this.scrollTo.bind(_this); _this.handleClick = _this.handleClick.bind(_this); _this.spyHandler = _this.spyHandler.bind(_this); return _this; }
n/a
function Element(Component) { var _ = function (_React$Component2) { _inherits(_, _React$Component2); function _(props) { _classCallCheck(this, _); var _this2 = _possibleConstructorReturn(this, (_.__proto__ || Object.getPrototypeOf(_)).call(this, props)); _this2.registerElems = _this2.registerElems.bind(_this2); return _this2; } _createClass(_, [{ key: 'componentDidMount', value: function componentDidMount() { this.registerElems(this.props.name); } }, { key: 'componentWillReceiveProps', value: function componentWillReceiveProps(nextProps) { if (this.props.name !== nextProps.name) { this.registerElems(nextProps.name); } } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { defaultScroller.unregister(this.props.name); } }, { key: 'registerElems', value: function registerElems(name) { var domNode = ReactDOM.findDOMNode(this); defaultScroller.register(name, domNode); } }, { key: 'render', value: function render() { return React.createElement(Component, this.props); } }]); return _; }(React.Component); ; _.propTypes = { name: PropTypes.string, id: PropTypes.string }; return _; }
...
<div {...this.props}>
{this.props.children}
</div>
);
}
});
module.exports = Helpers.Element(Element);
var Link = React.createClass({
render: function () {
return (
<a {...this.props}>
{this.props.children}
</a>
...
function Scroll(Component, customScroller) {
var scroller = customScroller || defaultScroller;
var _ = function (_React$Component) {
_inherits(_, _React$Component);
function _(props) {
_classCallCheck(this, _);
var _this = _possibleConstructorReturn(this, (_.__proto__ || Object.getPrototypeOf(_)).call(this, props));
_this.scrollTo = _this.scrollTo.bind(_this);
_this.handleClick = _this.handleClick.bind(_this);
_this.spyHandler = _this.spyHandler.bind(_this);
return _this;
}
_createClass(_, [{
key: 'scrollTo',
value: function scrollTo(to, props) {
scroller.scrollTo(to, props);
}
}, {
key: 'handleClick',
value: function handleClick(event) {
/*
* give the posibility to override onClick
*/
if (this.props.onClick) {
this.props.onClick(event);
}
/*
* dont bubble the navigation
*/
if (event.stopPropagation) event.stopPropagation();
if (event.preventDefault) event.preventDefault();
/*
* do the magic!
*/
this.scrollTo(this.props.to, this.props);
}
}, {
key: 'spyHandler',
value: function spyHandler(y) {
var element = scroller.get(this.props.to);
if (!element) return;
var cords = element.getBoundingClientRect();
var topBound = cords.top + y;
var bottomBound = topBound + cords.height;
var offsetY = y - this.props.offset;
var to = this.props.to;
var isInside = offsetY >= topBound && offsetY <= bottomBound;
var isOutside = offsetY < topBound || offsetY > bottomBound;
var activeLink = scroller.getActiveLink();
if (isOutside && activeLink === to) {
scroller.setActiveLink(void 0);
this.setState({ active: false });
if (this.props.onSetInactive) {
this.props.onSetInactive();
}
} else if (isInside && activeLink != to) {
scroller.setActiveLink(to);
this.setState({ active: true });
if (this.props.onSetActive) {
this.props.onSetActive(to);
}
scrollSpy.updateStates();
}
}
}, {
key: 'componentDidMount',
value: function componentDidMount() {
var containerId = this.props.containerId;
var scrollSpyContainer = containerId ? document.getElementById(containerId) : document;
if (!scrollSpy.isMounted(scrollSpyContainer)) {
scrollSpy.mount(scrollSpyContainer);
}
if (this.props.spy) {
var to = this.props.to;
var element = null;
var elemTopBound = 0;
var elemBottomBound = 0;
this._stateHandler = function () {
if (scroller.getActiveLink() != to) {
if (this.state !== null && this.state.active && this.props.onSetInactive) {
this.props.onSetInactive();
}
this.setState({ active: false });
}
}.bind(this);
scrollSpy.addStateHandler(this._stateHandler);
this._spyHandler = function (y) {
var containerTop = 0;
if (scrollSpyContainer.getBoundingClientRect) {
var containerCords = scrollSpyContainer.getBoundingClientRect();
containerTop = containerCords.top;
}
if (!element || this.props.isDynamic) {
element = scroller.get(to);
if (!element) {
return;
}
var cords = element.getBoundingClientRect();
elemTopBound = cords.top - containerTop + y;
elemBottomBound = elemTopBound + cords.height;
}
var offsetY = y - this.props.offset;
var isInside = offsetY >= Math.floor(elemTopBound) && offsetY <= Math.floor(elemBottomBound);
var isOutside = offsetY < Math.floor(elemTopBound) || offsetY > Math.floor(elemBottomBound);
var active ...
...
<a {...this.props}>
{this.props.children}
</a>
);
}
});
module.exports = Helpers.Scroll(Link);
```
### Scroll Animations
> Add a custom easing animation to the smooth option. This prop will accept a Boolean if you want the default, or any of the
animations listed below
...
function startAnimateTopScroll(y, options, to, target) {
window.clearTimeout(__delayTimeout);
if (!options.ignoreCancelEvents) {
/*
* Sets the cancel trigger
*/
cancelEvents.register(function () {
__cancel = true;
});
}
setContainer(options);
__start = null;
__cancel = false;
__startPositionY = currentPositionY();
__targetPositionY = options.absolute ? y : y + __startPositionY;
__deltaTop = Math.round(__targetPositionY - __startPositionY);
__duration = functionWrapper(options.duration)(__deltaTop);
__duration = isNaN(parseFloat(__duration)) ? 1000 : parseFloat(__duration);
__to = to;
__target = target;
var easing = getAnimationType(options);
var easedAnimate = animateTopScroll.bind(null, easing);
if (options && options.delay > 0) {
__delayTimeout = window.setTimeout(function animate() {
requestAnimationFrameHelper.call(window, easedAnimate);
}, options.delay);
return;
}
requestAnimationFrameHelper.call(window, easedAnimate);
}
...
return;
}
/*
* Animate scrolling
*/
animateScroll.animateTopScroll(scrollOffset, props, to, target);
}
};
...
function getAnimationType(options) { if (_typeof(options.smooth) === Boolean && options.smooth === true) { return smooth.defaultEasing; } else { var animationType = options.smooth; switch (animationType) { case "linear": return smooth.linear; case "easeInQuad": return smooth.easeInQuad; case "easeOutQuad": return smooth.easeOutQuad; case "easeInOutQuad": return smooth.easeInOutQuad; case "easeInCubic": return smooth.easeInCubic; case "easeOutCubic": return smooth.easeOutQuad; case "easeInOutCubic": return smooth.easeInQuad; case "easeInQuart": return smooth.easeInQuart; case "easeOutQuart": return smooth.easeOutQuart; case "easeInOutQuart": return smooth.easeInOutQuart; case "easeInQuint": return smooth.easeInQuint; case "easeOutQuint": return smooth.easeInQuint; case "easeInOutQuint": return smooth.easeInOutQuint; default: return smooth.defaultEasing; } } }
...
var _smooth2 = _interopRequireDefault(_smooth);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
describe('AnimationTypeUnitTests', function () {
it('chooses correct easing function with no smooth options', function () {
var animation = _animateScroll2.default.getAnimationType({});
(0, _expect2.default)(animation).toEqual(_smooth2.default.defaultEasing);
});
it('chooses correct easing function for smooth: true', function () {
var animation = _animateScroll2.default.getAnimationType({ smooth: true });
(0, _expect2.default)(animation).toEqual(_smooth2.default.defaultEasing);
});
...
function scrollMore(toY, options) { setContainer(options); startAnimateTopScroll(currentPositionY() + toY, assign(options || {}, { absolute: true })); }
...
scrollToBottom: function() {
scroll.scrollToBottom();
},
scrollTo: function() {
scroll.scrollTo(100);
},
scrollMore: function() {
scroll.scrollMore(100);
},
handleSetActive: function(to) {
console.log(to);
},
render: function () {
return (
<div>
...
function scrollTo(toY, options) { startAnimateTopScroll(toY, assign(options || {}, { absolute: true })); }
...
scrollToTop: function() {
scroll.scrollToTop();
},
scrollToBottom: function() {
scroll.scrollToBottom();
},
scrollTo: function() {
scroll.scrollTo(100);
},
scrollMore: function() {
scroll.scrollMore(100);
},
handleSetActive: function(to) {
console.log(to);
},
...
function scrollToBottom(options) { setContainer(options); startAnimateTopScroll(scrollContainerHeight(), assign(options || {}, { absolute: true })); }
...
Events.scrollEvent.remove('begin');
Events.scrollEvent.remove('end');
},
scrollToTop: function() {
scroll.scrollToTop();
},
scrollToBottom: function() {
scroll.scrollToBottom();
},
scrollTo: function() {
scroll.scrollTo(100);
},
scrollMore: function() {
scroll.scrollMore(100);
},
...
function scrollToTop(options) { startAnimateTopScroll(0, assign(options || {}, { absolute: true })); }
...
},
componentWillUnmount: function() {
Events.scrollEvent.remove('begin');
Events.scrollEvent.remove('end');
},
scrollToTop: function() {
scroll.scrollToTop();
},
scrollToBottom: function() {
scroll.scrollToBottom();
},
scrollTo: function() {
scroll.scrollTo(100);
},
...
function addSpyHandler(handler, scrollSpyContainer) { var container = this.scrollSpyContainers[this.scrollSpyContainers.indexOf(scrollSpyContainer)]; if (!container.spyCallbacks) { container.spyCallbacks = []; } container.spyCallbacks.push(handler); }
...
this.props.onSetActive(to);
}
scrollSpy.updateStates();
}
}.bind(this);
scrollSpy.addSpyHandler(this._spyHandler, scrollSpyContainer);
}
}
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
scrollSpy.unmount(this._stateHandler, this._spyHandler);
}
...
function addStateHandler(handler) { this.spySetState.push(handler); }
...
if (this.state !== null && this.state.active && this.props.onSetInactive) {
this.props.onSetInactive();
}
this.setState({ active: false });
}
}.bind(this);
scrollSpy.addStateHandler(this._stateHandler);
this._spyHandler = function (y) {
var containerTop = 0;
if (scrollSpyContainer.getBoundingClientRect) {
var containerCords = scrollSpyContainer.getBoundingClientRect();
containerTop = containerCords.top;
...
function currentPositionY(scrollSpyContainer) { if (scrollSpyContainer === document) { var supportPageOffset = window.pageXOffset !== undefined; var isCSS1Compat = (document.compatMode || "") === "CSS1Compat"; return supportPageOffset ? window.pageYOffset : isCSS1Compat ? document.documentElement.scrollTop : document.body.scrollTop; } else { return scrollSpyContainer.scrollTop; } }
...
}
},
scrollHandler: function scrollHandler(scrollSpyContainer) {
var callbacks = this.scrollSpyContainers[this.scrollSpyContainers.indexOf(scrollSpyContainer)].spyCallbacks;
if (callbacks) {
for (var i = 0; i < callbacks.length; i++) {
var position = this.currentPositionY(scrollSpyContainer);
callbacks[i](this.currentPositionY(scrollSpyContainer));
}
}
},
addStateHandler: function addStateHandler(handler) {
this.spySetState.push(handler);
...
function isMounted(scrollSpyContainer) { return this.scrollSpyContainers.indexOf(scrollSpyContainer) !== -1; }
...
key: 'componentDidMount',
value: function componentDidMount() {
var containerId = this.props.containerId;
var scrollSpyContainer = containerId ? document.getElementById(containerId) : document;
if (!scrollSpy.isMounted(scrollSpyContainer)) {
scrollSpy.mount(scrollSpyContainer);
}
if (this.props.spy) {
var to = this.props.to;
var element = null;
var elemTopBound = 0;
...
function mount(scrollSpyContainer) { var t = this; if (scrollSpyContainer) { var eventHandler = eventThrottler(function (event) { t.scrollHandler(scrollSpyContainer); }); this.scrollSpyContainers.push(scrollSpyContainer); addPassiveEventListener(scrollSpyContainer, 'scroll', eventHandler); } }
...
value: function componentDidMount() {
var containerId = this.props.containerId;
var scrollSpyContainer = containerId ? document.getElementById(containerId) : document;
if (!scrollSpy.isMounted(scrollSpyContainer)) {
scrollSpy.mount(scrollSpyContainer);
}
if (this.props.spy) {
var to = this.props.to;
var element = null;
var elemTopBound = 0;
var elemBottomBound = 0;
...
function scrollHandler(scrollSpyContainer) { var callbacks = this.scrollSpyContainers[this.scrollSpyContainers.indexOf(scrollSpyContainer)].spyCallbacks; if (callbacks) { for (var i = 0; i < callbacks.length; i++) { var position = this.currentPositionY(scrollSpyContainer); callbacks[i](this.currentPositionY(scrollSpyContainer)); } } }
...
spySetState: [],
scrollSpyContainers: [],
mount: function mount(scrollSpyContainer) {
var t = this;
if (scrollSpyContainer) {
var eventHandler = eventThrottler(function (event) {
t.scrollHandler(scrollSpyContainer);
});
this.scrollSpyContainers.push(scrollSpyContainer);
addPassiveEventListener(scrollSpyContainer, 'scroll', eventHandler);
}
},
isMounted: function isMounted(scrollSpyContainer) {
...
function unmount(stateHandler, spyHandler) { for (var i = 0; i < this.scrollSpyContainers.length; i++) { var callbacks = this.scrollSpyContainers[i].spyCallbacks; if (callbacks && callbacks.length) { callbacks.splice(callbacks.indexOf(spyHandler), 1); } } if (this.spySetState && this.spySetState.length) { this.spySetState.splice(this.spySetState.indexOf(stateHandler), 1); } document.removeEventListener('scroll', this.scrollHandler); }
...
scrollSpy.addSpyHandler(this._spyHandler, scrollSpyContainer);
}
}
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
scrollSpy.unmount(this._stateHandler, this._spyHandler);
}
}, {
key: 'render',
value: function render() {
var className = "";
if (this.state && this.state.active) {
...
function update() { for (var i = 0; i < this.scrollSpyContainers.length; i++) { this.scrollHandler(this.scrollSpyContainers[i]); } }
...
console.log("begin", arguments);
});
Events.scrollEvent.register('end', function(to, element) {
console.log("end", arguments);
});
scrollSpy.update();
},
componentWillUnmount: function() {
Events.scrollEvent.remove('begin');
Events.scrollEvent.remove('end');
},
scrollToTop: function() {
...
function updateStates() { var length = this.spySetState.length; for (var i = 0; i < length; i++) { this.spySetState[i](); } }
...
scroller.setActiveLink(to);
this.setState({ active: true });
if (this.props.onSetActive) {
this.props.onSetActive(to);
}
scrollSpy.updateStates();
}
}
}, {
key: 'componentDidMount',
value: function componentDidMount() {
var containerId = this.props.containerId;
...
function get(name) { return __mapped[name] || document.getElementById(name); }
...
* do the magic!
*/
this.scrollTo(this.props.to, this.props);
}
}, {
key: 'spyHandler',
value: function spyHandler(y) {
var element = scroller.get(this.props.to);
if (!element) return;
var cords = element.getBoundingClientRect();
var topBound = cords.top + y;
var bottomBound = topBound + cords.height;
var offsetY = y - this.props.offset;
var to = this.props.to;
var isInside = offsetY >= topBound && offsetY <= bottomBound;
...
function getActiveLink() { return __activeLink; }
...
var cords = element.getBoundingClientRect();
var topBound = cords.top + y;
var bottomBound = topBound + cords.height;
var offsetY = y - this.props.offset;
var to = this.props.to;
var isInside = offsetY >= topBound && offsetY <= bottomBound;
var isOutside = offsetY < topBound || offsetY > bottomBound;
var activeLink = scroller.getActiveLink();
if (isOutside && activeLink === to) {
scroller.setActiveLink(void 0);
this.setState({ active: false });
if (this.props.onSetInactive) {
this.props.onSetInactive();
...
function register(name, element) { __mapped[name] = element; }
...
var scroll = Scroll.animateScroll;
var scrollSpy = Scroll.scrollSpy;
var Section = React.createClass({
componentDidMount: function() {
Events.scrollEvent.register('begin', function(to, element) {
console.log("begin", arguments);
});
Events.scrollEvent.register('end', function(to, element) {
console.log("end", arguments);
});
...
function scrollTo(to, props) {
/*
* get the mapped DOM element
*/
var target = this.get(to);
if (!target) {
console.warn("target Element not found");
return;
}
props = assign({}, props, { absolute: false });
if (events.registered['begin']) {
events.registered['begin'](to, target);
}
var containerId = props.containerId;
var containerElement = containerId ? document.getElementById(containerId) : null;
var scrollOffset;
if (containerId && containerElement) {
props.absolute = true;
if (containerElement !== target.offsetParent) {
if (!containerElement.contains(target)) {
throw new Error('Container with ID ' + containerId + ' is not a parent of target ' + to);
} else {
throw new Error('Container with ID ' + containerId + ' is not a positioned element');
}
}
scrollOffset = target.offsetTop;
} else {
var coordinates = target.getBoundingClientRect();
scrollOffset = coordinates.top;
}
scrollOffset += props.offset || 0;
/*
* if animate is not provided just scroll into the view
*/
if (!props.smooth) {
if (containerId && containerElement) {
containerElement.scrollTop = scrollOffset;
} else {
// window.scrollTo accepts only absolute values so body rectangle needs to be subtracted
var bodyRect = document.body.getBoundingClientRect();
window.scrollTo(0, scrollOffset - bodyRect.top);
}
if (events.registered['end']) {
events.registered['end'](to, target);
}
return;
}
/*
* Animate scrolling
*/
animateScroll.animateTopScroll(scrollOffset, props, to, target);
}
...
scrollToTop: function() {
scroll.scrollToTop();
},
scrollToBottom: function() {
scroll.scrollToBottom();
},
scrollTo: function() {
scroll.scrollTo(100);
},
scrollMore: function() {
scroll.scrollMore(100);
},
handleSetActive: function(to) {
console.log(to);
},
...
function setActiveLink(link) { __activeLink = link; }
...
var offsetY = y - this.props.offset;
var to = this.props.to;
var isInside = offsetY >= topBound && offsetY <= bottomBound;
var isOutside = offsetY < topBound || offsetY > bottomBound;
var activeLink = scroller.getActiveLink();
if (isOutside && activeLink === to) {
scroller.setActiveLink(void 0);
this.setState({ active: false });
if (this.props.onSetInactive) {
this.props.onSetInactive();
}
} else if (isInside && activeLink != to) {
scroller.setActiveLink(to);
...
function unmount() { __mapped = {}; }
...
scrollSpy.addSpyHandler(this._spyHandler, scrollSpyContainer);
}
}
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
scrollSpy.unmount(this._stateHandler, this._spyHandler);
}
}, {
key: 'render',
value: function render() {
var className = "";
if (this.state && this.state.active) {
...
function unregister(name) { delete __mapped[name]; }
...
if (this.props.name !== nextProps.name) {
this.registerElems(nextProps.name);
}
}
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
defaultScroller.unregister(this.props.name);
}
}, {
key: 'registerElems',
value: function registerElems(name) {
var domNode = ReactDOM.findDOMNode(this);
defaultScroller.register(name, domNode);
}
...