巧用 CSS 變數,實現動畫函數復用,製作高級感拉滿的網格動畫

来源:https://www.cnblogs.com/coco1s/archive/2023/03/07/17186878.html
-Advertisement-
Play Games

本文將介紹一種基於 CSS 變數技巧,通過合理使用 CSS 變數,實現 CSS 動畫 @keyframes 的復用。 CSS 變數 CSS 變數大家應該都比較熟悉了,已經不能算是新知識了,快速過一遍。 CSS 變數(CSS Variable),在之前也叫做 CSS 自定義屬性,其使用方式如下: // ...


本文將介紹一種基於 CSS 變數技巧,通過合理使用 CSS 變數,實現 CSS 動畫 @keyframes 的復用。

CSS 變數

CSS 變數大家應該都比較熟悉了,已經不能算是新知識了,快速過一遍。

CSS 變數(CSS Variable),在之前也叫做 CSS 自定義屬性,其使用方式如下:

// 聲明一個變數:
:root{
  --bgColor: #000;
}

這裡我們藉助了上面 #12、結構性偽類 中的 :root{ } 偽類,在全局 :root{ } 偽類中定義了一個 CSS 變數,取名為 --bgColor

定義完了之後則是使用,假設我要設置一個 div 的背景色為黑色:

.main{
  background:var(--bgColor);
}

這裡,我們在需要使用之前定義變數的地方,通過 var(定義的變數名) 來調用。

在 @keyframes 中使用 CSS 變數

OK,回歸我們的正題。巧用 CSS 變數,實現動畫函數復用

假設,我們現在有多個元素,需要實現一個位移動畫,從位置 A 位移到 位置 B,位置 A 相同,但是位置 B 不一樣,像是這樣:

正常而言,由於終點不一樣,我們可能需要實現 3 個不一樣的 @keyframes,像是這樣:

<ul>
    <li></li>
    <li></li>
    <li></li>
</ul>
li:nth-child(1) {
    animation: move1 2s linear;
}
li:nth-child(2) {
    animation: move2 2s linear;
}
li:nth-child(3) {
    animation: move3 2s linear;
}

@keyframes move1 {
    60%,
    100% {
        transform: translate(150px);
    }
}
@keyframes move2 {
    60%,
    100% {
        transform: translate(120px);
    }
}
@keyframes move3 {
    60%,
    100% {
        transform: translate(200px);
    }
}

這個代碼有問題嗎?沒有。

但是,我們可以利用 CSS 變數,讓它變得更為簡潔,我們改造一下 @keyframes 代碼,將固定的位移值,變成一個變數:

@keyframes move {
    60%,
    100% {
        transform: translate(var(--dis));
    }
}

由於 CSS 變數是存在作用域的,我們可以通過 CSS 變數的方式,給每一個 li 定義一個不同的 --dis 變數,像是這樣:

li:nth-child(1) {
    --dis: 150px;
}
li:nth-child(2) {
    --dis: 120px;
}
li:nth-child(3) {
    --dis: 200px;
}

這樣,雖然動畫的結束點不一樣,但是我們利用 CSS 變數,復用了同一個 @keyframes 函數:

通過內聯 style 屬性傳入自定義變數

除了通過在 <style> 內傳入不同的自定義變數,我們還可以通過內聯 style 屬性傳入自定義變數。

我們再改造一下我們的 @keyframes:

@keyframes move {
    60%,
    100% {
        transform: translate(var(--end));
        background: var(--color);
    }
}

這一次,我們不需要通過 :nth-child() 去修改每一個 li 的 CSS,而是通過 HTML 元素的內聯 style 屬性,像是這樣:

<ul>
    <li style="--end: 150px; --color: red;"></li>
    <li style="--end: 200px; --color: blue;"></li>
    <li style="--end: 120px; --color: green;"></li>
</ul>

是的,每個 li 元素的 @keyframes 可以讀取到每個 li 的 style 裡面定義的不一樣的 CSS 變數。

這樣,我們就可以得到如下效果:

完整的代碼,可以戳這裡:CodePen Demo -- 巧用 CSS 變數,實現動畫函數復用

實戰演練

下麵我們實戰演練一下,上一點難度。

在很久之前,我們實現過這樣一個動畫效果:

這個動畫效果的實現方式在於:

  1. 父級元素實現一個 rotateZ(360deg) 的勻速動畫
  2. 子級元素實現一個反向的 rotateZ(-360deg) 的勻速動畫
  3. 給父級元素添加一個 rotateX(40deg) 的動畫

由於父容器和子容器同時相反向旋轉,所以子元素看上去其實和沒有旋轉是一樣的。但是由於又添加了一個 rotateX(40deg) 動畫,因此看上去就會有這樣一種 3D 效果。

