vue3 + css3 实现动效overview页面

本文主要讲解关于vue3 + css3 实现动效overview页面相关内容,让我们来一起学习下吧!

一、要点

1. 保持背景视频仅铺满第一屏并适配不同尺寸显示器。【🌟】

    1. 通过媒体查询适配宽高比例,控制width跟height的100vh/vw切换。
    1. 页面挂载完毕时执行视频自动静音播放(非静音无法静默播放)。

2. 导航栏动态吸顶。【🌟】

    1. 监听页面滚动距离,超出一屏时切换isFixed改变导航栏的position定位。
    1. 加入animation动画实现缓入缓出(此处不宜使用transition)。
    1. 结合window.scrollTo实现导航栏锚点缓滚。

3. 跑马灯logo列表横向滚动。【🌟🌟】

    1. 加入animation动画实现横向滚动。
    1. 结合css变量设置父子宽度实现流畅持续滚动。

4. 内容根据滚动情况动态展示。【🌟】

    1. 监听页面滚动距离,指定距离触发动态展示。
    1. 为需要动态展示的标签添加标识类名。
    1. 结合animation,delay,transform实现动画延时执行。

5. 按钮默认炫光跑马特效,hover时光源扩散效果实现。【🌟🌟🌟】

    1. 通过定时器状态1实现默认炫光跑马特效。
    1. 通过定时器状态2停止定时器状态1和状态3,实现mouseenter时光源扩散。
    1. 通过定时器状态3停止定时器状态1和状态2,实现mouseleave时光源聚集。

6. banner切换通过元素组合动态实现。【🌟🌟🌟】

    1. 还原每个banner元素的位置及大小。
    1. 设置对应每个bannerItem需要展示的元素及元素的变形/位移效果。
    1. 通过mouseenter实现bannerItem的切换及动画转场。

二、6个要点实现详情

1. 保持背景视频仅铺满第一屏并适配不同尺寸显示器。

<div class="leocomedy-banner" @mouseenter="bannerPlayVideoHandle">
  <video class="leocomedy-banner__video"
    ref="bannerVideoPlayer"
    :src="bannerVideoSrc"
    autoplay
    loop
    preload="auto"
    playsinline
  ></video>
</div>
    1. 通过媒体查询适配宽高比例,控制width跟height的100vh/vw切换。
@media screen and (max-aspect-ratio: 1920/1080) {
  .leocomedy-banner__video {
    height: 100vh;
    width: auto;
  }
}
    1. 页面挂载完毕时执行视频自动静音播放(非静音无法静默播放)。
const bannerVideoPlayer = ref(null);
const bannerVideoSrc = ref(leocomedyVideo);
const bannerPlayVideoHandle = () => {
  if (bannerVideoSrc) {
    bannerVideoPlayer.value.muted = true;
    bannerVideoPlayer.value.play();
  }
};

onMounted(() => {
    bannerPlayVideoHandle()
  })
})

完整代码

vue3 + css3 实现动效overview页面

2. 导航栏动态吸顶。

<Nav :isFixed="animationData.navFixed"></Nav>
    1. 监听页面滚动距离,超出一屏时切换isFixed改变导航栏的position定位。
const animationData = reactive({
  navFixed: false,
  navFixedZone: null
})

onMounted(() => {
  nextTick(() => {
    let top = 0,
      windowHeight = window.innerHeight;
    const navEl = document.getElementsByClassName('leocomedy-nav');
    window.addEventListener('scroll', () => {
      top = window.pageYOffset;   
      if (top <= windowHeight) {
        if ((navEl[0].classList[1] === 'isFixed')  && !animationData.navFixedZone) {
          navEl[0].classList.add('isNavTransition');
          animationData.navFixedZone = setTimeout(() => {
            animationData.navFixed = false;
            animationData.navFixedZone = null;
          }, 300);
        }
      } else {
        animationData.navFixed = true;
      }
    })
  })
})
    1. 加入animation动画实现缓入缓出(此处不宜使用transition,否则宽度变化及定位变化会导致标签变化怪异,非预期效果)。
