看网上的视频说到仿钉钉特效,又查阅到css相关的知识,尝试用css动画实现钉钉动效(需要一点js代码,如果使用scss则只需要监听滚动)代码如下
<style>
:root {
--nums: 16;
--boxLength: 50px;
--boxMargin: 18px;
}
#main {
width: 100%;
height: 3000px;
display: flex;
}
#container {
height: 600px;
width: calc(var(--nums) / 2 * (var(--boxMargin) + var(--boxLength)));
margin: auto;
}
#scroll {
position: sticky;
top: calc(50vh - 0.5 * (var(--boxLength) + var(--boxMargin)));
display: flex;
flex-wrap: wrap;
}
#scroll > div:nth-of-type(n) {
margin-right: var(--boxMargin);
margin-bottom: var(--boxMargin);
width: var(--boxLength);
height: var(--boxLength);
}
</style>
<div id="main">
<div id="container">
<div id="scroll">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>
</div>
<script>
const doms = {
boxes: document.getElementById('scroll').children,
scroll: document.getElementById('scroll'),
main: document.getElementById('main'),
container: document.getElementById('container')
}
const pos = {
begin: doms.container.offsetTop,
end: doms.container.offsetTop + doms.container.offsetHeight - doms.scroll.offsetHeight
}
console.dir(doms.scroll)
const animationCreator = (begin, end, attrBegin, attrEnd) => {//起始位置 结束位置 属性的起始 结束
const all = end - begin
const attrAll = attrEnd - attrBegin
//该函数返回一个函数,传入当前的运动到的位置。返回一个动画的进度节点
return (pos) => {
const use = pos - begin
const percent = (use / all).toFixed(2)
const attrFinished = attrBegin + attrAll * percent
return attrFinished.toFixed(2)
}
};
const keyFrame = (index) => {
const boxLeftTop = [doms.boxes[index].offsetLeft, doms.boxes[index].offsetTop]
const middlePos = [doms.boxes[12].offsetLeft - 25, doms.boxes[12].offsetTop - 25]
return `@keyframes move-${index}{
0% {
transform:translate(${middlePos[0] - boxLeftTop[0]}px,${middlePos[1] - boxLeftTop[1]}px)//动画开始时把所有的方块移动到中心点
}
100%{
transform: translate(0px, 0px);//动画结束时回到原点
}
}`
}
const style = document.createElement('style')
for (let i = 0; i < doms.boxes.length; i++) {
style.innerHTML += keyFrame(i)
doms.boxes[i].style.backgroundColor = `hsl(${Math.random() * 360},50%,50%)`
}
document.getElementsByTagName('head')[0].appendChild(style);
const opacityAnimation = animationCreator(pos.begin, pos.end, 0, 100)//生成透明度变化的动画进度
const moveAnimation = animationCreator(pos.begin, pos.end, 0, 1)//生成css动画 delay进度
addEventListener('scroll', (e) => {
const target = doms.scroll.offsetTop
if (target > pos.begin && target < pos.end) {
for (let i = 0; i < doms.boxes.length; i++) {
doms.boxes[i].style.opacity = opacityAnimation(target) + '%'
doms.boxes[i].style.animation = `move-${i} 1s ${-moveAnimation(target)}s paused` //暂停动画,根据delay控制物体的位置。不需要计算每次滑动后所到达的位置,只需要给出初始位置和结束位置,计算delay值即可
}
}
if (target >= pos.end) {
for (let i = 0; i < doms.boxes.length; i++) {
doms.boxes[i].style.opacity = '1'
doms.boxes[i].style.animation = ''
}
}
if (target <= pos.begin) {
for (let i = 0; i < doms.boxes.length; i++) {
doms.boxes[i].style.opacity = 0
doms.boxes[i].style.animation = ''
}
}
})
</script>
生成一个粘性布局,外面包一层大的壳用来控制滑动到特定区域悬停,用js计算滑动的初始范围和结束范围,由此可以根据滑动的进度生成动画的进度。具体实现可以看代码注释