在之前,我們的代碼是這樣的:

<div class="reverseRotate">
    <div class="rotate">
    </div>
</div>
.rotate {
    animation: rotate 5s linear infinite; 
}
.reverseRotate {
    animation: reverseRotate 5s linear infinite; 
}
@keyframes rotate {
    0% {
        transform: rotateX(0deg) rotateZ(0deg);
    }
    50% {
        transform: rotateX(40deg) rotateZ(180deg);
    }
    100% {
        transform: rotateX(0deg) rotateZ(360deg);
    }
}
@keyframes reverseRotate {
    0% {
        transform: rotateZ(0deg);
    }
    100% {
        transform: rotateZ(-360deg);
    }
}

可以看到,我們這裡實現了兩個動畫效果:

  1. @keyframes rotate {} 父容器的旋轉動畫
  2. @keyframes reverseRotate {} 子容器的旋轉動畫

其實,這裡,運用今天的技巧,我們可以把兩個動畫合成為一個,利用 CSS 自定義變數進行控制。改造後更簡潔的 CSS 代碼如下:

.rotate {
    --degZ: 360deg;
    --degZMiddle: 180deg;
    --degX: 30deg;
    animation: rotate 5s linear infinite; 
}

.reverseRotate {
    --degZ: -360deg;
    --degZMiddle: -180deg;
    --degX: 0;
    animation: rotate 5s linear infinite; 
}

@keyframes rotate {
    0% {
        transform: rotateX(0deg) rotateZ(0deg);
    }
    50% {
        transform: rotateX(var(--degX)) rotateZ(var(--degZMiddle));
    }
    100% {
        transform: rotateX(0deg) rotateZ(var(--degZ));
    }
}

是的,我們可以得到同樣的效果!

完整的代碼,你可以戳這裡:CodePen DEMO -- Css動畫正反旋轉相消

圖片旋轉配合容器旋轉

下麵,我們再來嘗試一個有意思的動畫效果,圖片旋轉配合容器旋轉。

在上述的基礎上,如果我們把子元素,改成圖片,整個效果就會有意思不少,我們稍微改變一點點代碼:

<div class="reverseRotate">
    <img class="rotate" src="https://picsum.photos/1000/1000?random=5" alt="">
</div>
.rotate,
.reverseRotate {
    width: 60vh;
    height: 60vh;
}
.reverseRotate {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    border: 3px solid #999;
    overflow: hidden;
}

.rotate {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 100%;
    height: 100%;
}

.rotate {
    --degZ: 360deg;
    animation: rotate 5s linear infinite; 
}

.reverseRotate {
    --degZ: -360deg;
    animation: rotate 5s linear infinite; 
}

@keyframes rotate {
    0% {
        transform: translate(-50%, -50%) rotateZ(0deg);
    }
    100% {
        transform: translate(-50%, -50%) rotateZ(var(--degZ));
    }
}

這裡,我們做了什麼事情呢?

  1. 去掉了 3D 效果
  2. 給外層容器加了邊框
  3. 內層圖片基於父容器絕對定位,水平垂直居中
  4. 內外兩層容器反向旋轉 360° 動畫

這樣,我們就能看到,雖然內外兩層容器同時在進行相反方向的旋轉 360° 動畫,但是內部的圖片其實是靜止不動的!

效果如下:

由於,內部圖片的大小為父容器的 100%,所以在旋轉過程中,父容器會有明顯的無法包裹住整個圖片的情況。

這個很好解決,我們只需要把圖片大小調整大一點:

// ... 其它代碼不變
.rotate {
    width: 150%;
    height: 150%;
}
.rotate {
    --degZ: 360deg;
    animation: rotate 5s linear infinite; 
}

正常而言,對於正方形容器,內部圖片設置到 141% 即可滿足父容器旋轉過程,可以一直包裹住圖片的效果。那麼,我們就能得到這樣一種效果:

完整的代碼,你可以戳這裡:CodePen Demo -- Css動畫正反旋轉相消

Gird 佈局配合正反旋轉動畫

當然,上述當只有一個容器的時候,整個動畫效果還不夠震撼。

如果我們可以把這個效果融合進整個佈局的動畫之中,整個效果又會完全不一樣。

Rotating gallery with CSS scroll-driven animations 這篇文章中,作者提供了一種非常巧妙的思路,將 Grid 佈局動畫與上述動畫效果巧妙的結合了起來。

首先,我們利用 Gird 佈局,實現這樣一個簡單的網格佈局結構:

<div class="container">
  <div class="A">
      <img src="https://picsum.photos/600/600?random=1" alt=""></div>
  <div class="B">
      <img src="https://picsum.photos/600/600?random=2" alt=""></div>
  <div class="C">
      <img src="https://picsum.photos/600/600?random=3" alt=""></div>
  <div class="D">
      <img src="https://picsum.photos/600/600?random=4" alt=""></div>
  <div class="E">
      <img src="https://picsum.photos/600/600?random=5" alt=""></div>
