• Bilibili [冬] banner 早中晚切换效果


    博客地址:https://ainyi.com/101

    Bilibili 官网冬季的 banner 图吸引了我,一开始是中午图,鼠标左移浮现早上图,右移浮现晚上图,挺有意思

    WechatIMG455.png

    来实现一波

    做之前先不要调试看 b 站的代码,自己先想想怎么实现,这样知识记得比较深

    我们尽量使用 css 解决,js 弥补

    分析层级、实现方法

    比较明显的可以看到早中晚三张22 33娘玩耍的图片,浏览器调试可以发现还有三张早中晚的树木图片,一张雪球图片、窗口积雪图片
    一共有 8 张图片资源,其中晚上图片是一段 webm 视频,因为有个小火炉在燃烧
    把这些资源文件直接保存到本地

    所有图片资源,都在头部位置,用绝对定位
    默认展示是中午,早晚是鼠标经过才会出现,那么它们的层级关系可以这样定位:

    • 早:不设置 z-index
    • 中(包括雪球):z-index: 10
    • 晚(包括窗口积雪):z-index: 20

    对应早中晚的树木也应该是如此


    重点:切换
    鼠标移动过程中图片切换的效果,实质对应的是切换每张图片的透明度 opacity

    设置了如上早中晚的层级关系后,我们来定一下左移和右移三个时间段的 opacity

    1. 左移:中午 -> 早上,由于层级中午 > 早上,那么这个过程的早上 opacity >= 1,中午的 opacity 逐渐变为 0(因为中午的层级高,会覆盖中午)
    2. 右移:中午 -> 晚上,由于层级中午 < 晚上,这个过程晚上 opacity 逐渐变为 1,中午的 opacity >= 1(因为晚上的层级高,会覆盖中午)

    上面两个过程可以知道,早上的 opacity 可以不用管,而中午和晚上的 opacity 都涉及到变化

    切换的过程也涉及到图片的移动,可以使用transform: translatex()

    鼠标移开 banner 图时,恢复成中午,有个过渡动画,可以使用transition

    关键点:根据鼠标移动的距离计算 opacity


    计算
    在包裹 banner 的 div 容器样式表设置 --percentage 属性,默认为 0.5

    而鼠标移动的距离,需要通过 js 计算
    mouseenter、mousemove、mouseout 三个监听函数,动态计算出移动百分比,赋值到 --percentage 属性

    图片的 transform、opacity 属性需要应用到 --percentage 来计算数值

    需要注意的是:

    1. 中午的雪球、早中晚的树木 移动的速度大于图片,这块的 transform 要单独处理
    2. 晚上的窗口的积雪是晚上图片完全显示出来后,才开始慢慢浮现,这个 opacity 也要单独处理

    基本该说的点都在上面了,下面来展示一波代码

    注:还有一个下雪的动画,需要用 canvas 实现,这里就没做了

    代码

    html:

    <div class="banner">
      <div class="view">
        <img
          src="../../assets/bilibili/bilibili-winter-view-1.jpg"
          class="morning"
        />
        <img
          src="../../assets/bilibili/bilibili-winter-view-2.jpg"
          class="afternoon"
        />
        <img
          src="../../assets/bilibili/bilibili-winter-ball.png"
          class="ball"
        />
        <video autoplay loop muted class="evening">
          <source
            src="../../assets/bilibili/bilibili-winter-view-3.webm"
            type="video/webm"
          />
        </video>
        <img
          src="../../assets/bilibili/bilibili-winter-view-3-snow.png"
          class="window-cover"
        />
      </div>
      <div class="tree">
        <img
          src="../../assets/bilibili/bilibili-winter-tree-1.png"
          class="morning"
        />
        <img
          src="../../assets/bilibili/bilibili-winter-tree-2.png"
          class="afternoon"
        />
        <img
          src="../../assets/bilibili/bilibili-winter-tree-3.png"
          class="evening"
        />
      </div>
    </div>
    

    css:

    .banner {
      min-height: 155px;
      height: 9.375vw;
      position: relative;
      overflow: hidden;
      --percentage: 0.5;
    
      .view,
      .tree {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        display: flex;
        justify-content: center;
        align-items: center;
        transition: 0.2s all ease-in;
      }
    
      .view {
        transform: translatex(calc(var(--percentage) * 100px));
      }
      .tree {
        transform: translatex(calc(var(--percentage) * 150px - 25px));
        filter: blur(3px);
      }
    
      img,
      video {
        position: absolute;
        height: 100%;
      }
      .evening {
        transition: 0.2s all ease-in;
        z-index: 20;
        opacity: calc((0.5 - var(--percentage)) / 0.5);
      }
      .afternoon {
        transition: 0.2s all ease-in;
        z-index: 10;
        opacity: calc(1 - (var(--percentage) - 0.5) / 0.5);
      }
    
      &.moving {
        .afternoon,
        .evening,
        .ball,
        .view,
        .tree {
          transition: none;
        }
      }
    
      .ball {
        transition: 0.2s all ease-in;
        z-index: 10;
        opacity: calc(1.5 - (var(--percentage) - 0.5) / 0.5);
        transform: translate(calc(100px * var(--percentage)), 22px) rotate(calc(10deg * var(--percentage) + 5deg));
      }
    
      .window-cover {
        z-index: 20;
        opacity: calc(var(--percentage) * (-2));
      }
    }
    

    js:

    let startingPoint = 0
    const header = document.querySelector('.banner')
    
    header.addEventListener('mouseenter', (e) => {
      startingPoint = e.clientX
      header.classList.add('moving')
    })
    
    header.addEventListener('mouseout', (e) => {
      header.classList.remove('moving')
      header.style.setProperty('--percentage', 0.5)
    })
    
    header.addEventListener('mousemove', (e) => {
      let percentage = ((startingPoint - e.clientX) / window.outerWidth) * 2 + 0.5
    
      header.style.setProperty('--percentage', percentage)
    })
    

    demo

    http://ele.ainyi.com/my-transfer

    博客地址:https://ainyi.com/101

  • 相关阅读:
    centos免密码登录
    conda3 快速下载python包
    Flink问题及解决方案
    git把项目推送到不同的remote(git地址)
    选择器提取内容
    spark写入mysql
    flume简介及netcat样例
    Spark 读取 Hbase 优化 --手动划分 region 提高并行数
    shell grep正则表达式
    Hibernate持久化
  • 原文地址:https://www.cnblogs.com/ainyi/p/14261152.html
Copyright © 2020-2023  润新知