.isFixed {
    position: fixed;
    z-index: 3;
    color: #FFFFFF;
    margin: 20px auto 0;
    width: calc(100% - 240px);
    border-radius: 999px;
    animation: isFixedShow 0.5s forwards 1;
    backdrop-filter: blur(8px);
    background-color: #61616133;
    border: 3px solid #5e5e5e4d;
    height: 76px;
    left: 0;
    right: 0;
}

@keyframes isFixedShow {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}
    1. 结合window.scrollTo实现导航栏锚点缓滚。
const navItemHandle = (item) => {
  if (item.site) {
    const getEl = document.getElementById(item.site);
    window.scrollTo({
      top: getEl.offsetTop - 84,
      behavior: 'smooth'
    })
  }
}

完整代码

vue3 + css3 实现动效overview页面

3. 跑马灯logo列表横向滚动。

  <div class="leocomedy-linescroll"
    :style="[
      {'--linAllWidth': linAllWidth},
      {'--linOnceWidth': linOnceWidth}
    ]"
  >
    <div class="leocomedy-linescroll-box">
      <div class="leocomedy-linescroll-double"
        v-for="item in 4"
      >
        <div class="leocomedy-linescroll-item"
          v-for="(item, index) in baseData.lineList" :key="index"
        >
          <img class="leocomedy-linescroll-item__img" :src="item" />
        </div>
      </div>
    </div>
  </div>
    1. 加入animation动画实现横向滚动。
animation: scrollToLeft 20s linear infinite;


@-webkit-keyframes scrollToLeft {
    from {
      -webkit-transform: translateX(0px);
      transform: translateX(0px);
    }
    to {
      -webkit-transform: translateX(calc(0px - var(--linAllWidth) + 2 * var(--linOnceWidth)));
      transform: translateX(calc(0px - var(--linAllWidth) + 2 * var(--linOnceWidth)));
    }
}
@keyframes scrollToLeft {
    from {
      -webkit-transform: translateX(0px);
      transform: translateX(0px);
    }
    to {
      -webkit-transform: translateX(calc(0px - var(--linAllWidth) + 2 * var(--linOnceWidth)));
      transform: translateX(calc(0px - var(--linAllWidth) + 2 * var(--linOnceWidth)));
    }
}
    1. 结合css变量设置父子宽度实现流畅持续滚动。
const linAllWidth = ref('0px')
const linOnceWidth = ref('0px')

onMounted(() => {
  linOnceWidth.value = ((184.2 * baseData.lineList.length) + 'px');
  linAllWidth.value = (4 * (184.2 * baseData.lineList.length) + 'px');
})

完整代码

vue3 + css3 实现动效overview页面

4. 内容根据滚动情况动态展示。

<div class="leocomedy-banner-title animationBox"
    :style="{
      'animation-delay' : 0.2 + 's'
    }"
  >
    <h1 class="leocomedy-banner-l__h1">先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。</h1>
</div>
    1. 监听页面滚动距离,指定距离触发动态展示。
// 获取元素距离页面顶部的距离
const getElementTop = (element) => {
  const rect = element.getBoundingClientRect();
  return rect.top + window.scrollY; // 加上scrollY因为top是相对于视窗的
}
// 设置每个元素执行动画的延时
const settimeAnimation = (animationBoxsItem) => {
    animationBoxsItem.classList.add("animationAction");
}
// 遍历页面上的animationBox,根据滚动及元素距离页面顶部距离判断是否要执行动画
const controllAnimation = (nowtop = 0, windowHeight) => {
  const animationBoxs = document.getElementsByClassName('animationBox');
  if (animationBoxs.length <= 0) return;
  for (let i = 0; i < animationBoxs.length; i++) {
    if (nowtop !== getElementTop(animationBoxs[i])) {
      if (getElementTop(animationBoxs[i]) - nowtop - (windowHeight * (10/10)) <= 0) {
        settimeAnimation(animationBoxs[i])
      }
    }
  }
}

onMounted(() => {
  nextTick(() => {
    let top = 0,
      windowHeight = window.innerHeight;
    const navEl = document.getElementsByClassName('leocomedy-nav');
    window.addEventListener('scroll', () => {
      top = window.pageYOffset;   
      controllAnimation(top, windowHeight);
    })
    controllAnimation(top, windowHeight);
  })
})

    1. 为需要动态展示的标签添加标识类名。
