在vue的项目中经常用到固钉,但是 element-ui 上并没有提供这样的组件可供使用,ant-design-vue 有提供,总不能为了这一个组件再去引入一个组件库吧
下面是一个封装好的 affix 组件,可以放到项目中直接使用
Affix.vue
<template>
<div class="affix-placeholder" :style="wrapStyle">
<div :class="{'affix': affixed}" :style="styles">
<slot></slot>
</div>
</div>
</template>
<script>
/**
* @file Affix.vue
* @author v_shenjieping@baidu.com
* @date 2018-12-11 10:09:50
*/
export default {
props: {
offset: {
type: Number,
default: 0
},
onAffix: {
type: Function,
default() {}
},
boundary: {
type: String,
default: ''
}
},
data() {
return {
affixed: false,
styles: {},
affixedClientHeight: 0,
wrapStyle: {}
};
},
methods: {
getScroll(w, top) {
let ret = w[`page${(top ? 'Y' : 'X')}Offset`];
const method = `scroll${top ? 'Top' : 'Left'}`;
if (typeof ret !== 'number') {
const d = w.document;
// ie6,7,8 standard mode
ret = d.documentElement[method];
if (typeof ret !== 'number') {
// quirks mode
ret = d.body[method];
}
}
return ret;
},
getOffset(element) {
const rect = element.getBoundingClientRect();
const body = document.body;
const clientTop = element.clientTop || body.clientTop || 0;
const clientLeft = element.clientLeft || body.clientLeft || 0;
// const clientHeight = element.clientHeight || 0;
const scrollTop = this.getScroll(window, true);
const scrollLeft = this.getScroll(window);
return {
top: rect.bottom + scrollTop - clientTop - this.affixedClientHeight,
left: rect.left + scrollLeft - clientLeft
};
},
handleScroll() {
const scrollTop = this.getScroll(window, true) + this.offsets; // handle setting offset
const elementOffset = this.getOffset(this.$el);
if (!this.affixed && scrollTop > elementOffset.top) {
this.affixed = true;
this.styles = {
top: `${this.offsets}px`,
left: `${elementOffset.left}px`,
`${this.$el.offsetWidth}px`
};
this.onAffix(this.affixed);
}
// if setting boundary
if (this.boundary && scrollTop > elementOffset.top) {
const el = document.getElementById(this.boundary.slice(1));
if (el) {
const boundaryOffset = this.getOffset(el);
if ((scrollTop + this.offsets) > boundaryOffset.top) {
const top = scrollTop - boundaryOffset.top;
this.styles.top = `-${top}px`;
}
}
}
if (this.affixed && scrollTop < elementOffset.top) {
this.affixed = false;
this.styles = {};
this.onAffix(this.affixed);
}
if (this.affixed && this.boundary) {
const el = document.getElementById(this.boundary.slice(1));
if (el) {
const boundaryOffset = this.getOffset(el);
if ((scrollTop + this.offsets) <= boundaryOffset.top) {
this.styles.top = 0;
}
}
}
}
},
computed: {
offsets() {
if (this.boundary) {
return 0;
}
return this.offset;
}
},
mounted() {
this.affixedClientHeight = this.$el.children[0].clientHeight;
this.wrapStyle = {height: `${this.affixedClientHeight}px`};
window.addEventListener('scroll', this.handleScroll);
window.addEventListener('resize', this.handleScroll);
},
beforeDestroy() {
window.removeEventListener('scroll', this.handleScroll);
window.removeEventListener('resize', this.handleScroll);
}
};
</script>
<style lang="sass">
.affix
position: fixed
</style>
使用方法也是非常简单
test.vue
<template>
<div class="test">
<affix>
<div>这是一个固钉组件</div>
</affix>
<affix :offset="40">
<div>这是一个固钉组件</div>
</affix>
</div>
</template>
<script>
import Affix from '@/components/Affix';
export default {
name: 'test',
components: {
Affix
}
};
</script>
API
| 参数 | 说明 | 类型 | 默认值 |
| offset | 距离窗口顶部达到指定偏移量后触发 | Number | 0 |
| boundary | 设置 Affix 的活动范围,值为affix上级元素的id(可以是父元素,也可以是父元素的父元素...) | String(#parent) | |
| on-affix | 固定状态改变时触发的回调函数 | Function(affixed) | 无 |