首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Bilibili [冬] banner 早中晚切换效果

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

原创
作者头像
Krry
修改2021-01-11 14:20:10
2.6K0
修改2021-01-11 14:20:10
举报
文章被收录于专栏:KrryblogKrryblog

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

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

WechatIMG455.png
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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 分析层级、实现方法
  • 代码
  • demo
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档