Add Anchor[getContainer] (#9694)

This commit is contained in:
Wei Zhu 2018-03-30 15:55:54 +08:00 committed by GitHub
parent c4698154b4
commit da4062f9c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 44 additions and 20 deletions

View File

@ -8,11 +8,11 @@ import AnchorLink from './AnchorLink';
import getScroll from '../_util/getScroll';
import getRequestAnimationFrame from '../_util/getRequestAnimationFrame';
function getDefaultTarget() {
function getDefaultContainer() {
return window;
}
function getOffsetTop(element: HTMLElement): number {
function getOffsetTop(element: HTMLElement, container: AnchorContainer): number {
if (!element) {
return 0;
}
@ -24,9 +24,11 @@ function getOffsetTop(element: HTMLElement): number {
const rect = element.getBoundingClientRect();
if (rect.width || rect.height) {
const doc = element.ownerDocument;
const docElem = doc.documentElement;
return rect.top - docElem.clientTop;
if (container === window) {
container = element.ownerDocument.documentElement;
return rect.top - container.clientTop;
}
return rect.top - (container as HTMLElement).getBoundingClientRect().top;
}
return rect.top;
@ -43,21 +45,27 @@ function easeInOutCubic(t: number, b: number, c: number, d: number) {
const reqAnimFrame = getRequestAnimationFrame();
const sharpMatcherRegx = /#([^#]+)$/;
function scrollTo(href: string, offsetTop = 0, target: () => Window | HTMLElement, callback = () => { }) {
const scrollTop = getScroll(target(), true);
function scrollTo(href: string, offsetTop = 0, getContainer: () => AnchorContainer, callback = () => { }) {
const container = getContainer();
const scrollTop = getScroll(container, true);
const sharpLinkMatch = sharpMatcherRegx.exec(href);
if (!sharpLinkMatch) { return; }
const targetElement = document.getElementById(sharpLinkMatch[1]);
if (!targetElement) {
return;
}
const eleOffsetTop = getOffsetTop(targetElement);
const eleOffsetTop = getOffsetTop(targetElement, container);
const targetScrollTop = scrollTop + eleOffsetTop - offsetTop;
const startTime = Date.now();
const frameFunc = () => {
const timestamp = Date.now();
const time = timestamp - startTime;
window.scrollTo(window.pageXOffset, easeInOutCubic(time, scrollTop, targetScrollTop, 450));
const nextScrollTop = easeInOutCubic(time, scrollTop, targetScrollTop, 450);
if (container === window) {
window.scrollTo(window.pageXOffset, nextScrollTop);
} else {
(container as HTMLElement).scrollTop = nextScrollTop;
}
if (time < 450) {
reqAnimFrame(frameFunc);
} else {
@ -73,6 +81,8 @@ type Section = {
top: number;
};
export type AnchorContainer = HTMLElement | Window;
export interface AnchorProps {
prefixCls?: string;
className?: string;
@ -82,7 +92,14 @@ export interface AnchorProps {
bounds?: number;
affix?: boolean;
showInkInFixed?: boolean;
target?: () => HTMLElement | Window;
getContainer?: () => AnchorContainer;
}
export interface AnchorDefaultProps extends AnchorProps {
prefixCls: string;
affix: boolean;
showInkInFixed: boolean;
getContainer: () => AnchorContainer;
}
export default class Anchor extends React.Component<AnchorProps, any> {
@ -92,6 +109,7 @@ export default class Anchor extends React.Component<AnchorProps, any> {
prefixCls: 'ant-anchor',
affix: true,
showInkInFixed: false,
getContainer: getDefaultContainer,
};
static childContextTypes = {
@ -133,8 +151,8 @@ export default class Anchor extends React.Component<AnchorProps, any> {
}
componentDidMount() {
const getTarget = this.props.target || getDefaultTarget;
this.scrollEvent = addEventListener(getTarget(), 'scroll', this.handleScroll);
const { getContainer } = this.props as AnchorDefaultProps;
this.scrollEvent = addEventListener(getContainer(), 'scroll', this.handleScroll);
this.handleScroll();
}
@ -159,10 +177,10 @@ export default class Anchor extends React.Component<AnchorProps, any> {
}
handleScrollTo = (link: string) => {
const { offsetTop, target = getDefaultTarget } = this.props;
const { offsetTop, getContainer } = this.props as AnchorDefaultProps;
this.animating = true;
this.setState({ activeLink: link });
scrollTo(link, offsetTop, target, () => {
scrollTo(link, offsetTop, getContainer, () => {
this.animating = false;
});
}
@ -174,16 +192,20 @@ export default class Anchor extends React.Component<AnchorProps, any> {
}
const linkSections: Array<Section> = [];
const { getContainer } = this.props as AnchorDefaultProps;
const container = getContainer();
this.links.forEach(link => {
const sharpLinkMatch = sharpMatcherRegx.exec(link.toString());
if (!sharpLinkMatch) { return; }
const target = document.getElementById(sharpLinkMatch[1]);
if (target && getOffsetTop(target) < offsetTop + bounds) {
const top = getOffsetTop(target);
linkSections.push({
link,
top,
});
if (target) {
const top = getOffsetTop(target, container);
if (top < offsetTop + bounds) {
linkSections.push({
link,
top,
});
}
}
});

View File

@ -19,6 +19,7 @@ For displaying anchor hyperlinks on page and jumping between them.
| -------- | ----------- | ---- | ------- |
| affix | Fixed mode of Anchor | boolean | true |
| bounds | Bounding distance of anchor area | number | 5(px) |
| getContainer | Scrolling container | () => HTMLElement | () => window |
| offsetBottom | Pixels to offset from bottom when calculating position of scroll | number | - |
| offsetTop | Pixels to offset from top when calculating position of scroll | number | 0 |
| showInkInFixed | Whether show ink-balls in Fixed mode | boolean | false |

View File

@ -20,6 +20,7 @@ title: Anchor
| --- | --- | --- | --- |
| affix | 固定模式 | boolean | true |
| bounds | 锚点区域边界 | number | 5(px) |
| getContainer | 指定滚动的容器 | () => HTMLElement | () => window |
| offsetBottom | 距离窗口底部达到指定偏移量后触发 | number | |
| offsetTop | 距离窗口顶部达到指定偏移量后触发 | number | |
| showInkInFixed | 固定模式是否显示小圆点 | boolean | false |