.animationBox {
  opacity: 0;
  transform: translateY(30px);
}
.animationAction {
  animation:animationShow 0.5s forwards 1;
}
@keyframes animationShow {
  0% {
    transform: translateY(30px);
    opacity: 0;
  }
  100% {
    transform: translateY(0px);
    opacity: 1;
  }
}
    1. 结合animation,delay,transform实现动画延时执行。
animation-delay

5. 按钮默认炫光跑马特效,hover时光源扩散效果实现。

<div class="leocomedy-btn"
    @click="routerPush"
    @mouseenter="btnMouse(true)"
    @mouseleave="btnMouse(false)"
    :style="{'--eightGradient': baseData.eightGradient}"
>
    <div class="leocomedy-btn-con">
      <span class="leocomedy-btn-txt">{{ btnText }}</span>
      <i class="icon-arrow"></i>
    </div>
    <div class="leocomedy-btn-bg" 
      :style="{ '--transform': baseData.transform }"
    ></div>
</div>
<!-- base -- />
const baseData = reactive({
  transformNum: 0,
  transform: "rotate(0deg) translate(-50%,-50%)",
  timeZone: null,
  transformScalePaused: 1,
  transformNumPaused: 50,
  timeZonePaused: null,
  eightX: 50,
  eightY: 100,
  eightGradient: "radial-gradient(25% 50% at 50% 0%, #c8aefc 0%, rgba(104, 39, 230, 0.9) 50%, rgba(103, 39, 230, 0.32) 100%)"
})
.leocomedy-btn {
  box-sizing: border-box;
  width: auto;
  height: auto;
  display: inline-flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
  overflow: visible;
  align-content: center;
  flex-wrap: nowrap;
  position: relative;
  cursor: pointer;
  .leocomedy-btn-con {
    width: 100%;
    height: 100%;
    background: #000000;
    padding: 20px 28px 20px 28px;
    border-radius: 118px;
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-items: center;
    position: relative;
    z-index: 1;
    .leocomedy-btn-txt {
      color: #ffffff;
      font-weight: bold;
      font-size: 15px;
      line-height: 1.2;
    }
  }
  .leocomedy-btn-bg {
    display: block;
    position: absolute;
    width: calc(100% + 4px);
    height: calc(100% + 4px);
    z-index: 0;
    border-radius: 61px;
    overflow: hidden;
    &:after{
      display: block;
      content: "";
      height: 0;
      padding-bottom: calc(100% + 20px);
      width: calc(100% + 20px);
      background: radial-gradient(28.8937% 26.4759% at 69.3682% 74.4384%, #A578FF 0%, #6727e6 68.7688%, rgba(103, 39, 230, 0.24) 100%)calc(100% + 20px);
      border-radius: 180px;
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate(-50%,-50%) scale(1);
      transform-origin: 0 0;
      transform: var(--transform);
    }
  }
  &::after {
    content: "";
    position: absolute;
    width: 167px;
    height: 55px;
    filter: blur(15px);
    background: var(--eightGradient);
    overflow: hidden;
    border-radius: 72px;
    z-index: 0;
  }
  .icon-arrow {
    margin-left: 8px;
    width: 18px;
    height: 18px;
    background-color: #FFFFFF;
    -webkit-mask: url(@/assets/leocomedybtnArrow.png) no-repeat;
    mask: url(@/assets/leocomedybtnArrow.png) no-repeat;
    -webkit-mask-size: 100% 100%;
    mask-size: 100% 100%;

  }
}
    1. 通过定时器状态1实现默认炫光跑马特效。
// 旋转事件
const countDown = () => {
  if (baseData.timeZonePaused) {
    restoreHandle()
    return
  } 
  baseData.timeZone = setInterval(() => {
    if (baseData.transformNum < 360) {
      if (((baseData.transformNum <= 120) && (baseData.transformNum > 75)) || ((baseData.transformNum <= 300) && (baseData.transformNum > 255))) {
        // 半圆,基于π,但忘记是怎么推导出0.349了
        baseData.transformNum += 0.349
      } else {
        baseData.transformNum += 1
      }
    } else {
      baseData.transformNum = 0
    }
    baseData.transform = `rotate(${baseData.transformNum}deg) translate(-50%,-50%) scale(1)`
  }, 8.333)
  // 3s
}
    1. 通过定时器状态2停止定时器状态1和状态3,实现mouseenter时光源扩散。
// 停止旋转后执行放大事件
const pausedHandle = () => {
  if (baseData.timeZone) {
    clearInterval(baseData.timeZone)
    baseData.timeZone = null
  }
  clearInterval(baseData.timeZonePaused)
  baseData.timeZonePaused = null
  baseData.timeZonePaused = setInterval(() => {
    if (baseData.transformScalePaused < 4) {
      baseData.transformScalePaused += 0.0166
      baseData.transformNumPaused += 1.388
    } else {
      clearInterval(baseData.timeZonePaused)
    }
    baseData.transform = `rotate(${baseData.transformNum}deg) translate(-${baseData.transformNumPaused}%,-${baseData.transformNumPaused}%) scale(${baseData.transformScalePaused})`
  }, 8.333)
  // 1.5s
}
    1. 通过定时器状态3停止定时器状态1和状态2,实现mouseleave时光源聚集。
// 停止放大后执行缩小事件
const restoreHandle = () => {
  clearInterval(baseData.timeZonePaused)
  baseData.timeZonePaused = null
  baseData.timeZonePaused = setInterval(() => {
    if (baseData.transformScalePaused > 1) {
      baseData.transformScalePaused -= 0.0166
      baseData.transformNumPaused -= 1.388
    } else {
      clearInterval(baseData.timeZonePaused)
      baseData.timeZonePaused = null
      countDown()
    }
    baseData.transform = `rotate(${baseData.transformNum}deg) translate(-${baseData.transformNumPaused}%,-${baseData.transformNumPaused}%) scale(${baseData.transformScalePaused})`
  }, 8.333)
  // 1.5s
}

完整代码

vue3 + css3 实现动效overview页面

6. banner切换通过元素组合动态实现。

    1. 还原每个banner元素的位置及大小。
<div class="skeleton-box" :class="[baseData.videoSkeleton]">
  <img :src="skeleton101" class="skeleton101 skeleton-box-img" />
  <img :src="skeleton102" class="skeleton102 skeleton-box-img" />
  <img :src="skeleton103" class="skeleton103 skeleton-box-img" />
  <img :src="skeleton202" class="skeleton202 skeleton-box-img" />
  <img :src="skeleton301" class="skeleton301 skeleton-box-img" />
</div>
    1. 设置对应每个bannerItem需要展示的元素及元素的变形/位移效果。
  .skeleton103 {
    animation: skleton103SkewHide 0.75s forwards 1, skleton103OpacityHide 0.75s forwards 0.75s 1;
  }
@keyframes skleton103SkewHide {
  0% {
    transform: skew(4deg, -2deg) rotate(-1deg) scale(0.585, 0.585);
    right: 79px;
    bottom: -47px;
    opacity: 1;
  }
  100% {
    transform: skew(0deg, 0deg) rotate(0deg) scale(1, 1);
    right: 0px;
    bottom: 0px;
    opacity: 1;
  }
}
@keyframes skleton103OpacityHide {
  0% {
    transform: skew(0deg, 0deg) rotate(0deg) scale(1, 1);
    right: 0px;
    bottom: 0px;
    opacity: 1;
  }
  100% {
    transform: skew(0deg, 0deg) rotate(0deg) scale(1, 1);
    right: 0px;
    bottom: 0px;
    opacity: 0;
  }
}
    1. 通过mouseenter实现bannerItem的切换及动画转场。
const radiusItemMouse = (index) => {
    if (index === baseData.default) return;
    baseData.videoSkeleton = "skeleton-box-";
    if (baseData.default === 0) {
      baseData.videoSkeleton += (index === 1) ? '102' : '103';
    }
    if (baseData.default === 1) {
      baseData.videoSkeleton += (index === 0) ? '201' : '203';
    }
    if (baseData.default === 2) {
      baseData.videoSkeleton += (index === 0) ? '301' : '302';
    }
    baseData.default = index;
}

以上就是关于vue3 + css3 实现动效overview页面相关的全部内容,希望对你有帮助。欢迎持续关注程序员导航网,学习愉快哦!

版权声明:juejinhot 发表于 2024-05-08 20:25:28。
转载请注明:vue3 + css3 实现动效overview页面 | 程序员导航网

暂无评论

暂无评论...