仿钉钉官网滚动特效

1、前言     

刷抖音的时候,看到某视频作者分享的钉钉官网滚动特效,觉得挺不错的,那位老师没有使用css,而是使用js来做的。个人觉得很不错,便自己练习并记录。

2、感兴趣的可以去Gitee去拉下来,跑起来运行看效果

Gitee仿钉钉官网滚动特效

3、废话不多说,直接上代码

<template>
  <div class="main-conteiner">
    <div class="logo-title">
      <div class="logo">
        <img src="https://gw.alicdn.com/imgextra/i2/O1CN01kEuOaQ1OQe6INmAUY_!!6000000001700-55-tps-180-180.svg" alt="" />
      </div>
      <div class="title">
        <img src="https://gw.alicdn.com/imgextra/i2/O1CN01rC0EJs1ZZJR2WHfdQ_!!6000000003208-2-tps-1880-272.png" alt="" />
      </div>
    </div>
    <div class="playground" ref="playground">
      <div class="animation-container" ref="animationRef">
        <div class="list" ref="listRef">
          <div class="list-item" :data-order="orderMap[index]" v-for="(item, index) in list" :key="index" :class="[
            index == 6 || index == 13 ? 'mr-0' : '',
            index > 6 ? 'mt-100' : '',
          ]">
            <img :src="item.url" alt="" />
            <p>{{ item.name }}</p>
          </div>
        </div>
      </div>
    </div>
    <div class="last-area">
      <img src="https://gw.alicdn.com/imgextra/i4/O1CN013OYoqp1foK6tig7lJ_!!6000000004053-2-tps-864-168.png" alt="" />
      <span>企业数字化一个钉钉就解决</span>
    </div>
  </div>
</template>

<script setup lang="ts">
const listRef = ref();
const playground = ref();
const animationRef = ref();

onMounted(() => {
  updateMap();
});

const list = [
  {
    name: "即时沟通",
    url: "https://img.alicdn.com/imgextra/i3/O1CN01TVWH501KYCEaUxPgv_!!6000000001175-0-tps-480-480.jpg",
  },
  {
    name: "组织",
    url: "https://img.alicdn.com/imgextra/i1/O1CN01vqBxIP1L5d98G9xJD_!!6000000001248-0-tps-480-480.jpg",
  },
  {
    name: "智能人事",
    url: "https://img.alicdn.com/imgextra/i3/O1CN01AKJU3T1tSi2NAFHFP_!!6000000005901-0-tps-480-480.jpg",
  },
  {
    name: "组织大脑",
    url: "https://img.alicdn.com/imgextra/i3/O1CN01AKJU3T1tSi2NAFHFP_!!6000000005901-0-tps-480-480.jpg",
  },
  {
    name: "OA审批",
    url: "	https://img.alicdn.com/imgextra/i1/O1CN01sphiAp1Fj7bOKEppz_!!6000000000522-2-tps-480-480.png",
  },
  {
    name: "邮箱",
    url: "https://img.alicdn.com/imgextra/i2/O1CN015T9BdZ28Ns5n1A82W_!!6000000007921-0-tps-480-480.jpg",
  },
  {
    name: "Teambition",
    url: "	https://img.alicdn.com/imgextra/i3/O1CN01bc6FKX1OCuHkDC2J7_!!6000000001670-0-tps-480-480.jpg",
  },
  {
    name: "文档",
    url: "https://img.alicdn.com/imgextra/i1/O1CN01Y936lS1whluhvos2E_!!6000000006340-0-tps-480-480.jpg",
  },
  {
    name: "音视频会议",
    url: "https://img.alicdn.com/imgextra/i3/O1CN017Wk2Cp1lp8VaSd22U_!!6000000004867-0-tps-480-480.jpg",
  },
  {
    name: "开放平台",
    url: "https://img.alicdn.com/imgextra/i4/O1CN016JIoXg1lMHYbmErdR_!!6000000004804-0-tps-240-240.jpg",
  },
  {
    name: "宜搭",
    url: "https://img.alicdn.com/imgextra/i3/O1CN011GyvMe1of0bAveV4u_!!6000000005251-0-tps-480-480.jpg",
  },
  {
    name: "钉闪会",
    url: "https://img.alicdn.com/imgextra/i2/O1CN01zFSNcP26wYrM09A4S_!!6000000007726-0-tps-480-480.jpg",
  },
  {
    name: "连接器",
    url: "https://img.alicdn.com/imgextra/i2/O1CN0142F9wc23s261dItxf_!!6000000007310-0-tps-480-480.jpg",
  },
  {
    name: "酷应用",
    url: "https://img.alicdn.com/imgextra/i4/O1CN012wi8vZ1xt3HbO0ttd_!!6000000006500-0-tps-480-480.jpg",
  },
];

