# 滚动条元素定位
# 父子容器的滚动
const scrollTo = (params = {}) => {
const baseConfig = {
duration: 500, // 滚动的动画的时间
scroller: "", // 滚动容器,默认为 body(请使用id,保证唯一,使用的方法querySelector)
target: "", // 滚动到指定位置的元素,
extraDistance: 0, // 设置额外偏移量
selector: document.querySelector,
};
const config = {
...baseConfig,
...params,
};
const { target, duration, scroller, selector, extraDistance } = config;
const documentSelector = selector.bind(document);
const scrollerDom = documentSelector(scroller);
const targetDom = documentSelector(target);
const scrollerDomRect = scrollerDom.getBoundingClientRect();
const targetDomRect = targetDom.getBoundingClientRect();
if (targetDom) {
const { scrollTop } = getScrollTop(scroller, documentSelector);
const distanceWithScroll = targetDomRect.top - scrollerDomRect.top;
// 需要减去滚动容器到顶部的距离,默认滚动容器是 body ,scrollerOffsetTop 的值为 0
const offsetTop = distanceWithScroll + scrollTop;
let start;
let timeOff = false;
const scrollFrame = (timestramp) => {
if (start === undefined) {
start = timestramp;
}
const elapsed = timestramp - start;
const offset = getOffset({
scrollTop,
offsetTop,
elapsed,
duration,
extraDistance,
});
if (duration - elapsed >= 0) {
window.requestAnimationFrame(scrollFrame);
scolling(scroller, offset + extraDistance, documentSelector);
} else {
// 最后一帧因为时间判断可能不会执行,补上最后一帧
if (!timeOff) {
scolling(scroller, offsetTop + extraDistance, documentSelector);
timeOff = true;
}
}
};
window.requestAnimationFrame(scrollFrame);
}
};
const getScrollTop = (scrollerTag, documentSelector) => {
let scrollTop;
let scrollerOffsetTop = 0; // 滚动容器距离 body 顶部的距离
const ele = documentSelector(scrollerTag);
if (ele) {
scrollTop = ele.scrollTop;
scrollerOffsetTop = ele.offsetTop;
} else {
scrollTop =
document.documentElement.scrollTop ||
window.pageYOffset ||
document.body.scrollTop;
}
return { scrollTop, scrollerOffsetTop };
};
const getOffset = (data) => {
const { scrollTop, offsetTop, elapsed, duration } = data;
let offset;
// 判断是向下还是向上滚动
if (scrollTop > offsetTop) {
offset = scrollTop - (elapsed / duration) * (scrollTop - offsetTop);
} else {
offset = scrollTop + (elapsed / duration) * (offsetTop - scrollTop);
}
return offset;
};
const scolling = (scrollerTag, offset, documentSelector) => {
const scroller = getScroller(scrollerTag, documentSelector);
scroller.scrollTo(0, offset);
};
const getScroller = (scrollerTag, documentSelector) =>
documentSelector(scrollerTag) || window;
export default scrollTo;
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# 改版
警告
上面的方法存在一一些限制:
1.限制使用了 getElementById
选择器
2.上面计算距离使用的是目标元素与父容器件的方法,如果目标元素为更深层次(非直接父子关系),那么计算会失效
考虑到上面两个问题,解决:
1.使用传入的选择器,可自主选择选择器
2.使用 getBoundingClientRect
计算目标元素的偏移
const scrollToWithDeepDom = (params = {}) => {
const baseConfig = {
duration: 500, // 滚动的动画的时间
scroller: "", // 滚动容器,默认为 body(请使用id,保证唯一,使用的方法querySelector)
target: "", // 滚动到指定位置的元素,
extraDistance: 0, // 设置额外偏移量
selector: document.querySelector,
};
const config = {
...baseConfig,
...params,
};
const { target, duration, scroller, selector, extraDistance } = config;
const documentSelector = selector.bind(document);
const scrollerDom = documentSelector(scroller);
const targetDom = documentSelector(target);
const scrollerDomRect = scrollerDom.getBoundingClientRect();
const targetDomRect = targetDom.getBoundingClientRect();
if (targetDom) {
const { scrollTop } = getScrollTop(scroller, documentSelector);
const distanceWithScroll = targetDomRect.top - scrollerDomRect.top;
// 需要减去滚动容器到顶部的距离,默认滚动容器是 body ,scrollerOffsetTop 的值为 0
const offsetTop = distanceWithScroll + scrollTop;
let start;
let timeOff = false;
const scrollFrame = (timestramp) => {
if (start === undefined) {
start = timestramp;
}
const elapsed = timestramp - start;
const offset = getOffset({
scrollTop,
offsetTop,
elapsed,
duration,
extraDistance,
});
if (duration - elapsed >= 0) {
window.requestAnimationFrame(scrollFrame);
scolling(scroller, offset + extraDistance, documentSelector);
} else {
// 最后一帧因为时间判断可能不会执行,补上最后一帧
if (!timeOff) {
scolling(scroller, offsetTop + extraDistance, documentSelector);
timeOff = true;
}
}
};
window.requestAnimationFrame(scrollFrame);
}
};
const getScrollTop = (scrollerTag, documentSelector) => {
let scrollTop;
let scrollerOffsetTop = 0; // 滚动容器距离 body 顶部的距离
const ele = documentSelector(scrollerTag);
if (ele) {
scrollTop = ele.scrollTop;
scrollerOffsetTop = ele.offsetTop;
} else {
scrollTop =
document.documentElement.scrollTop ||
window.pageYOffset ||
document.body.scrollTop;
}
return { scrollTop, scrollerOffsetTop };
};
const getOffset = (data) => {
const { scrollTop, offsetTop, elapsed, duration } = data;
let offset;
// 判断是向下还是向上滚动
if (scrollTop > offsetTop) {
offset = scrollTop - (elapsed / duration) * (scrollTop - offsetTop);
} else {
offset = scrollTop + (elapsed / duration) * (offsetTop - scrollTop);
}
return offset;
};
const scolling = (scrollerTag, offset, documentSelector) => {
const scroller = getScroller(scrollerTag, documentSelector);
scroller.scrollTo(0, offset);
};
const getScroller = (scrollerTag, documentSelector) =>
documentSelector(scrollerTag) || window;
export default scrollToWithDeepDom;
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
使用
scrollToWithDeepDom({
scroller: "content",
target: `item-1`,
selector: document.getElementById,
});
1
2
3
4
5
2
3
4
5