mirror of
https://github.com/ant-design/ant-design.git
synced 2025-06-11 11:32:52 +08:00
Add Anchor[getContainer] (#9694)
This commit is contained in:
parent
c4698154b4
commit
da4062f9c1
@ -8,11 +8,11 @@ import AnchorLink from './AnchorLink';
|
|||||||
import getScroll from '../_util/getScroll';
|
import getScroll from '../_util/getScroll';
|
||||||
import getRequestAnimationFrame from '../_util/getRequestAnimationFrame';
|
import getRequestAnimationFrame from '../_util/getRequestAnimationFrame';
|
||||||
|
|
||||||
function getDefaultTarget() {
|
function getDefaultContainer() {
|
||||||
return window;
|
return window;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOffsetTop(element: HTMLElement): number {
|
function getOffsetTop(element: HTMLElement, container: AnchorContainer): number {
|
||||||
if (!element) {
|
if (!element) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -24,9 +24,11 @@ function getOffsetTop(element: HTMLElement): number {
|
|||||||
const rect = element.getBoundingClientRect();
|
const rect = element.getBoundingClientRect();
|
||||||
|
|
||||||
if (rect.width || rect.height) {
|
if (rect.width || rect.height) {
|
||||||
const doc = element.ownerDocument;
|
if (container === window) {
|
||||||
const docElem = doc.documentElement;
|
container = element.ownerDocument.documentElement;
|
||||||
return rect.top - docElem.clientTop;
|
return rect.top - container.clientTop;
|
||||||
|
}
|
||||||
|
return rect.top - (container as HTMLElement).getBoundingClientRect().top;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rect.top;
|
return rect.top;
|
||||||
@ -43,21 +45,27 @@ function easeInOutCubic(t: number, b: number, c: number, d: number) {
|
|||||||
|
|
||||||
const reqAnimFrame = getRequestAnimationFrame();
|
const reqAnimFrame = getRequestAnimationFrame();
|
||||||
const sharpMatcherRegx = /#([^#]+)$/;
|
const sharpMatcherRegx = /#([^#]+)$/;
|
||||||
function scrollTo(href: string, offsetTop = 0, target: () => Window | HTMLElement, callback = () => { }) {
|
function scrollTo(href: string, offsetTop = 0, getContainer: () => AnchorContainer, callback = () => { }) {
|
||||||
const scrollTop = getScroll(target(), true);
|
const container = getContainer();
|
||||||
|
const scrollTop = getScroll(container, true);
|
||||||
const sharpLinkMatch = sharpMatcherRegx.exec(href);
|
const sharpLinkMatch = sharpMatcherRegx.exec(href);
|
||||||
if (!sharpLinkMatch) { return; }
|
if (!sharpLinkMatch) { return; }
|
||||||
const targetElement = document.getElementById(sharpLinkMatch[1]);
|
const targetElement = document.getElementById(sharpLinkMatch[1]);
|
||||||
if (!targetElement) {
|
if (!targetElement) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const eleOffsetTop = getOffsetTop(targetElement);
|
const eleOffsetTop = getOffsetTop(targetElement, container);
|
||||||
const targetScrollTop = scrollTop + eleOffsetTop - offsetTop;
|
const targetScrollTop = scrollTop + eleOffsetTop - offsetTop;
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
const frameFunc = () => {
|
const frameFunc = () => {
|
||||||
const timestamp = Date.now();
|
const timestamp = Date.now();
|
||||||
const time = timestamp - startTime;
|
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) {
|
if (time < 450) {
|
||||||
reqAnimFrame(frameFunc);
|
reqAnimFrame(frameFunc);
|
||||||
} else {
|
} else {
|
||||||
@ -73,6 +81,8 @@ type Section = {
|
|||||||
top: number;
|
top: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type AnchorContainer = HTMLElement | Window;
|
||||||
|
|
||||||
export interface AnchorProps {
|
export interface AnchorProps {
|
||||||
prefixCls?: string;
|
prefixCls?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -82,7 +92,14 @@ export interface AnchorProps {
|
|||||||
bounds?: number;
|
bounds?: number;
|
||||||
affix?: boolean;
|
affix?: boolean;
|
||||||
showInkInFixed?: 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> {
|
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',
|
prefixCls: 'ant-anchor',
|
||||||
affix: true,
|
affix: true,
|
||||||
showInkInFixed: false,
|
showInkInFixed: false,
|
||||||
|
getContainer: getDefaultContainer,
|
||||||
};
|
};
|
||||||
|
|
||||||
static childContextTypes = {
|
static childContextTypes = {
|
||||||
@ -133,8 +151,8 @@ export default class Anchor extends React.Component<AnchorProps, any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const getTarget = this.props.target || getDefaultTarget;
|
const { getContainer } = this.props as AnchorDefaultProps;
|
||||||
this.scrollEvent = addEventListener(getTarget(), 'scroll', this.handleScroll);
|
this.scrollEvent = addEventListener(getContainer(), 'scroll', this.handleScroll);
|
||||||
this.handleScroll();
|
this.handleScroll();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,10 +177,10 @@ export default class Anchor extends React.Component<AnchorProps, any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleScrollTo = (link: string) => {
|
handleScrollTo = (link: string) => {
|
||||||
const { offsetTop, target = getDefaultTarget } = this.props;
|
const { offsetTop, getContainer } = this.props as AnchorDefaultProps;
|
||||||
this.animating = true;
|
this.animating = true;
|
||||||
this.setState({ activeLink: link });
|
this.setState({ activeLink: link });
|
||||||
scrollTo(link, offsetTop, target, () => {
|
scrollTo(link, offsetTop, getContainer, () => {
|
||||||
this.animating = false;
|
this.animating = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -174,16 +192,20 @@ export default class Anchor extends React.Component<AnchorProps, any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const linkSections: Array<Section> = [];
|
const linkSections: Array<Section> = [];
|
||||||
|
const { getContainer } = this.props as AnchorDefaultProps;
|
||||||
|
const container = getContainer();
|
||||||
this.links.forEach(link => {
|
this.links.forEach(link => {
|
||||||
const sharpLinkMatch = sharpMatcherRegx.exec(link.toString());
|
const sharpLinkMatch = sharpMatcherRegx.exec(link.toString());
|
||||||
if (!sharpLinkMatch) { return; }
|
if (!sharpLinkMatch) { return; }
|
||||||
const target = document.getElementById(sharpLinkMatch[1]);
|
const target = document.getElementById(sharpLinkMatch[1]);
|
||||||
if (target && getOffsetTop(target) < offsetTop + bounds) {
|
if (target) {
|
||||||
const top = getOffsetTop(target);
|
const top = getOffsetTop(target, container);
|
||||||
linkSections.push({
|
if (top < offsetTop + bounds) {
|
||||||
link,
|
linkSections.push({
|
||||||
top,
|
link,
|
||||||
});
|
top,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ For displaying anchor hyperlinks on page and jumping between them.
|
|||||||
| -------- | ----------- | ---- | ------- |
|
| -------- | ----------- | ---- | ------- |
|
||||||
| affix | Fixed mode of Anchor | boolean | true |
|
| affix | Fixed mode of Anchor | boolean | true |
|
||||||
| bounds | Bounding distance of anchor area | number | 5(px) |
|
| 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 | - |
|
| 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 |
|
| offsetTop | Pixels to offset from top when calculating position of scroll | number | 0 |
|
||||||
| showInkInFixed | Whether show ink-balls in Fixed mode | boolean | false |
|
| showInkInFixed | Whether show ink-balls in Fixed mode | boolean | false |
|
||||||
|
@ -20,6 +20,7 @@ title: Anchor
|
|||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| affix | 固定模式 | boolean | true |
|
| affix | 固定模式 | boolean | true |
|
||||||
| bounds | 锚点区域边界 | number | 5(px) |
|
| bounds | 锚点区域边界 | number | 5(px) |
|
||||||
|
| getContainer | 指定滚动的容器 | () => HTMLElement | () => window |
|
||||||
| offsetBottom | 距离窗口底部达到指定偏移量后触发 | number | |
|
| offsetBottom | 距离窗口底部达到指定偏移量后触发 | number | |
|
||||||
| offsetTop | 距离窗口顶部达到指定偏移量后触发 | number | |
|
| offsetTop | 距离窗口顶部达到指定偏移量后触发 | number | |
|
||||||
| showInkInFixed | 固定模式是否显示小圆点 | boolean | false |
|
| showInkInFixed | 固定模式是否显示小圆点 | boolean | false |
|
||||||
|
Loading…
Reference in New Issue
Block a user