Skip to content

Commit

Permalink
feat(Affix): implement affix by {position: absolute}
Browse files Browse the repository at this point in the history
  • Loading branch information
jinchanz committed Dec 4, 2018
1 parent 90f46f6 commit da3e3ec
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 27 deletions.
58 changes: 58 additions & 0 deletions docs/affix/demo/absolute-affix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# 启用绝对布局

- order: 3

可以通过 `container` 属性设置 Affix 组件需要监听其滚动事件的元素,该属性接收一个函数作为参数,默认为 `() => window`
设置 absoluteAffix 为 true,通过 absolute 布局实现组件固定。

:::lang=en-us
# Container

- order: 3

Change the default container by passing a function to `container`.

:::

---

````jsx
import { Affix, Button } from '@alifd/next';

class Demo extends React.Component {

_containerRefHandler(ref) {
this.container = ref;
}

render() {
return (
<div className="custom-affix-container" ref={this._containerRefHandler.bind(this)}>
<div className="affix-wrapper">
<Affix container={() => this.container} offsetTop={0} absoluteAffix={true}>
<Button type="secondary">Affixed Button</Button>
</Affix>
</div>
</div>
);
}
}



ReactDOM.render(<Demo />, mountNode);
````

````css
.custom-affix-container {
height: 150px;
overflow-y: scroll;
width: 50px;
background: url(https://img.alicdn.com/tfs/TB1AbJXSpXXXXXJXpXXXXXXXXXX-32-32.jpg) repeat 50% 50%;
}

.custom-affix-container .affix-wrapper {
padding-top: 100px;
height: 500px;
}
````
4 changes: 2 additions & 2 deletions docs/affix/demo/on-affix.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# onAffix

- order: 3
- order: 4

可以通过传入 `onAffix` 的事件回调函数来监听元素是否发生了固钉状态。该函数会在状态变化时返回固钉状态。

:::lang=en-us
# onAffix

- order: 3
- order: 4

Listening the affix state by `onAffix`.

Expand Down
11 changes: 6 additions & 5 deletions docs/affix/index.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ The Affix component allows an element to become affixed (locked) to an area on t
### Affix

| Param | Description | Type | Default Value |
| ------------ | ------------------------------------------------------------------------------------------------------------------- | -------- | ------------ |
| container | The container for listening scroll events<br><br>**signature**:<br>Function() => ReactElement<br>**return**:<br>{ReactElement} the instance of container<br> | Function | () => window |
| offsetTop | Offset from top when event triggers | Number | - |
| offsetBottom | Offset from bottom when event triggers | Number | - |
| onAffix | Callback when affix event triggers <br><br>**signature**:<br>Function(isAffixed: Boolean) => void<br>**parameters**:<br>_if element is affixed_: {Boolean} null | Function | func.noop |
| ------------- | ------------------------------------------------------------------------------------------------------------------- | -------- | ------------ |
| container | The container for listening scroll events<br><br>**signature**:<br>Function() => ReactElement<br>**return**:<br>{ReactElement} the instance of container<br> | Function | () => window |
| offsetTop | Offset from top when event triggers | Number | - |
| offsetBottom | Offset from bottom when event triggers | Number | - |
| onAffix | Callback when affix event triggers <br><br>**signature**:<br>Function(isAffixed: Boolean) => void<br>**parameters**:<br>_if element is affixed_: {Boolean} null | Function | func.noop |
| absoluteAffix | Enable absolute position | Boolean | - |
13 changes: 7 additions & 6 deletions docs/affix/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@

### Affix

| 参数 | 说明 | 类型 | 默认值 |
| ------------ | ------------------------------------------------------------------------------------------------------------------- | -------- | ------------ |
| container | 设置 Affix 需要监听滚动事件的容器元素<br><br>**签名**:<br>Function() => ReactElement<br>**返回值**:<br>{ReactElement} 目标容器元素的实例<br> | Function | () => window |
| offsetTop | 距离窗口顶部达到指定偏移量后触发 | Number | - |
| offsetBottom | 距离窗口底部达到制定偏移量后触发 | Number | - |
| onAffix | 当元素的样式发生固钉样式变化时触发的回调函数<br><br>**签名**:<br>Function(元素是否被固钉: Boolean) => void<br>**参数**:<br>_元素是否被固钉_: {Boolean} null | Function | func.noop |
| 参数 | 说明 | 类型 | 默认值 |
| ------------- | ------------------------------------------------------------------------------------------------------------------- | -------- | ------------ |
| container | 设置 Affix 需要监听滚动事件的容器元素<br><br>**签名**:<br>Function() => ReactElement<br>**返回值**:<br>{ReactElement} 目标容器元素的实例<br> | Function | () => window |
| offsetTop | 距离窗口顶部达到指定偏移量后触发 | Number | - |
| offsetBottom | 距离窗口底部达到制定偏移量后触发 | Number | - |
| onAffix | 当元素的样式发生固钉样式变化时触发的回调函数<br><br>**签名**:<br>Function(元素是否被固钉: Boolean) => void<br>**参数**:<br>_元素是否被固钉_: {Boolean} null | Function | func.noop |
| absoluteAffix | 是否启用绝对布局实现 affix | Boolean | - |
76 changes: 62 additions & 14 deletions src/affix/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ class Affix extends React.Component {
* @param {Boolean} 元素是否被固钉
*/
onAffix: PropTypes.func,
/**
* 是否启用绝对布局实现 affix
* @param {Boolean} 是否启用绝对布局
*/
absoluteAffix: PropTypes.bool,
className: PropTypes.string,
style: PropTypes.object,
children: PropTypes.any,
Expand All @@ -49,11 +54,11 @@ class Affix extends React.Component {
}

componentDidMount() {
const { container } = this.props;
const { container, absoluteAffix } = this.props;
this._updateNodePosition();
// wait for parent rendered
this.timeout = setTimeout(() => {
this._setEventHandlerForContainer(container);
this._setEventHandlerForContainer(container, absoluteAffix);
});
}

Expand All @@ -62,25 +67,24 @@ class Affix extends React.Component {
clearTimeout(this.timeout);
this.timeout = null;
}
const { container } = this.props;
this._removeEventHandlerForContainer(container);
const { container, absoluteAffix } = this.props;
this._removeEventHandlerForContainer(container, absoluteAffix);
}

_setEventHandlerForContainer(getContainer) {
_setEventHandlerForContainer(getContainer, absoluteAffix = false) {
const container = getContainer();
if (!container) {
return;
}

events.on(container, 'scroll', this._updateNodePosition);
events.on(container, 'resize', this._updateNodePosition);
events.on(container, 'scroll', absoluteAffix ? this._updateNodeAbsolutePosition : this._updateNodePosition, false);
events.on(container, 'resize', absoluteAffix ? this._updateNodeAbsolutePosition : this._updateNodePosition, false);
}

_removeEventHandlerForContainer(getContainer) {
_removeEventHandlerForContainer(getContainer, absoluteAffix = false) {
const container = getContainer();
if (container) {
events.off(container, 'scroll', this._updateNodePosition);
events.off(container, 'resize', this._updateNodePosition);
events.off(container, 'scroll', absoluteAffix ? this._updateNodeAbsolutePosition : this._updateNodePosition);
events.off(container, 'resize', absoluteAffix ? this._updateNodeAbsolutePosition : this._updateNodePosition);
}
}

Expand Down Expand Up @@ -126,7 +130,51 @@ class Affix extends React.Component {
this._setAffixStyle(null);
this._setContainerStyle(null);
}
}
};

_updateNodeAbsolutePosition = () => {
const { container } = this.props;
const affixContainer = container();

if (!affixContainer) {
return false;
}
const containerScrollTop = getScroll(affixContainer, true); // 容器在垂直位置上的滚动 offset
const affixOffset = this._getOffset(this.affixNode, affixContainer); // 目标节点当前相对于容器的 offset
const containerHeight = getNodeHeight(affixContainer); // 容器的高度
const affixHeight = this.affixNode.offsetHeight;

const affixMode = this.affixMode;
if (affixMode.top && containerScrollTop > affixOffset.top - affixMode.offset) {
// affix top
this._setAffixStyle({
position: 'absolute',
top: containerScrollTop - (affixOffset.top - affixMode.offset),
width: affixOffset.width,
}, true);
this._setContainerStyle({
width: affixOffset.width,
height: affixHeight,
position: 'relative',
});
} else if (affixMode.bottom && containerScrollTop < affixOffset.top + affixHeight + affixMode.offset - containerHeight) {
// affix bottom
this._setAffixStyle({
position: 'absolute',
top: containerScrollTop - (affixOffset.top + affixHeight + affixMode.offset - containerHeight),
width: affixOffset.width,
height: affixHeight,
}, true);
this._setContainerStyle({
width: affixOffset.width,
height: affixHeight,
position: 'relative',
});
} else {
this._setAffixStyle(null);
this._setContainerStyle(null);
}
};

_getAffixMode() {
const { offsetTop, offsetBottom } = this.props;
Expand All @@ -150,7 +198,7 @@ class Affix extends React.Component {
return affixMode;
}

_setAffixStyle(affixStyle) {
_setAffixStyle(affixStyle, affixed = false) {
if (obj.shallowEqual(affixStyle, this.state.style)) {
return;
}
Expand All @@ -161,7 +209,7 @@ class Affix extends React.Component {

const { onAffix } = this.props;

if (affixStyle && affixStyle.position === 'fixed') {
if ((affixStyle && affixStyle.position === 'fixed') || affixed) {
onAffix(true);
} else if (!affixStyle) {
onAffix(false);
Expand Down

0 comments on commit da3e3ec

Please sign in to comment.