</div>
.container {
    width: 60vmin;
    height: 60vmin;
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    grid-template-rows: 1fr 1fr 1fr;
    gap: 4px;
    grid-template-areas:
        "E B B"
        "E A C"
        "D D C";
}
.container > div {
    border: 3px solid #431312;
    border-radius: 5px;
}

.A {
    grid-area: A;
}
.B {
    grid-area: B;
}
.C {
    grid-area: C;
}
.D {
    grid-area: D;
}
.E {
    grid-area: E;
}

效果如下:

接下來,我們要做的,就是結合上面的知識點,容器滾動起來,圖片反向滾動起來,配合一些 tranfrom 變換。

有了上面的鋪墊,下麵的新增的代碼就非常好理解了:

.container > div img {
    --scale: 1;
    --rotation: -360deg;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 260%;
    height: 260%;
    object-fit: cover;
    object-position: center;
}
.container,
.container > div img {
    animation: 10s scale-up both ease-in-out infinite alternate;
}
@keyframes scale-up {
	0% {
		transform: translate(-50%, -50%) scale(var(--scale)) rotate(0deg);
	}
	100% {
		transform: translate(-50%, -50%) scale(1) rotate(var(--rotation));	
	}
}

這樣,我們就得到了一個高級感拉滿的網格旋轉動畫:

註意,這裡我們依舊是通過 CSS 自定義變數,在不同元素間,復用了同一個動畫 @keyframes 函數。

完整的代碼,你可以戳這裡:CodePen Demo -- Grid 圖片旋轉動畫 & 使用 CSS 變數復用動畫函數

最後

好了,本文到此結束,希望本文對你有所幫助

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 我們在初學react時(有vue等其他基礎),我們在方法調用的時候通常是 方法名() 例如: import React from 'react' function test() { console.log('這是測試') } export default class Login extends Re ...
  • 什麼是單例模式 單例模式 (Singleton Pattern)又稱為單體模式,保證一個類只有一個實例,並提供一個訪問它的全局訪問點。也就是說,第二次使用同一個類創建新對象的時候,應該得到與第一次創建的對象完全相同的對象。 簡單的說就是保證一個類僅有一個實例,並提供一個訪問它的全局訪問點,這樣的模式 ...
  • 前言 最近需要用到多包管理 monorepo 開發新項目,所以提前預研一下項目搭建。 monorepo(monolithic repository)是一種項目架構,就是用一個倉庫管理多個項目(應用,庫),react和babel的源碼倉庫都是用這種方式在管理。 優缺點略過,可以參考:精讀《Monore ...
  • 首先需要明確一點,以往瀏覽器對css的支持是不同的,不同瀏覽器的樣式可能會存在差異,對待這種差異問題,需要寫幾套不同的css來相容(邊框、圓角、顏色等),這樣是非常麻煩的,瀏覽器css顯示差異化屬於瀏覽器自身的問題,跟我們的css是沒有關係的,好的瀏覽器就有好的顯示,糟糕的瀏覽器就只有普通顯示,我們 ...
  • 簡單工廠問題 簡單工廠中我們通過參數來返回不同的產品對象,如果管理的對象過多,這個工廠函數會比較龐大,且當我們需要增加一個新的產品時,需要修改這個工廠方法,違反開閉原則(對拓展開放,對修改關閉)。 為瞭解決簡單工廠模式的問題,出現了工廠方法模式。 解決簡單工廠思路 簡單工廠類圖關係類似如下: 如上圖 ...
  • 本篇文章將介紹如何使用 vite 打包我們的組件庫,同時告訴大家如何使用插件讓打包後的文件自動生成聲明文件(*.d.ts) 打包配置 vite 專門提供了庫模式的打包方式,配置其實非常簡單,首先全局安裝 vite 以及@vitejs/plugin-vue pnpm add vite @vitejs/ ...
  • 在原博主Instagram 上每天都會發佈 JavaScript 的多選問題,並且同時也會在這個倉庫中發佈。 從基礎到進階,測試你有多瞭解 JavaScript,刷新你的知識,或者幫助你的 coding 面試! :muscle: :rocket: 博主每周都會在這個倉庫下更新新的問題。 答案在問題下 ...
  • 單例模式是一種設計模式,它可以確保某個類只有一個實例,並提供一個全局的訪問點來訪問該實例,我們可以使用單例模式來管理全局狀態和共用資源。 在JavaScript中,單例模式可以通過多種方式實現,以下是一些常見的實現方式: 1. 對象字面量 使用對象字面量可以輕鬆地創建單例對象,例如: const s ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...