钉钉官网的滚动动画(详解)

钉钉官网的滚动动画

可以将滚动效果看做一个坐标轴,这时候横坐标就不是时间了,而是滚动的距离,后续会写一个函数,将滚动的距离(也就是当滚动到某一个位置的时候,滚动距离的值)传入函数,就返回给你一个value,这个value就是当你滚动到这个位置的时候,里面包裹的动画该改变的一些属性,比如opacit,transform,当然这些该变化的值又得靠一些函数计算获得。这时候就让我想起了高中逝去的数学了,全靠函数获取动态的值。

现在介绍一下这个图中的四个变量的值代表啥

  1. scrollStart:滚动到哪一个位置的时候,触发动画,然后开始斜线的计算拿到不断变化的属性值,从而改变css样式,这个我设置的是头部完全消失就触发动画
  2. scrollEnd:滚动到哪一个位置的时候,停止触发动画,再滑动就一直保持那个样子,这时候那几个属性已经都是死的了,这个我设置的是黑色部分向上走的时候停止触发动画
  3. valueStart:刚开始的透明度和偏移量
  4. valueEnd:结束后透明度为1,偏移量自己可以设置

好啦,HTML和CSS就不用多介绍了吧~~~~

image-20230514134228299

HTML

data-order="0"是控制方块出来的顺序的

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>ding talk</title><link rel="stylesheet" href="./index.css" /></head><body><div class="header">HEADER</div><div class="playground"><div class="animation-container"><div class="list"><div data-order="0" class="list-item"></div><div data-order="1" class="list-item"></div><div data-order="2" class="list-item"></div><div data-order="3" class="list-item"></div><div data-order="2" class="list-item"></div><div data-order="1" class="list-item"></div><div data-order="0" class="list-item"></div><div data-order="0" class="list-item"></div><div data-order="1" class="list-item"></div><div data-order="2" class="list-item"></div><div data-order="3" class="list-item"></div><div data-order="2" class="list-item"></div><div data-order="1" class="list-item"></div><div data-order="0" class="list-item"></div></div></div></div><div class="footer">FOOTER</div><script src="./index.js"></script></body>
</html>

CSS

