From 22b28b34c2ffda4714e1578a536a1a230c15070f Mon Sep 17 00:00:00 2001
From: tangjinzhou <415800467@qq.com>
Date: Sat, 8 Feb 2020 13:07:57 +0800
Subject: [PATCH] feat: update anchor
---
build/config.js | 2 +-
components/anchor/Anchor.jsx | 185 +++++++++++-------
components/anchor/AnchorLink.jsx | 4 +-
.../__tests__/__snapshots__/demo.test.js.snap | 47 +++++
components/anchor/demo/basic.md | 1 +
components/anchor/demo/customizeHighlight.md | 31 +++
components/anchor/demo/index.vue | 6 +
components/anchor/demo/onChange.md | 31 +++
components/anchor/demo/static.md | 2 +-
components/anchor/demo/targetOffset.md | 34 ++++
components/anchor/index.en-US.md | 38 ++--
components/anchor/index.zh-CN.md | 38 ++--
tests/utils.js | 1 +
13 files changed, 314 insertions(+), 106 deletions(-)
create mode 100644 components/anchor/demo/customizeHighlight.md
create mode 100644 components/anchor/demo/onChange.md
create mode 100644 components/anchor/demo/targetOffset.md
diff --git a/build/config.js b/build/config.js
index 45fccbd57..24a3cbe38 100644
--- a/build/config.js
+++ b/build/config.js
@@ -1,5 +1,5 @@
module.exports = {
dev: {
- componentName: 'alert', // dev components
+ componentName: 'anchor', // dev components
},
};
diff --git a/components/anchor/Anchor.jsx b/components/anchor/Anchor.jsx
index b8412d6ed..485bb9e22 100644
--- a/components/anchor/Anchor.jsx
+++ b/components/anchor/Anchor.jsx
@@ -2,8 +2,8 @@ import PropTypes from '../_util/vue-types';
import classNames from 'classnames';
import addEventListener from '../vc-util/Dom/addEventListener';
import Affix from '../affix';
+import scrollTo from '../_util/scrollTo';
import getScroll from '../_util/getScroll';
-import raf from 'raf';
import { initDefaultProps } from '../_util/props-util';
import BaseMixin from '../_util/BaseMixin';
import { ConfigConsumerProps } from '../config-provider';
@@ -34,47 +34,47 @@ function getOffsetTop(element, container) {
return rect.top;
}
-function easeInOutCubic(t, b, c, d) {
- const cc = c - b;
- t /= d / 2;
- if (t < 1) {
- return (cc / 2) * t * t * t + b;
- }
- return (cc / 2) * ((t -= 2) * t * t + 2) + b;
-}
+// function easeInOutCubic(t, b, c, d) {
+// const cc = c - b;
+// t /= d / 2;
+// if (t < 1) {
+// return (cc / 2) * t * t * t + b;
+// }
+// return (cc / 2) * ((t -= 2) * t * t + 2) + b;
+// }
const sharpMatcherRegx = /#([^#]+)$/;
-function scrollTo(href, offsetTop = 0, getContainer, 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, container);
- const targetScrollTop = scrollTop + eleOffsetTop - offsetTop;
- const startTime = Date.now();
- const frameFunc = () => {
- const timestamp = Date.now();
- const time = timestamp - startTime;
- const nextScrollTop = easeInOutCubic(time, scrollTop, targetScrollTop, 450);
- if (container === window) {
- window.scrollTo(window.pageXOffset, nextScrollTop);
- } else {
- container.scrollTop = nextScrollTop;
- }
- if (time < 450) {
- raf(frameFunc);
- } else {
- callback();
- }
- };
- raf(frameFunc);
-}
+// function scrollTo(href, offsetTop = 0, getContainer, 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, container);
+// const targetScrollTop = scrollTop + eleOffsetTop - offsetTop;
+// const startTime = Date.now();
+// const frameFunc = () => {
+// const timestamp = Date.now();
+// const time = timestamp - startTime;
+// const nextScrollTop = easeInOutCubic(time, scrollTop, targetScrollTop, 450);
+// if (container === window) {
+// window.scrollTo(window.pageXOffset, nextScrollTop);
+// } else {
+// container.scrollTop = nextScrollTop;
+// }
+// if (time < 450) {
+// raf(frameFunc);
+// } else {
+// callback();
+// }
+// };
+// raf(frameFunc);
+// }
export const AnchorProps = {
prefixCls: PropTypes.string,
@@ -85,6 +85,8 @@ export const AnchorProps = {
getContainer: PropTypes.func,
wrapperClass: PropTypes.string,
wrapperStyle: PropTypes.object,
+ getCurrentAnchor: PropTypes.func,
+ targetOffset: PropTypes.number,
};
export default {
@@ -130,43 +132,38 @@ export default {
mounted() {
this.$nextTick(() => {
const { getContainer } = this;
- this.scrollEvent = addEventListener(getContainer(), 'scroll', this.handleScroll);
+ this.scrollContainer = getContainer();
+ this.scrollEvent = addEventListener(this.scrollContainer, 'scroll', this.handleScroll);
this.handleScroll();
});
},
-
+ updated() {
+ this.$nextTick(() => {
+ if (this.scrollEvent) {
+ const { getContainer } = this;
+ const currentContainer = getContainer();
+ if (this.scrollContainer !== currentContainer) {
+ this.scrollContainer = currentContainer;
+ this.scrollEvent.remove();
+ this.scrollEvent = addEventListener(this.scrollContainer, 'scroll', this.handleScroll);
+ this.handleScroll();
+ }
+ }
+ this.updateInk();
+ });
+ },
beforeDestroy() {
if (this.scrollEvent) {
this.scrollEvent.remove();
}
},
-
- updated() {
- this.$nextTick(() => {
- this.updateInk();
- });
- },
methods: {
- handleScroll() {
- if (this.animating) {
- return;
+ getCurrentActiveLink(offsetTop = 0, bounds = 5) {
+ const { getCurrentAnchor } = this;
+
+ if (typeof getCurrentAnchor === 'function') {
+ return getCurrentAnchor();
}
- const { offsetTop, bounds } = this;
- this.setState({
- activeLink: this.getCurrentAnchor(offsetTop, bounds),
- });
- },
-
- handleScrollTo(link) {
- const { offsetTop, getContainer } = this;
- this.animating = true;
- this.setState({ activeLink: link });
- scrollTo(link, offsetTop, getContainer, () => {
- this.animating = false;
- });
- },
-
- getCurrentAnchor(offsetTop = 0, bounds = 5) {
const activeLink = '';
if (typeof document === 'undefined') {
return activeLink;
@@ -199,6 +196,56 @@ export default {
return '';
},
+ handleScrollTo(link) {
+ const { offsetTop, getContainer, targetOffset } = this;
+
+ this.setCurrentActiveLink(link);
+ const container = getContainer();
+ const scrollTop = getScroll(container, true);
+ const sharpLinkMatch = sharpMatcherRegx.exec(link);
+ if (!sharpLinkMatch) {
+ return;
+ }
+ const targetElement = document.getElementById(sharpLinkMatch[1]);
+ if (!targetElement) {
+ return;
+ }
+
+ const eleOffsetTop = getOffsetTop(targetElement, container);
+ let y = scrollTop + eleOffsetTop;
+ y -= targetOffset !== undefined ? targetOffset : offsetTop || 0;
+ this.animating = true;
+
+ scrollTo(y, {
+ callback: () => {
+ this.animating = false;
+ },
+ getContainer,
+ });
+ },
+ setCurrentActiveLink(link) {
+ const { activeLink } = this;
+
+ if (activeLink !== link) {
+ this.setState({
+ activeLink: link,
+ });
+ this.$emit('change', link);
+ }
+ },
+
+ handleScroll() {
+ if (this.animating) {
+ return;
+ }
+ const { offsetTop, bounds, targetOffset } = this;
+ const currentActiveLink = this.getCurrentActiveLink(
+ targetOffset !== undefined ? targetOffset : offsetTop || 0,
+ bounds,
+ );
+ this.setCurrentActiveLink(currentActiveLink);
+ },
+
updateInk() {
if (typeof document === 'undefined') {
return;
@@ -206,7 +253,7 @@ export default {
const { _sPrefixCls } = this;
const linkNode = this.$el.getElementsByClassName(`${_sPrefixCls}-link-title-active`)[0];
if (linkNode) {
- this.$refs.linkNode.style.top = `${linkNode.offsetTop + linkNode.clientHeight / 2 - 4.5}px`;
+ this.$refs.inkNode.style.top = `${linkNode.offsetTop + linkNode.clientHeight / 2 - 4.5}px`;
}
},
},
@@ -245,7 +292,7 @@ export default {
diff --git a/components/anchor/AnchorLink.jsx b/components/anchor/AnchorLink.jsx
index 6d1655d44..ca36495f2 100644
--- a/components/anchor/AnchorLink.jsx
+++ b/components/anchor/AnchorLink.jsx
@@ -7,6 +7,7 @@ export const AnchorLinkProps = {
prefixCls: PropTypes.string,
href: PropTypes.string,
title: PropTypes.any,
+ target: PropTypes.string,
};
export default {
@@ -47,7 +48,7 @@ export default {
},
},
render() {
- const { prefixCls: customizePrefixCls, href, $slots } = this;
+ const { prefixCls: customizePrefixCls, href, $slots, target } = this;
const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('anchor', customizePrefixCls);
@@ -66,6 +67,7 @@ export default {
class={titleClassName}
href={href}
title={typeof title === 'string' ? title : ''}
+ target={target}
onClick={this.handleClick}
>
{title}
diff --git a/components/anchor/__tests__/__snapshots__/demo.test.js.snap b/components/anchor/__tests__/__snapshots__/demo.test.js.snap
index 2d396de52..d1e5d2c4e 100644
--- a/components/anchor/__tests__/__snapshots__/demo.test.js.snap
+++ b/components/anchor/__tests__/__snapshots__/demo.test.js.snap
@@ -8,6 +8,7 @@ exports[`renders ./components/anchor/demo/basic.md correctly 1`] = `
+
API
@@ -18,6 +19,34 @@ exports[`renders ./components/anchor/demo/basic.md correctly 1`] = `
`;
+exports[`renders ./components/anchor/demo/customizeHighlight.md correctly 1`] = `
+
+`;
+
+exports[`renders ./components/anchor/demo/onChange.md correctly 1`] = `
+
+`;
+
exports[`renders ./components/anchor/demo/onClick.md correctly 1`] = `
@@ -45,3 +74,21 @@ exports[`renders ./components/anchor/demo/static.md correctly 1`] = `
`;
+
+exports[`renders ./components/anchor/demo/targetOffset.md correctly 1`] = `
+
+`;
diff --git a/components/anchor/demo/basic.md b/components/anchor/demo/basic.md
index be91e4374..87df84e25 100644
--- a/components/anchor/demo/basic.md
+++ b/components/anchor/demo/basic.md
@@ -13,6 +13,7 @@ The simplest usage.
+
diff --git a/components/anchor/demo/customizeHighlight.md b/components/anchor/demo/customizeHighlight.md
new file mode 100644
index 000000000..9ba3e5d40
--- /dev/null
+++ b/components/anchor/demo/customizeHighlight.md
@@ -0,0 +1,31 @@
+
+#### 自定义锚点高亮
+自定义锚点高亮。
+
+
+
+#### Customize the anchor highlight
+Customize the anchor highlight.
+
+
+```tpl
+
+
+
+
+
+
+
+
+
+
+
+```
diff --git a/components/anchor/demo/index.vue b/components/anchor/demo/index.vue
index e94258489..d0c08292b 100644
--- a/components/anchor/demo/index.vue
+++ b/components/anchor/demo/index.vue
@@ -2,6 +2,9 @@
import Basic from './basic';
import Static from './static';
import OnClick from './onClick';
+import CustomizeHighlight from './customizeHighlight';
+import OnChange from './OnChange';
+import TargetOffset from './targetOffset';
import CN from '../index.zh-CN.md';
import US from '../index.en-US.md';
@@ -37,6 +40,9 @@ export default {
+
+
+
diff --git a/components/anchor/demo/onChange.md b/components/anchor/demo/onChange.md
new file mode 100644
index 000000000..2f8a0d5d1
--- /dev/null
+++ b/components/anchor/demo/onChange.md
@@ -0,0 +1,31 @@
+
+#### 监听锚点链接改变
+监听锚点链接改变
+
+
+
+#### Listening for anchor link change
+Listening for anchor link change.
+
+
+```tpl
+
+
+
+
+
+
+
+
+
+
+
+```
diff --git a/components/anchor/demo/static.md b/components/anchor/demo/static.md
index a071d7c00..3c4da5d2e 100644
--- a/components/anchor/demo/static.md
+++ b/components/anchor/demo/static.md
@@ -4,7 +4,7 @@
-#### Static Anchor
+#### Static
Do not change state when page is scrolling.
diff --git a/components/anchor/demo/targetOffset.md b/components/anchor/demo/targetOffset.md
new file mode 100644
index 000000000..f6502b45d
--- /dev/null
+++ b/components/anchor/demo/targetOffset.md
@@ -0,0 +1,34 @@
+
+#### 设置锚点滚动偏移量
+锚点目标滚动到屏幕正中间。
+
+
+
+#### Set Anchor scroll offset
+Anchor target scroll to screen center.
+
+
+```tpl
+
+
+
+
+
+
+
+
+
+
+
+```
diff --git a/components/anchor/index.en-US.md b/components/anchor/index.en-US.md
index 2863a90fd..6176631be 100644
--- a/components/anchor/index.en-US.md
+++ b/components/anchor/index.en-US.md
@@ -2,26 +2,30 @@
### Anchor Props
-| Property | Description | Type | Default |
-| --- | --- | --- | --- |
-| 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 |
-| wrapperClass | The class name of the container | string | - |
-| wrapperStyle | The style of the container | object | - |
+| Property | Description | Type | Default | Version |
+| --- | --- | --- | --- | --- |
+| 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 | |
+| wrapperClass | The class name of the container | string | - | |
+| wrapperStyle | The style of the container | object | - | |
+| getCurrentAnchor | Customize the anchor highlight | () => string | - | 1.5.0 |
+| targetOffset | Anchor scroll offset, default as `offsetTop`, [example](#components-anchor-demo-targetOffset) | number | `offsetTop` | 1.5.0 |
### Events
-| Events Name | Description | Arguments |
-| ----------- | --------------------------------------- | -------------------------------- |
-| click | set the handler to handle `click` event | Function(e: Event, link: Object) |
+| Events Name | Description | Arguments | Version |
+| --- | --- | --- | --- |
+| click | set the handler to handle `click` event | Function(e: Event, link: Object) | |
+| change | Listening for anchor link change | (currentActiveLink: string) => void | | 1.5.0 |
### Link Props
-| Property | Description | Type | Default |
-| -------- | -------------------- | ------------ | ------- |
-| href | target of hyperlink | string | |
-| title | content of hyperlink | string\|slot | |
+| Property | Description | Type | Default | Version |
+| -------- | ----------------------------------------- | ------------ | ------- | ------- |
+| href | target of hyperlink | string | | |
+| title | content of hyperlink | string\|slot | | |
+| target | Specifies where to display the linked URL | string | | |
diff --git a/components/anchor/index.zh-CN.md b/components/anchor/index.zh-CN.md
index 69957fa56..5c448c700 100644
--- a/components/anchor/index.zh-CN.md
+++ b/components/anchor/index.zh-CN.md
@@ -2,26 +2,30 @@
### Anchor Props
-| 成员 | 说明 | 类型 | 默认值 |
-| -------------- | -------------------------------- | ----------------- | ------------ |
-| affix | 固定模式 | boolean | true |
-| bounds | 锚点区域边界 | number | 5(px) |
-| getContainer | 指定滚动的容器 | () => HTMLElement | () => window |
-| offsetBottom | 距离窗口底部达到指定偏移量后触发 | number | |
-| offsetTop | 距离窗口顶部达到指定偏移量后触发 | number | |
-| showInkInFixed | 固定模式是否显示小圆点 | boolean | false |
-| wrapperClass | 容器的类名 | string | - |
-| wrapperStyle | 容器样式 | object | - |
+| 成员 | 说明 | 类型 | 默认值 | 版本 |
+| --- | --- | --- | --- | --- |
+| affix | 固定模式 | boolean | true | |
+| bounds | 锚点区域边界 | number | 5(px) | |
+| getContainer | 指定滚动的容器 | () => HTMLElement | () => window | |
+| offsetBottom | 距离窗口底部达到指定偏移量后触发 | number | | |
+| offsetTop | 距离窗口顶部达到指定偏移量后触发 | number | | |
+| showInkInFixed | 固定模式是否显示小圆点 | boolean | false | |
+| wrapperClass | 容器的类名 | string | - | |
+| wrapperStyle | 容器样式 | object | - | |
+| getCurrentAnchor | 自定义高亮的锚点 | () => string | - | 1.5.0 |
+| targetOffset | 锚点滚动偏移量,默认与 offsetTop 相同,[例子](#components-anchor-demo-targetOffset) | number | `offsetTop` | 1.5.0 |
### 事件
-| 事件名称 | 说明 | 回调参数 |
-| -------- | ---------------------- | -------------------------------- |
-| click | `click` 事件的 handler | Function(e: Event, link: Object) |
+| 事件名称 | 说明 | 回调参数 | 版本 |
+| -------- | ---------------------- | ----------------------------------- | ---- |
+| click | `click` 事件的 handler | Function(e: Event, link: Object) | |
+| change | 监听锚点链接改变 | (currentActiveLink: string) => void | | 1.5.0 |
### Link Props
-| 成员 | 说明 | 类型 | 默认值 |
-| ----- | -------- | ------------ | ------ |
-| href | 锚点链接 | string | |
-| title | 文字内容 | string\|slot | |
+| 成员 | 说明 | 类型 | 默认值 | 版本 |
+| ------ | -------------------------------- | ------------ | ------ | ---- |
+| href | 锚点链接 | string | | |
+| title | 文字内容 | string\|slot | | |
+| target | 该属性指定在何处显示链接的资源。 | string | | |
diff --git a/tests/utils.js b/tests/utils.js
index 9a70f4b23..bb99b93bd 100644
--- a/tests/utils.js
+++ b/tests/utils.js
@@ -25,3 +25,4 @@ export function asyncExpect(fn, timeout) {
}
});
}
+export const sleep = (timeout = 0) => new Promise(resolve => setTimeout(resolve, timeout));