Animación de desplazamiento del sitio web oficial de DingTalk (explicación detallada)

Animación de desplazamiento del sitio web oficial de DingTalk

El efecto de desplazamiento se puede considerar como un eje de coordenadas. En este momento, la abscisa no es el tiempo, sino la distancia de desplazamiento. Más adelante, se escribirá una función para calcular la distancia de desplazamiento (es decir, cuando se desplaza a una determinada posición, (el valor de distancia de desplazamiento) se pasa a la función y se le devolverá un valor. Este valor son algunas propiedades que la animación envuelta en él debe cambiar cuando se desplaza a esta posición, como opacit, transform. Por supuesto , estos valores que deben cambiarse dependen de algunos Obtenido mediante el cálculo de la función. En ese momento, me recordó las matemáticas perdidas en la escuela secundaria, confiando en funciones para obtener valores dinámicos.

Ahora permítanme presentarles lo que representan los valores de las cuatro variables en esta figura.

  1. scrollStart: al desplazarse a qué posición, se activa la animación y luego se inicia el cálculo de la barra para obtener el valor del atributo cambiante, cambiando así el estilo CSS. Lo configuré para que se active la animación cuando la cabeza desaparezca por completo.
  2. scrollEnd: al desplazarse a qué posición, deje de activar la animación y luego deslice para mantenerla así. En este momento, esos atributos ya están muertos. Lo configuré para que deje de activar la animación cuando la parte negra suba.
  3. valueStart: la transparencia inicial y el desplazamiento
  4. valueEnd: después del final, la transparencia es 1 y el desplazamiento se puede configurar usted mismo

Bueno, no es necesario introducir HTML y CSS ~~~~

imagen-20230514134228299

HTML

data-order="0" es para controlar el orden en que salen los bloques

<!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

Nota: Mire aquí y aquí, debe prestar atención, ya escribí el orden de escritura del código js en ese momento, asegúrese de leer las funciones que escribí en este orden, de lo contrario su cabeza estará muy confundida y la idea Es muy claro cuando sigues el orden.

Figura 1

imagen-20230514141321479

Figura II

imagen-20230514145945205

Figura tres

imagen-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所示,结束距离,scrollEnd
  const 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对象里面的属性值是一个函数喔,所以得传一个参数scrollY
      item.style[prop] = animations[prop](scrollY);
    }
  }
}
updateStyles();
//***********第四步看这里
//监听滚动事件,调用updateStyles函数
window.addEventListener('scroll', updateStyles);

window.addEventListener('resize', () => {
    
    
  updateAnimationMap();
  updateStyles();
});

Woohoo, he aprendido todo aquí, dale el visto bueno al blogger e inspiro la lucha del personal de front-end ~~~

Supongo que te gusta

Origin blog.csdn.net/qq_53461589/article/details/130669674
Recomendado
Clasificación