* {margin: 0;padding: 0;box-sizing: border-box;
}
html {overflow-x: hidden;
}
.header,
.footer {height: 100vh;display: flex;justify-content: center;align-items: center;font-size: 4em;
}
.playground {height: 4000px;background: #000;
}.animation-container {position: sticky;height: 100vh;top: 0;
}.list {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);width: 80%;aspect-ratio: 2/1;border-radius: 10px;display: grid;grid-template-columns: repeat(7, 1fr);grid-template-rows: repeat(2, 1fr);place-items: center;
}.list-item {width: 60%;aspect-ratio: 1/1;background: #fff;border-radius: 10px;
}.list-item:nth-child(3n + 1) {background: linear-gradient(#3e90f7, #246bf6);
}
.list-item:nth-child(3n + 2) {background: linear-gradient(#53b655, #469c50);
}
.list-item:nth-child(3n + 3) {background: linear-gradient(#f3a93c, #f4ad3d);
}

JS

注意:看这里看这里,一定要注意,我已经写好了写js代码当时的顺序,请大家一定要按着这个顺序看我写的函数,不然脑袋很懵喔,按照顺序看思路很清晰

图一

image-20230514141321479

图二

image-20230514145945205

图三

image-20230514152605106

//***********第一步看这里
//拿到三个dom元素
const items = document.querySelectorAll('.list-item');
const playGround = document.querySelector('.playground');
const list = document.querySelector('.list');
//这个就是我上面提及到的函数,传入横坐标的scroll值给一个value,return出来的就是value值
function createAnimation(xStart, xEnd, yStart, yEnd) {return function (x) {//第一阶段if (x <= xStart) {return yStart;}//第三阶段if (x >= xEnd) {return yEnd;}//斜线部分(高中函数知识,也可以理解为yStart + ((x - xStart) * (yEnd - yStart) / (xEnd - xStart)) )return yStart + ((x - xStart) / (xEnd - xStart)) * (yEnd - yStart);};
}
//上面这个函数可以这样死调用:const p = createAnimation(100,1000,0,1)
//p(100),传入的是100,就返回一个value值是1,但是这样太死了,我们滚动的时候怎么会知道传入什么值呢,所以可以想到这里的100应该又得是一个变量,而不是一个死的值//***********第二步看这里
//做一个数据结构,也就是一个映射
//如最上面图所示:
const animationMap = new Map();//***********第五步看这里
//完善这个animationMap
function updateAnimationMap() {//先清空map,因为考虑到缩放浏览器页面大小啥的需要一直计算,这里也可以不写animationMap.clear();//防止没有方块if (items.length === 0) {return;}//拿到蓝色部分的矩形区域const playGroundRect = playGround.getBoundingClientRect();const scrollY = window.scrollY;//如上图2所示,计算出的该触发动画的滚动值 也就是scrollStart值const playGroundTop = playGroundRect.top + scrollY;//如上图3所示,结束距离,scrollEndconst playGroundBottom = playGroundRect.bottom + scrollY - window.innerHeight;//方块的矩形区域const listRect = list.getBoundingClientRect();//循环所以items,因为是每个小方块在动态变化样式for (let i = 0; i < items.length; i++) {const item = items[i];//拿到order,也就是html中写的出来的顺序,相差600个滚动位置出来下一组方块const scrollStart = playGroundTop + item.dataset.order * 600;const scrollEnd = playGroundBottom;//拿到方块的宽高左右距离来使方块处于一直居中状态const itemWidth = item.clientWidth;const itemHeight = item.clientHeight;const itemLeft = item.offsetLeft;const itemTop = item.offsetTop;//动态计算opacity属性值const opacityAnimation = createAnimation(scrollStart, scrollEnd, 0, 1);//动态计算scale属性值const scaleAnimation = createAnimation(scrollStart, scrollEnd, 0.5, 1);//动态计算translateX属性值const translateXAnimation = createAnimation(scrollStart,scrollEnd,listRect.width / 2 - itemLeft - itemWidth / 2,0);//动态计算translateY属性值const translateYAnimation = createAnimation(scrollStart,scrollEnd,listRect.height / 2 - itemTop - itemHeight / 2,0);//之前第三步中说到键是dom元素,值就是dom元素指向的那整个对象,所以要把这整个对象加到map中去并且是个函数const animations = {opacity: function (scrollY) {//return出去的属性值,为什么要写函数,因为这个属性值也要根据动态计算return opacityAnimation(scrollY);},transform: function (scrollY) {const scaled = scaleAnimation(scrollY);const x = translateXAnimation(scrollY);const y = translateYAnimation(scrollY);//用模板字符串插入到css中return `translate(${x}px, ${y}px) scale(${scaled})`;},};//将每个item加到animationMap中去animationMap.set(item, animations);}
}
updateAnimationMap();//***********第三步看这里
//更新我们的样式
function updateStyles() {//拿到当前我们滚动的位置const scrollY = window.scrollY;//循环map,也就是图1里面的键值对,键是dom元素,值就是dom元素指向的那整个对象,这里我写的item和animations。//提示:这里的时候还没有得出animationMap,只是先这样写着,让自己思路清晰,一步一步来,所以得去这个函数上面继续完善animationMap函数for (const [item, animations] of animationMap) {//设置每一个dom元素的属性//遍历对象,设置dom元素样式for (const prop in animations) {//提示:别忘记animations对象里面的属性值是一个函数喔,所以得传一个参数scrollYitem.style[prop] = animations[prop](scrollY);}}
}
updateStyles();
//***********第四步看这里
//监听滚动事件,调用updateStyles函数
window.addEventListener('scroll', updateStyles);window.addEventListener('resize', () => {updateAnimationMap();updateStyles();
});

呜呜呜,都学到这儿了,给博主点个赞,激励一下前端人员的奋斗吧呜呜呜~~~


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部