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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章