const orderMap = ref(<any>{
  0: 0,
  1: 1,
  2: 2,
  3: 3,
  4: 2,
  5: 1,
  6: 0,
  7: 0,
  8: 1,
  9: 2,
  10: 3,
  11: 2,
  12: 1,
  13: 0,
})

// 动画曲线 - 根据传入的横坐标计算对应的纵坐标(value)
const createAnimation = (
  scrollstart: number,
  scrollEnd: number,
  valueStart: number,
  valueEnd: number
) => {
  return function (scroll: number) {
    if (scroll <= scrollstart) {
      return valueStart;
    }
    if (scroll >= scrollEnd) {
      return valueEnd;
    }
    return (
      valueStart +
      ((valueEnd - valueStart) * (scroll - scrollstart)) /
      (scrollEnd - scrollstart)
    );
  };
}

// 映射 - dom => {}
const animationMap = new Map();

const getDomAnimation = (scrollStart: number, scrollEnd: number, dom: any) => {
  scrollStart = scrollStart + dom.dataset.order * 600;
  const opacityAimation = createAnimation(scrollStart, scrollEnd, 0, 1);
  const opacity = function (scroll: number) {
    return opacityAimation(scroll);
  };

  const xAnimation = createAnimation(
    scrollStart,
    scrollEnd,
    animationRef.value.clientWidth / 2 - dom.offsetLeft - dom.clientWidth / 2,
    0
  );
  const yAnimation = createAnimation(
    scrollStart,
    scrollEnd,
    animationRef.value.clientHeight / 2 - dom.offsetTop - dom.clientHeight / 2,
    0
  );
  const scaleAnimation = createAnimation(scrollStart, scrollEnd, 0.5, 1);

  const transform = function (scroll: number) {
    return `translate(${xAnimation(scroll)}px, ${yAnimation(
      scroll
    )}px) scale(${scaleAnimation(scroll)})`;
  };

  return {
    opacity,
    transform,
  };
}

// 更新map
const updateMap = () => {
  // 每次调用时先将之前的清除掉,窗口大小等发生改变时重新处理
  animationMap.clear();
  const playGroundRect = playground.value.getBoundingClientRect();
  const scrollStart = playGroundRect.top + window.scrollY;
  const scrollEnd = playGroundRect.bottom + window.scrollY - window.innerHeight;
  for (const item of listRef.value.children) {
    animationMap.set(item, getDomAnimation(scrollStart, scrollEnd, item));
  }
}

// 更新dom的样式,遍历map给每个dom元素设置样式(透明度、偏移量、放缩....)
const updatestyles = () => {
  const scroll = window.scrollY;
  for (let [dom, value] of animationMap) {
    for (const cssProp in value) {
      dom.style[cssProp] = value[cssProp](scroll);
    }
  }
}

// 初始时调用一次
updatestyles();

// 监听滚动条事件
window.addEventListener("scroll", updatestyles);
</script>
<style>
*,
body,
html {
  margin: 0;
  padding: 0;
}

/* 隐藏滚动条 */
body::-webkit-scrollbar {
  display: none;
}
</style>
<style lang="scss" scoped>
.main-conteiner {
  width: 100%;
  height: 100%;
}

.logo-title {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  height: 800px;
  margin-top: 150px;

  .logo {
    width: 200px;
    height: 200px;

    img {
      display: block;
      width: 100%;
    }
  }

  .title {
    width: 800px;
    height: 100px;
    margin-top: 50px;

    img {
      display: block;
      width: 100%;
      background: #040506;
    }
  }
}

.playground {
  width: 100%;
  height: 3000px;

  .animation-container {
    position: sticky;
    top: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100%;
    background: #040506;
    height: 710px;

    .list {
      display: flex;
      flex-wrap: wrap;
      width: 1200px;
      height: 460px;
      background: #15171b;
      border-radius: 20px;
      padding: 100px;
      box-sizing: border-box;
    }

    .list-item {
      width: 80px;
      height: 80px;
      margin-right: 73.333px;

      img {
        display: block;
        width: 80px;
        height: 80px;
        border-radius: 20px;
      }

      p {
        color: #fff;
        text-align: center;
      }
    }

    .mr-0 {
      margin-right: 0;
    }

    .mt-100 {
      margin-top: 100px;
    }
  }
}

.last-area {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 800px;
  background: #0a0610;

  img {
    display: block;
    width: 200px;
    margin-right: 20px;
  }

  span {
    display: inline-block;
    line-height: 60px;
    font-size: 23px;
    font-weight: 700;
    color: #fff;
  }
}
</style>

  • 12
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值