Javascript list cards fadeOut effect
2022-11-26
先看效果
- Card 1
- Card 2
- Card 3
- Card 4
- Card 5
- Card 6
- Card 7
- Card 8
- Card 9
- Card 10
那么怎么实现呢,首先是 HTML 结构:
<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都可以。
样式也随意,只需要注意 ul
的 position
不是能初始值,可以给一个 relative
,否则会影响计算子元素的 offsetTop
。
定义动画函数,并监听 ul
的滚动事件
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
设置子元素大小及透明度样式,那么随着滚动距离越大,子元素就越小,不透明度越来越低
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
属性:
.fadeOutScalingTop {
position: relative;
position: -webkit-sticky;
position: sticky;
-webkit-transform-origin: center top;
transform-origin: center top;
}
为了性能好一些可以给子元素设置上 will-change: transform
属性。
现在动效逻辑就完成了,调用即可:
fadeout(document.querySelector('ul')[0]);
完整代码
/**
* 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>