一个元素吸顶效果指令实现

本文介绍基于 vue 的自定义吸顶指令 v-affix 的实现。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
 // @/directives/affix.js

// 导入 dom 处理工具
import dom from '@/utils/dom'

export default {
bind(el){
dom.css(el , {
width: '100%',
zIndex: '1994214'
})
},
inserted( el , {value} ){
if ( !value ) return

const nextEl = el.nextElementSibling, // 后一兄弟节点元素
nextElPaddingTop = Number.parseFloat( dom.getStyleValue( nextEl , 'paddingTop') ), // 后一兄弟节点元素 padding-top 值
elOffsetTop = Number.parseFloat( dom.offset( el ).top ) , // 当前元素距离屏幕顶部的距离值
elHeight = Number.parseFloat(dom.getStyleValue( el , 'height' )); // 当前元素的高度值

// 滚动处理
el._affix_scroll_handle = function(e){
let scroll_instance = window.pageYOffset || window.scrollY,
position = 'static',
paddingTop = nextElPaddingTop;

if ( scroll_instance >= elOffsetTop ){
position = 'fixed'
paddingTop = elHeight + nextElPaddingTop + 'px'
}else{
position = 'static'
paddingTop = nextElPaddingTop + 'px'
}

dom.css( el , {
position,
left: '0px',
top: '0px'
})

dom.css(nextEl , {paddingTop})

}

// 点击处理(点击被绑指令元素时,自动滚动到顶部)
el._affix_click_handle = function(){
window.scrollTo(0,0)
}

window.addEventListener( 'scroll' , el._affix_scroll_handle )
el.addEventListener( 'click', el._affix_click_handle )

},
unbind(el){
el._affix_scroll_handle && window.removeEventListener( 'scroll' , el._affix_scroll_handle )
el._affix_click_handle && el.removeEventListener( 'click' , el._affix_click_handle )
}
}

dom.js工具函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
 // /utils/dom.js
const dom = {
// 设置样式
css( el , styles ){
for ( let k in styles ){
if ( styles.hasOwnProperty(k) )
el.style[k] = styles[k]
}
},
//获取指定样式
getStyleValue(elObj,attr){

var view = elObj.ownerDocument.defaultView;
if (!view || !view.opener) {
view = window;
}

if(elObj.currentStyle){ //IE
return elObj.currentStyle[attr];
}else{
return view.getComputedStyle(elObj)[attr]; //Firefox
}
},

//获取元素偏移量( top , left )
offset(curEle){
var totalLeft = null,totalTop = null,par = curEle.offsetParent;

//首先把自己本身的进行累加
totalLeft += curEle.offsetLeft;
totalTop += curEle.offsetTop;

//只要没有找到body,我们就把父级参照物的边框和偏移量累加
while(par){
if(navigator.userAgent.indexOf("MSIE 8.0") === -1){
//不是标准的ie8浏览器,才进行边框累加
//累加父级参照物边框
totalLeft += par.clientLeft;
totalTop += par.clientTop;
}
//累加父级参照物本身的偏移
totalLeft += par.offsetLeft;
totalTop += par.offsetTop;
par = par.offsetParent;
}

return {left:totalLeft,top:totalTop};
},

}


export default dom;

使用方式

  1. 注册 v-affix 指令。
1
2
3
4
5
6
7
8
// main.js
import Vue from 'vue'
import affix from '@/directives/affix'

Vue.directive( 'affix' , affix )

// 忽略其他代码 ...

  1. 对需要吸顶的元素或组件添加 v-affix 指令即可。
1
2
3
4
5
6
7
<template>
<div>
<some-comp></some-comp>
<some-comp2 v-affix></some-comp2>
<some-comp3></some-comp3>
</div>
</template>

然后,在窗体滚动到一定距离后,吸顶效果就会出现。

如果需要控制吸顶效果的开启状态,可向 v-affix 指令传入一个 Boolean 类型值。

1
2
3
4
5
6
7
<template>
<div>
<some-comp></some-comp>
<some-comp2 v-affix="false"></some-comp2>
<some-comp3></some-comp3>
</div>
</template>

这样,就可以控制 v-affix 的开启状态。