redoc/lib/services/scroll.service.ts

107 lines
3.0 KiB
TypeScript
Raw Normal View History

2016-05-07 10:54:44 +03:00
'use strict';
2016-11-23 02:23:32 +03:00
import { Injectable, EventEmitter } from '@angular/core';
2016-08-28 21:46:10 +03:00
import { BrowserDomAdapter as DOM } from '../utils/browser-adapter';
import { OptionsService } from './options.service';
2016-08-22 12:18:56 +03:00
import { throttle } from '../utils/helpers';
2016-05-07 10:54:44 +03:00
export const INVIEW_POSITION = {
ABOVE : 1,
BELLOW: -1,
INVIEW: 0
};
@Injectable()
export class ScrollService {
scrollYOffset: any;
$scrollParent: any;
2016-11-23 02:23:32 +03:00
scroll = new EventEmitter();
private prevOffsetY: number;
private _cancel:any;
2016-11-23 02:23:32 +03:00
private _savedPosition:number;
private _stickElement: HTMLElement;
2016-12-02 12:59:29 +03:00
constructor(optionsService:OptionsService) {
2016-05-07 10:54:44 +03:00
this.scrollYOffset = () => optionsService.options.scrollYOffset();
2016-11-24 16:29:29 +03:00
this.$scrollParent = optionsService.options.$scrollParent || window;
2016-05-07 10:54:44 +03:00
this.scroll = new EventEmitter();
this.bind();
2016-11-23 02:23:32 +03:00
if ('scrollRestoration' in history) {
history.scrollRestoration = 'manual';
}
2016-05-07 10:54:44 +03:00
}
scrollY() {
2016-06-13 20:54:24 +03:00
return (this.$scrollParent.pageYOffset != undefined) ? this.$scrollParent.pageYOffset : this.$scrollParent.scrollTop;
2016-05-07 10:54:44 +03:00
}
/* returns 1 if element if above the view, 0 if in view and -1 below the view */
getElementPos($el, inverted=false) {
2016-10-23 20:18:42 +03:00
let scrollYOffset = this.scrollYOffset();
let mul = inverted ? -1 : 1;
if (mul*Math.floor($el.getBoundingClientRect().top) > mul*scrollYOffset) {
2016-05-07 10:54:44 +03:00
return INVIEW_POSITION.ABOVE;
}
if (mul*$el.getBoundingClientRect().bottom <= mul*scrollYOffset) {
2016-05-07 10:54:44 +03:00
return INVIEW_POSITION.BELLOW;
}
return INVIEW_POSITION.INVIEW;
}
2016-11-23 02:23:32 +03:00
scrollToPos(posY: number) {
2016-05-07 10:54:44 +03:00
if (this.$scrollParent.scrollTo) {
2016-11-23 02:23:32 +03:00
this.$scrollParent.scrollTo(0, Math.floor(posY));
2016-05-07 10:54:44 +03:00
} else {
this.$scrollParent.scrollTop = posY;
2016-05-07 10:54:44 +03:00
}
}
2016-11-23 02:23:32 +03:00
scrollTo($el, offset:number = 0) {
if (!$el) return;
// TODO: rewrite this to use offsetTop as more reliable solution
let subjRect = $el.getBoundingClientRect();
let posY = this.scrollY() + subjRect.top - this.scrollYOffset() + offset + 1;
this.scrollToPos(posY);
return posY;
}
saveScroll() {
let $el = this._stickElement;
if (!$el) return;
let offsetParent = $el.offsetParent;
this._savedPosition = $el.offsetTop + (<any>offsetParent).offsetTop;
}
setStickElement($el) {
this._stickElement = $el;
}
restoreScroll() {
let $el = this._stickElement;
if (!$el) return;
let offsetParent = $el.offsetParent;
let currentPosition = $el.offsetTop + (<any>offsetParent).offsetTop;
let newY = this.scrollY() + (currentPosition - this._savedPosition);
this.scrollToPos(newY);
}
2016-05-07 10:54:44 +03:00
relativeScrollPos($el) {
let subjRect = $el.getBoundingClientRect();
2016-11-23 02:23:32 +03:00
return -subjRect.top + this.scrollYOffset() - 1;
}
2016-05-07 10:54:44 +03:00
scrollHandler(evt) {
let isScrolledDown = (this.scrollY() - this.prevOffsetY > 0);
this.prevOffsetY = this.scrollY();
this.scroll.next({isScrolledDown, evt});
}
bind() {
this.prevOffsetY = this.scrollY();
2016-08-28 21:46:10 +03:00
this._cancel = DOM.onAndCancel(this.$scrollParent, 'scroll',
2016-08-22 12:18:56 +03:00
throttle((evt) => { this.scrollHandler(evt); }, 100, this));
2016-05-07 10:54:44 +03:00
}
unbind() {
this._cancel();
}
}