html文档结构
<div>
<div class="header">
<h1>HEADER</h1>
</div>
<div class="palyground">
<div class="list">
<div class="list-item"></div>
<div class="list-item"></div>
<div class="list-item"></div>
<div class="list-item"></div>
<div class="list-item"></div>
<div class="list-item"></div>
<div class="list-item"></div>
<div class="list-item"></div>
<div class="list-item"></div>
<div class="list-item"></div>
<div class="list-item"></div>
<div class="list-item"></div>
<div class="list-item"></div>
<div class="list-item"></div>
</div>
</div>
<div class="footer"></div>
</div>
js
function createAnimation(scrollStart, scrollEnd, startValue, endValue) {
return function (x) {
if (x < scrollStart) {
return startValue
}
if (x > scrollEnd) {
return endValue
}
const progress = (x - scrollStart) / (scrollEnd - scrollStart);
return startValue + (endValue - startValue) * progress
}
}
const animationMap = new Map()
const items = document.querySelectorAll('.list-item')
const palyground = document.querySelector('.palyground')
const list = document.querySelector('.list')
function getDomAnimation(scrollStart, scrollEnd, dom) {
const opacityAnimation = createAnimation(scrollStart, scrollEnd, 0, 1)
const opacity = function (x) {
return opacityAnimation(x)
}
const scaleAnimation = createAnimation(scrollStart, scrollEnd, 0.5, 1)
const { clientWidth, clientHeight, offsetTop, offsetLeft } = dom
const rect = list.getBoundingClientRect()
const xAnimation = createAnimation(scrollStart, scrollEnd,
rect.width / 2 - clientWidth / 2 - offsetLeft
, 0);
const yAnimation = createAnimation(scrollStart, scrollEnd,
rect.height / 2 - clientHeight / 2 - offsetTop
, 0);
const transform = function (x) {
return `translate(${xAnimation(x)}px, ${yAnimation(x)}px) scale(${scaleAnimation(x)})`
}
return {
opacity,
transform
}
}
function updateMap() {
const palygroundRect = palyground.getBoundingClientRect()
const scrollY = window.scrollY
const scrollStart = palygroundRect.top + scrollY
const scrollEnd = palygroundRect.bottom + scrollY - window.innerHeight
for (const item of items) {
animationMap.set(item, getDomAnimation(scrollStart, scrollEnd, item))
}
}
updateMap()
function updateStyles() {
const scrollY = window.scrollY
for (const [dom, animations] of animationMap) {
for (const prop in animations) {
const value = animations[prop](scrollY);
dom.style[prop] = value
}
}
}
updateStyles()
window.addEventListener('scroll', updateStyles)
css
.header {
height: 800px;
background-color: #f2f2f2;
display: flex;
align-items: center;
justify-content: center;
}
.list {
position: -webkit-sticky;
position: sticky;
top: 0;
background-color: rgb(0, 0, 0);
height: 500px;
z-index: 100;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
padding: 10% 15%;
}
.list-item {
width: 50px;
height: 50px;
border-radius: 50%;
background-color: aquamarine;
margin: 40px;
}
.palyground {
height: 1500px;
background-color: #000000;
padding: 20px;
}
.footer {
height: 3000px;
}