最新 AI 研发里的 memory,至少有三层

2026.05.14

归档 22
分类 0
2022

Javascript list cards fadeOut effect

js实现卡片列表上滑渐隐动效

先看效果

  • Card 1
  • Card 2
  • Card 3
  • Card 4
  • Card 5
  • Card 6
  • Card 7
  • Card 8
  • Card 9
  • Card 10

那么怎么实现呢,首先是 HTML 结构:

1
2
3
4
5
6
7
8
9
10
11
12
<ul>
<li>Card 1</li>
<li>Card 2</li>
<li>Card 3</li>
<li>Card 4</li>
<li>Card 5</li>
<li>Card 6</li>
<li>Card 7</li>
<li>Card 8</li>
<li>Card 9</li>
<li>Card 10</li>
</ul>

只要是个列表就行,随便什么div都可以。

样式也随意,只需要注意 ulposition 不是能初始值,可以给一个 relative,否则会影响计算子元素的 offsetTop

定义动画函数,并监听 ul 的滚动事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function fadeout(el) {
const len = el.childElementCount;
let fadeOutAnimating = false;
function fadeoutEffect() {
fadeOutAnimating = true;
// animation logic
fadeOutAnimating = false;
}
function animate() {
if (!fadeOutAnimating) {
requestAnimationFrame(fadeoutEffect);
}
}
el.addEventListener('scroll', animate);
}

动画逻辑就是当滚动进行时,当子元素超出父元素可视范围时,将滚动距离与子元素的高度做转换,使用 requestAnimationFrame 设置子元素大小及透明度样式,那么随着滚动距离越大,子元素就越小,不透明度越来越低

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function fadeoutEffect() {
fadeOutAnimating = true;
for (let i = 0; i < len; i++) {
if (el.scrollTop > el.children[i].offsetTop) {
const scrolling = el.scrollTop - el.children[i].offsetTop;
const scaling = Math.min(1, Math.max(0, (el.children[i].offsetHeight - scrolling * 0.2) / el.children[i].offsetHeight));
if (!el.children[i].classList.contains('fadeOutScalingTop')) {
el.children[i].classList.add('fadeOutScalingTop');
}
const opacity = scaling * 100 < 96 ? scaling ** 8 : 1;
if (opacity > 0) {
el.children[i].style.opacity = opacity;
el.children[i].style.transform = `translate3d(0, ${scrolling}px, 0) scale(${scaling})`;
}
} else if (el.children[i].classList.contains('fadeOutScalingTop')) {
el.children[i].classList.remove('fadeOutScalingTop');
el.children[i].style.transform = 'none';
el.children[i].style.opacity = 1;
}
}
fadeOutAnimating = false;
}

动画效果中给子元素添加了 className fadeOutScalingTop, 主要是给子元素设置了 transform-origin 属性:

1
2
3
4
5
6
7
.fadeOutScalingTop {
position: relative;
position: -webkit-sticky;
position: sticky;
-webkit-transform-origin: center top;
transform-origin: center top;
}

为了性能好一些可以给子元素设置上 will-change: transform 属性。

现在动效逻辑就完成了,调用即可:

1
fadeout(document.querySelector('ul')[0]);

完整代码

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
/**
* Copyright ThomasChan.
* This source code is licensed under the MIT license.
*/

<style>
#demo {
position: relative;
width: 200px;
height: 400px;
overflow: overlay;
border: 1px solid #dadada;
border-radius: 2px;
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
padding: 0px 15px;
}
#demo li {
display: flex;
justify-content: center;
align-items: center;
list-style: none;
margin: 15px auto;
height: 100px;
border: 1px solid #dadada;
border-radius: 2px;
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
will-change: transform;
background-color: white;
}
.fadeOutScalingTop {
position: relative;
position: -webkit-sticky;
position: sticky;
-webkit-transform-origin: center top;
transform-origin: center top;
}
</style>
<ul id="demo">
<li>Card 1</li>
<li>Card 2</li>
<li>Card 3</li>
<li>Card 4</li>
<li>Card 5</li>
<li>Card 6</li>
<li>Card 7</li>
<li>Card 8</li>
<li>Card 9</li>
<li>Card 10</li>
</ul>
<script>
function fadeout(el) {
const len = el.childElementCount;
let fadeOutAnimating = false;
const childSize = 100;
function fadeoutEffect() {
fadeOutAnimating = true;
for (let i = 0; i < len; i++) {
if (el.scrollTop > el.children[i].offsetTop) {
const scrolling = el.scrollTop - el.children[i].offsetTop;
const scaling = Math.min(1, Math.max(0, (childSize - scrolling * 0.2) / childSize));
const opacity = scaling * 100 < 96 ? scaling ** 8 : 1;
if (opacity > 0) {
if (!el.children[i].classList.contains('fadeOutScalingTop')) {
el.children[i].classList.add('fadeOutScalingTop');
}
el.children[i].style.opacity = opacity;
el.children[i].style.transform = `translate3d(0, ${scrolling}px, 0) scale(${scaling})`;
}
} else if (el.children[i].classList.contains('fadeOutScalingTop')) {
el.children[i].classList.remove('fadeOutScalingTop');
el.children[i].style.transform = 'none';
el.children[i].style.opacity = 1;
}
}
fadeOutAnimating = false;
}
function animate() {
if (!fadeOutAnimating) {
requestAnimationFrame(fadeoutEffect);
}
}
el.addEventListener('scroll', animate);
}
window.onload = function() {
fadeout(document.querySelector('#demo'));
}
</script>