整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          大型項(xiàng)目的CSS

          大型項(xiàng)目的CSS

          ss是前端領(lǐng)域的一個(gè)難纏戶,一提到css,沒有人會(huì)說難,也沒有人愿意承認(rèn)自己不會(huì)寫,甚至在面試的過程中css相關(guān)的內(nèi)容都很少提及,足以說明大家對(duì)css的不重視。你真的會(huì)寫css嗎?

          關(guān)于css有兩類問題值得我們重視:樣式和工程。樣式問題指的是具體的效果實(shí)現(xiàn),能否實(shí)現(xiàn)某個(gè)效果,同一個(gè)效果有多種實(shí)現(xiàn)方式,如何抉擇;工程問題是如何在大型項(xiàng)目中寫出可維護(hù)性、可讀的css。

          在各種論壇中經(jīng)常看到關(guān)于css是否是一門編程語言的爭論。在我看來,最好用對(duì)待編程語言的態(tài)度來看待css,不要忽視css,否則,在項(xiàng)目后期,你會(huì)發(fā)現(xiàn),你的css越來越混亂,important會(huì)越來越多,不同位置的類名互相沖突覆蓋,改一個(gè)類的樣式,要檢查整個(gè)項(xiàng)目的頁面是否受到影響,項(xiàng)目內(nèi)部的css共享完全依賴拷貝……從這個(gè)角度來說,你敢說css不是編程語言?它完全有了像編程語言一樣能把你搞得焦頭爛額的能力。而這一切都來源于你在一開始對(duì)她的忽視與不屑。出來混,總要還的啊!

          css的問題

          盲目的定義基礎(chǔ)樣式

          在項(xiàng)目開始之初,拿到UI設(shè)計(jì)稿,信心滿滿地開始定義css的全局基礎(chǔ)樣式,謝天謝地,css對(duì)這一點(diǎn)支持得很徹底,一處定義,所有頁面都可以引用。

          先來一個(gè)color-red。

          .color-red {
          	color: red
          }

          這樣,在整個(gè)項(xiàng)目中,都可以給任意元素添加一個(gè)color-red類,美滋滋,我真是個(gè)小機(jī)靈鬼!

          幾個(gè)迭代過去,你已經(jīng)把color-red這面紅旗插滿了整個(gè)項(xiàng)目。UED說,咱們改個(gè)版,所有紅色的文本改為藍(lán)色,紅色的link不變!

          嗶!——(你發(fā)出的聲音)

          你又得屁顛屁顛地把一個(gè)一個(gè)的紅旗拔出來,再插上藍(lán)色的旗子(因?yàn)槟悴桓也桓裳剑?/p>

          命名沖突

          在一個(gè)頁面,你定義了一個(gè).header,寫了個(gè)完美的特效,發(fā)布到dev一看,就是不管用,橫看豎看,本地是好的啊,神奇(生氣)!過了若干時(shí)間的排查,另一個(gè)同事在另一個(gè)地方也寫了一個(gè).header,完美的覆蓋了你的。把你頭打歪!

          編輯器可不會(huì)提醒你哦!

          慢慢你會(huì)發(fā)現(xiàn),這種命名沖突可太頻繁了呀!頭大,要不要用上css modular啊?

          子類覆蓋

          有的小伙伴聰明地將類名嵌套定義,這就不會(huì)沖突了吧?嘿嘿,你想多了!

          /* in article.css */
          .article .title {
            border-bottom: 1px solid;
            font-size: 1.5em;
          }
          
          /* in widget.css */
          .widget .title {
            color: gray;
            text-transform: uppercase;
          }

          如果在dom樹里面,article和widget在一棵樹的路徑上,你說title是沖突呢還是不沖突呢?

          以上的這些問題,在項(xiàng)目中相信大家都遇見過,并且項(xiàng)目越大,出現(xiàn)的概率就越高,最后就會(huì)演變成一座[屎]山。

          “大家都別動(dòng),牽一發(fā)而動(dòng)全身哦!”

          “這就是蝴蝶效應(yīng)吧???”

          認(rèn)識(shí)BEM

          難道css這些問題就沒法解決了嗎?當(dāng)然不是,我們來看看大神們是如何解決這些問題的。

          BEM是Block、Element、Modifier的縮寫,是一個(gè)類命名的規(guī)范。

          首先我們來看一個(gè)例子:

          /* Block */
          .btn {}
          
          /* Element that depends upon the block */ 
          .btn__price {}
          
          /* Modifier that changes the style of the block */
          .btn--orange {} 
          .btn--big {}

          相信小伙伴們已經(jīng)有了一個(gè)初步的認(rèn)知:

          Block是一個(gè)獨(dú)立的組件(元素),定義的了“組件是什么,按鈕?還是菜單?”。

          Element是屬于Block,是依賴于Block的元素,描述的是“Block里面的什么?比如,文本?圖標(biāo)?”

          Modifier用于描述Block或者Element的外在表現(xiàn),比如大小、顏色、狀態(tài)等。

          看一個(gè)例子:

          <form class="search-form search-form_theme_islands">
              <input class="search-form__input">
              <button class="search-form__button search-form__button_size_m">Search</button>
          </form>

          search-form是Block;

          search-form_theme_islands是Modifier,描述了theme為islands的search-form;

          search-form__input是Element,描述的是search-form內(nèi)部的input元素;

          search-form__button是Element,描述的是search-form內(nèi)部的button元素;

          search-form__button_size_m是Modifier,描述的是size為m的search-form__button;

          這樣寫css是不是非常的清晰?瞬間就提高了可讀性和可維護(hù)性?

          概念如此簡單,還不趕緊多了解一下?

          另外,可能有些小伙伴也注意到了,Block和Element使用雙下劃線分隔,和Modifier是用中劃線分隔,這也不是一成不變的,可以按照自己的喜好來決定如何分割。

          Saas、Css Module

          有些小伙伴可能會(huì)有疑問,BEM和Saas、Css Module有什么區(qū)別?解決的問題是一樣的嗎?

          Sass是css的預(yù)處理器,在寫css的時(shí)候定義了一套規(guī)范,經(jīng)過編譯處理后輸出為css,和BEM是兩個(gè)不同的概念。使用saas或less也能實(shí)現(xiàn)BEM。BEM其實(shí)是不推崇類名的嵌套定義的,如果想像sass那樣嵌套的寫出標(biāo)準(zhǔn)的BEM,可以使用@at-root。

          Css Module解決的問題是多個(gè)模塊的css的命名沖突問題,個(gè)人覺得是傻瓜式解決方案,在應(yīng)用層的css-in-js應(yīng)用比較多,適合css入門選手。要想寫好css,還是得從根本上入手呀!

          家好,我是皮湯。最近業(yè)務(wù)調(diào)整,組內(nèi)開啟了前端工程化方面的基建,我主要負(fù)責(zé) CSS 技術(shù)選型這一塊,針對(duì)目前業(yè)界主流的幾套方案進(jìn)行了比較完善的調(diào)研與比較,分享給大家。

          目前整個(gè) CSS 工具鏈、工程化領(lǐng)域的主要方案如下:

          而我們技術(shù)選型的標(biāo)準(zhǔn)如下:

          - 開發(fā)速度快

          - 開發(fā)體驗(yàn)友好

          - 調(diào)試體驗(yàn)友好

          - 可維護(hù)性友好

          - 擴(kuò)展性友好

          - 可協(xié)作性友好

          - 體積小

          - 有最佳實(shí)踐指導(dǎo)

          目前主要需要對(duì)比的三套方案:

          - Less/Sass + PostCSS 的純 CSS c側(cè)方案

          - styled-components / emotion 的純 CSS-in-JS 側(cè)方案

          - TailwindCSS 的以寫輔助類為主的 HTML 側(cè)方案

          ## 純 CSS 側(cè)方案

          ### 介紹與優(yōu)點(diǎn)

          > 維護(hù)狀態(tài):一般

          > Star 數(shù):16.7K

          > 支持框架:無框架限制

          > 項(xiàng)目地址:https://github.com/less/less.js

          Less/Sass + PostCSS 這種方案在目前主流的組件庫和企業(yè)級(jí)項(xiàng)目中使用很廣,如 ant-design 等

          它們的主要作用如下:

          - 為 CSS 添加了類似 JS 的特性,你也可以使用變量、mixin,寫判斷等

          - 引入了模塊化的概念,可以在一個(gè) less 文件中導(dǎo)入另外一個(gè) less 文件進(jìn)行使用

          - 兼容標(biāo)準(zhǔn),可以快速使用 CSS 新特性,兼容瀏覽器 CSS 差異等

          這類工具能夠與主流的工程化工具一起使用,如 Webpack,提供對(duì)應(yīng)的 loader 如 sass-loader,然后就可以在 React/Vue 項(xiàng)目中建 `.scss` 文件,寫 sass 語法,并導(dǎo)入到 React 組件中生效。

          比如我寫一個(gè)組件在響應(yīng)式各個(gè)斷點(diǎn)下的展示情況的 sass 代碼:

          ```

          .component {

          width: 300px;

          @media (min-width: 768px) {

          width: 600px;

          @media (min-resolution: 192dpi) {

          background-image: url(/img/retina2x.png);

          }

          }

          @media (min-width: 1280px) {

          width: 800px;

          }

          }

          ```

          或?qū)胍恍┯糜跇?biāo)準(zhǔn)化瀏覽器差異的代碼:

          ```

          @import "normalize.css";

          // component 相關(guān)的其他代碼

          ```

          ### 不足

          這類方案的一個(gè)主要問題就是,只是對(duì) CSS 本身進(jìn)行了增強(qiáng),但是在幫助開發(fā)者如何寫更好的 CSS、更高效、可維護(hù)的 CSS 方面并沒有提供任何建議。

          - 你依然需要自己定義 CSS 類、id,并且思考如何去用這些類、id 進(jìn)行組合去描述 HTML 的樣式

          - 你依然可能會(huì)寫很多冗余的 Less/Sass 代碼,然后造成項(xiàng)目的負(fù)擔(dān),在可維護(hù)性方面也有巨大問題

          ### 優(yōu)化

          - 可以引入 CSS 設(shè)計(jì)規(guī)范:BEM 規(guī)范,來輔助用戶在整個(gè)網(wǎng)頁的 HTML 骨架以及對(duì)應(yīng)的類上進(jìn)行設(shè)計(jì)

          - 可以引入 CSS Modules,將 CSS 文件進(jìn)行 “作用域” 限制,確保在之后維護(hù)時(shí),修改一個(gè)內(nèi)容不會(huì)引起全局中其他樣式的效果

          #### BEM 規(guī)范

          B (Block)、E(Element)、M(Modifier),具體就是通過塊、元素、行為來定義所有的可視化功能。

          拿設(shè)計(jì)一個(gè) Button 為例:

          ```

          /* Block */

          .btn {}

          /* 依賴于 Block 的 Element */

          .btn__price {}

          /* 修改 Block 風(fēng)格的 Modifier */

          .btn--orange {}

          .btn--big {}

          ```

          遵循上述規(guī)范的一個(gè)真實(shí)的 Button:

          ```

          <a class="btn btn--big btn--orange" href="#">

          <span class="btn__price"></span>

          <span class="btn__text">BIG BUTTON</span>

          </a>

          ```

          可以獲得如下的效果:

          #### CSS Modules

          CSS Modules 主要為 CSS 添加局部作用域和模塊依賴,使得 CSS 也能具有組件化。

          一個(gè)例子如下:

          ```

          import React from 'react';

          import style from './App.css';

          export default ()=> {

          return (

          <h1 className={style.title}>

          Hello World

          </h1>

          );

          };

          ```

          ```

          .title {

          composes: className;

          color: red;

          }

          ```

          上述經(jīng)過編譯會(huì)變成如下 hash 字符串:

          ```

          <h1 class="_3zyde4l1yATCOkgn-DBWEL">

          Hello World

          </h1>

          ```

          ```

          ._3zyde4l1yATCOkgn-DBWEL {

          color: red;

          }

          ```

          CSS Modules 可以與普通 CSS、Less、Sass 等結(jié)合使用。

          ## 純 JS 側(cè)方案

          ### 介紹與優(yōu)點(diǎn)

          > 維護(hù)狀態(tài):一般

          > Star 數(shù):35.2K

          > 支持框架:React ,通過社區(qū)支持 Vue 等框架

          > 項(xiàng)目地址:https://github.com/styled-components/styled-components

          使用 JS 的模板字符串函數(shù),在 JS 里面寫 CSS 代碼,這帶來了兩個(gè)認(rèn)知的改變:

          - 不是在根據(jù) HTML,然后去寫 CSS,而是站在組件設(shè)計(jì)的角度,為組件寫 CSS,然后應(yīng)用組件的組合思想搭建大應(yīng)用

          - 自動(dòng)提供類似 CSS Modules 的體驗(yàn),不用擔(dān)心樣式的全局污染問題

          同時(shí)帶來了很多 JS 側(cè)才有的各種功能特性,可以讓開發(fā)者用開發(fā) JS 的方式開發(fā) CSS,如編輯器自動(dòng)補(bǔ)全、Lint、編譯壓縮等。

          比如我寫一個(gè)按鈕:

          ```

          const Button=styled.button`

          /* Adapt the colors based on primary prop */

          background: ${props=> props.primary ? "palevioletred" : "white"};

          color: ${props=> props.primary ? "white" : "palevioletred"};

          font-size: 1em;

          margin: 1em;

          padding: 0.25em 1em;

          border: 2px solid palevioletred;

          border-radius: 3px;

          `;

          render(

          <div>

          <Button>Normal</Button>

          <Button primary>Primary</Button>

          </div>

          );

          ```

          可以獲得如下效果:

          還可以擴(kuò)展樣式:

          ```

          // The Button from the last section without the interpolations

          const Button=styled.button`

          color: palevioletred;

          font-size: 1em;

          margin: 1em;

          padding: 0.25em 1em;

          border: 2px solid palevioletred;

          border-radius: 3px;

          `;

          // A new component based on Button, but with some override styles

          const TomatoButton=styled(Button)`

          color: tomato;

          border-color: tomato;

          `;

          render(

          <div>

          <Button>Normal Button</Button>

          <TomatoButton>Tomato Button</TomatoButton>

          </div>

          );

          ```

          可以獲得如下效果:

          ### 不足

          雖然這類方案提供了在 JS 中寫 CSS,充分利用 JS 的插值、組合等特性,然后應(yīng)用 React 組件等組合思想,將組件與 CSS 進(jìn)行細(xì)粒度綁定,讓 CSS 跟隨著組件一同進(jìn)行組件化開發(fā),同時(shí)提供和組件類似的模塊化特性,相比 Less/Sass 這一套,可以復(fù)用 JS 社區(qū)的最佳實(shí)踐等。

          但是它仍然有一些不足:

          - 仍然是是對(duì) CSS 增強(qiáng),提供非常大的靈活性,開發(fā)者仍然需要考慮如何去組織自己的 CSS

          - 沒有給出一套 “有觀點(diǎn)” 的最佳實(shí)踐做法

          - 在上層也缺乏基于 styled-components 進(jìn)行復(fù)用的物料庫可進(jìn)行參考設(shè)計(jì)和使用,導(dǎo)致在初始化使用時(shí)開發(fā)速度較低

          - 在 JS 中寫 CSS,勢(shì)必帶來一些本屬于 JS 的限制,如 TS 下,需要對(duì) Styled 的組件進(jìn)行類型注釋

          - 官方維護(hù)的內(nèi)容只兼容 React 框架,Vue 和其他框架都由社區(qū)提供支持

          整體來說不太符合團(tuán)隊(duì)協(xié)作使用,需要人為總結(jié)最佳實(shí)踐和規(guī)范等。

          ### 優(yōu)化

          - 尋求一套寫 CSS 的最佳實(shí)踐和團(tuán)隊(duì)協(xié)作規(guī)范

          - 能夠擁有大量的物料庫或輔助類等,提高開發(fā)效率,快速完成應(yīng)用開發(fā)

          ## 偏向 HTML 側(cè)方案

          ### 介紹與優(yōu)點(diǎn)

          > 維護(hù)狀態(tài):積極

          > Star 數(shù):48.9K

          > 支持框架:React、Vue、Svelte 等主流框架

          > 項(xiàng)目地址:https://github.com/tailwindlabs/tailwindcss

          典型的是 TailwindCSS,一個(gè)輔助類優(yōu)先的 CSS 框架,提供如 `flex` 、`pt-4` 、`text-center` 、`rotate-90` 這樣實(shí)用的類名,然后基于這些底層的輔助類向上組合構(gòu)建任何網(wǎng)站,而且只需要專注于為 HTML 設(shè)置類名即可。

          一個(gè)比較形象的例子可以參考如下代碼:

          ```

          <button class="btn btn--secondary">Decline</button>

          <button class="btn btn--primary">Accept</button>

          ```

          上述代碼應(yīng)用 BEM 風(fēng)格的類名設(shè)計(jì),然后設(shè)計(jì)兩個(gè)按鈕,而這兩個(gè)類名類似主流組件庫里面的 Button 的不同狀態(tài)的設(shè)計(jì),而這兩個(gè)類又是由更加基礎(chǔ)的 TailwindCSS 輔助類組成:

          ```

          .btn {

          @apply text-base font-medium rounded-lg p-3;

          }

          .btn--primary {

          @apply bg-rose-500 text-white;

          }

          .btn--secondary {

          @apply bg-gray-100 text-black;

          }

          ```

          上面的輔助類包含以下幾類:

          - 設(shè)置文本相關(guān): `text-base` 、`font-medium` 、`text-white` 、`text-black`

          - 設(shè)置背景相關(guān)的:`bg-rose-500` 、`bg-gray-100`

          - 設(shè)置間距相關(guān)的:`p-3`

          - 設(shè)置邊角相關(guān)的:`rounded-lg`

          通過 Tailwind 提供的 `@apply` 方法來對(duì)這些輔助類進(jìn)行組合構(gòu)建更上層的樣式類。

          上述的最終效果展示如下:

          可以看到 TailwindCSS 將我們開發(fā)網(wǎng)站的過程抽象成為使用 Figma 等設(shè)計(jì)軟件設(shè)計(jì)界面的過程,同時(shí)提供了一套用于設(shè)計(jì)的規(guī)范,相當(dāng)于內(nèi)置最佳實(shí)踐,如顏色、陰影、字體相關(guān)的內(nèi)容,一個(gè)很形象的圖片可以說明這一點(diǎn):

          TailwindCSS 為我們規(guī)劃了一個(gè)元素可以設(shè)置的屬性,并且為每個(gè)屬性給定了一組可以設(shè)置的值,這些屬性+屬性值組合成一個(gè)有機(jī)的設(shè)計(jì)系統(tǒng),非常便于團(tuán)隊(duì)協(xié)作與共識(shí),讓我們開發(fā)網(wǎng)站就像做設(shè)計(jì)一樣簡單、快速,但是整體風(fēng)格又能保持一致。

          TailwindCSS 同時(shí)也能與主流組件庫如 React、Vue、Svelte 結(jié)合,融入基于組件的 CSS 設(shè)計(jì)思想,但又只需要修改 HTML 上的類名,如我們?cè)O(shè)計(jì)一個(gè)食譜組件:

          ```

          // Recipes.js

          import Nav from './Nav.js'

          import NavItem from './NavItem.js'

          import List from './List.js'

          import ListItem from './ListItem.js'

          export default function Recipes({ recipes }) {

          return (

          <div className="divide-y divide-gray-100">

          <Nav>

          <NavItem href="/featured" isActive>Featured</NavItem>

          <NavItem href="/popular">Popular</NavItem>

          <NavItem href="/recent">Recent</NavItem>

          </Nav>

          <List>

          {recipes.map((recipe)=> (

          <ListItem key={recipe.id} recipe={recipe} />

          ))}

          </List>

          </div>

          )

          }

          // Nav.js

          export default function Nav({ children }) {

          return (

          <nav className="p-4">

          <ul className="flex space-x-2">

          {children}

          </ul>

          </nav>

          )

          }

          // NavItem.js

          export default function NavItem({ href, isActive, children }) {

          return (

          <li>

          <a

          href={href}

          className={`block px-4 py-2 rounded-md ${isActive ? 'bg-amber-100 text-amber-700' : ''}`}

          >

          {children}

          </a>

          </li>

          )

          }

          // List.js

          export default function List({ children }) {

          return (

          <ul className="divide-y divide-gray-100">

          {children}

          </ul>

          )

          }

          //ListItem.js

          export default function ListItem({ recipe }) {

          return (

          <article className="p-4 flex space-x-4">

          <img src={recipe.image} alt="" className="flex-none w-18 h-18 rounded-lg object-cover bg-gray-100" width="144" height="144" />

          <div className="min-w-0 relative flex-auto sm:pr-20 lg:pr-0 xl:pr-20">

          <h2 className="text-lg font-semibold text-black mb-0.5">

          {recipe.title}

          </h2>

          <dl className="flex flex-wrap text-sm font-medium whitespace-pre">

          <div>

          <dt className="sr-only">Time</dt>

          <dd>

          <abbr title={`${recipe.time} minutes`}>{recipe.time}m</abbr>

          </dd>

          </div>

          <div>

          <dt className="sr-only">Difficulty</dt>

          <dd> · {recipe.difficulty}</dd>

          </div>

          <div>

          <dt className="sr-only">Servings</dt>

          <dd> · {recipe.servings} servings</dd>

          </div>

          <div className="flex-none w-full mt-0.5 font-normal">

          <dt className="inline">By</dt>{' '}

          <dd className="inline text-black">{recipe.author}</dd>

          </div>

          <div class="absolute top-0 right-0 rounded-full bg-amber-50 text-amber-900 px-2 py-0.5 hidden sm:flex lg:hidden xl:flex items-center space-x-1">

          <dt className="text-amber-500">

          <span className="sr-only">Rating</span>

          <svg width="16" height="20" fill="currentColor">

          <path d="M7.05 3.691c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.372 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.539 1.118l-2.8-2.034a1 1 0 00-1.176 0l-2.8 2.034c-.783.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.363-1.118L.98 9.483c-.784-.57-.381-1.81.587-1.81H5.03a1 1 0 00.95-.69L7.05 3.69z" />

          </svg>

          </dt>

          <dd>{recipe.rating}</dd>

          </div>

          </dl>

          </div>

          </article>

          )

          }

          ```

          上述食譜的效果如下:

          可以看到我們無需寫一行 CSS,而是在 HTML 里面應(yīng)用各種輔助類,結(jié)合 React 的組件化設(shè)計(jì),既可以輕松完成一個(gè)非常現(xiàn)代化且好看的食譜組件。

          除了上面的特性,TailwindCSS 在響應(yīng)式、新特性支持、Dark Mode、自定義配置、自定義新的輔助類、IDE 方面也提供非常優(yōu)秀的支持,除此之外還有基于 TailwindCSS 構(gòu)建的物料庫 Tailwind UI ,提供各種各樣成熟、好看、可用于生產(chǎn)的物料庫:

          ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1cb983da6f71470b91ac764d14907998~tplv-k3u1fbpfcp-zoom-1.image)

          因?yàn)樾枰远ǖ?CSS 不多,而需要自定義的 CSS 可以定義為可復(fù)用的輔助類,所以在可維護(hù)性方面也是極好的。

          ### 不足

          - 因?yàn)橐胍粋€(gè)額外的運(yùn)行時(shí),TailwindCSS 輔助類到 CSS 的編譯過程,而隨著組件越來越多,需要編譯的工作量也會(huì)變大,所以速度會(huì)有影響

          - 過于底層,相當(dāng)于給了用于設(shè)計(jì)的最基礎(chǔ)的指標(biāo),但是如果我們想要快速設(shè)計(jì)網(wǎng)站,那么可能還需要一致的、更加上層的組件庫

          - 相當(dāng)于引入了一套框架,具有一定的學(xué)習(xí)成本和使用成本

          ### 優(yōu)化

          - Tailwind 2.0 支持 [JIT](https://blog.tailwindcss.com/tailwindcss-2-1 "JIT"),可以大大提升編譯速度,可以考慮引入

          - 基于 TailwindCSS,設(shè)計(jì)一套符合自身風(fēng)格的上層組件庫、物料庫,便于更加快速開發(fā)

          - 提前探索、學(xué)習(xí)和總結(jié)一套教程與開發(fā)最佳實(shí)踐

          - 探索 styled-components 等結(jié)合 TailwindCSS 的開發(fā)方式

          ## 參考鏈接

          - [CSS 工程化發(fā)展歷程](https://bytedance.feishu.cn/docs/doccnTRF0OZtJMgKuo3y0hIDMbc# "CSS 工程化發(fā)展歷程")


          / 感謝支持/

          以上便是本次分享的全部內(nèi)容,希望對(duì)你有所幫助^_^

          喜歡的話別忘了 分享、點(diǎn)贊、收藏 三連哦~

          歡迎關(guān)注公眾號(hào) 程序員巴士,來自字節(jié)、蝦皮、招銀的三端兄弟,分享編程經(jīng)驗(yàn)、技術(shù)干貨與職業(yè)規(guī)劃,助你少走彎路進(jìn)大廠。

          們每天都在網(wǎng)上摸魚,作為前端開發(fā)人員,網(wǎng)站上微妙的細(xì)節(jié)變化通過比別人會(huì)更關(guān)注。我一直注意到的一件事是網(wǎng)站上的動(dòng)畫的流暢性。動(dòng)畫對(duì)于用戶體驗(yàn)來說是非常好的,有時(shí)我們可以一些有趣的動(dòng)畫來留住用戶。

          創(chuàng)建高級(jí)動(dòng)畫聽起來是一個(gè)很難的話題,但好消息是,在CSS中,可以將多個(gè)簡單的動(dòng)畫相互疊加,以創(chuàng)建一個(gè)更復(fù)雜的動(dòng)畫

          在這節(jié)課中,我們會(huì)學(xué)習(xí)如下幾點(diǎn):

          • 什么是貝塞爾曲線,以及如何用一行CSS來創(chuàng)建一個(gè) "復(fù)雜"的動(dòng)畫
          • 如何將動(dòng)畫相互疊加以創(chuàng)建一個(gè)高級(jí)動(dòng)畫
          • 如何通過應(yīng)用上面學(xué)到的兩點(diǎn)來創(chuàng)建一個(gè)過山車動(dòng)畫

          什么是貝塞爾曲線

          CSS中的 cubic-bezier 函數(shù)是一個(gè)緩動(dòng)函數(shù),可以讓我們完全控制動(dòng)畫在時(shí)間上的表現(xiàn)。下面是官方的定義:

          貝塞爾緩動(dòng)函數(shù)是一種由四個(gè)實(shí)數(shù)定義的緩和函數(shù),指定了貝塞爾曲線的兩個(gè)控制點(diǎn)P1P2,其端點(diǎn)P0P3分別固定在(0, 0)和(1, 1)P1P2x坐標(biāo)被限制在[0, 1]范圍內(nèi)。

          什么是緩動(dòng)函數(shù)?

          線性曲線

          想象兩個(gè)點(diǎn)P0P1,其中P0是動(dòng)畫的起點(diǎn),P1是結(jié)束點(diǎn)。現(xiàn)在想象另一個(gè)點(diǎn)在兩點(diǎn)之間線性移動(dòng),如下所示

          這就是所謂的線性曲線,也是最簡單的動(dòng)畫。

          二次貝塞爾曲線

          如下圖所示,有三個(gè)點(diǎn)。P0、P1和P2。我們想讓動(dòng)畫從P0移動(dòng)到P2。在這種情況下,P1是一個(gè)控制點(diǎn),控制動(dòng)畫的曲線。

          二次方貝塞爾概念:

          • 在P0和P1之間以及P1和P2之間(用灰線表示)連接虛線
          • 點(diǎn)Q0沿著P0和P1之間的直線移動(dòng)。同時(shí),點(diǎn)Q1沿著P1和P2之間的直線移動(dòng)
          • 在Q0和Q1之間連接一條虛線(用綠線表示)
          • 在Q0和Q1開始移動(dòng)的同時(shí),點(diǎn)B開始沿著綠線移動(dòng),B點(diǎn)所走的路徑就是動(dòng)畫路徑

          請(qǐng)注意,Q0、Q1和B不以相同的速度移動(dòng)。它們都必須在同一時(shí)間開始,并在同一時(shí)間完成它們的路徑。因此,每一個(gè)點(diǎn)都是根據(jù)它所移動(dòng)的線長以適當(dāng)?shù)乃俣纫苿?dòng)的。

          三次貝塞爾曲線

          三次貝塞爾曲線由4個(gè)點(diǎn)組成。P0, P1, P2和P3。動(dòng)畫開始于P0,結(jié)束于P3。P1和P2是我們的控制點(diǎn)。

          三次貝賽爾的工作原理如下:

          • 在(P0, P1)、(P1, P2)和(P2, P3)之間連接虛線,由灰線表示
          • 點(diǎn)Q0、Q1和Q2分別沿直線(P0,P1)、(P1,P2)和(P2,P3)移動(dòng)
          • 在(Q0, Q1)和(Q1, Q2)之間連接虛線,它們由綠線表示。
          • 點(diǎn)R0和R1分別沿直線(Q0, Q1)和(Q1, Q2)移動(dòng)
          • 連接R0和R1之間的線(用藍(lán)線表示)
          • 最后,B點(diǎn)沿著R0和R1之間的連接線移動(dòng),B點(diǎn)所走的路徑就是動(dòng)畫路徑

          如果你想更好地了解三次體貝塞爾的工作原理,建議你看看這個(gè)desmos鏈接。玩玩控制點(diǎn),看看動(dòng)畫如何隨時(shí)間變化。(注意,鏈接中的動(dòng)畫是由黑線表示的)。

          疊加動(dòng)畫

          有很多步驟的大動(dòng)畫可以被分解成多個(gè)小動(dòng)畫。在 css 中,通過添加animation-delay屬性來實(shí)現(xiàn)這一點(diǎn)。計(jì)算延遲很簡單,把你要計(jì)算動(dòng)畫延遲的那個(gè)動(dòng)畫之前的所有動(dòng)畫的時(shí)間加起來。

          例如:

          animation: movePointLeft 4s linear forwards, movePointDown 3s linear forwards;
          

          這里,我們有兩個(gè)動(dòng)畫,movePointLeftmovePointDownmovePointLeft的動(dòng)畫延遲是零,因?yàn)樗俏覀兿胂冗\(yùn)行的動(dòng)畫。movePointDown的動(dòng)畫延遲是4秒,因?yàn)?span style="color: #EF7060; --tt-darkmode-color: #EF7060;">movePointLeft將在這段時(shí)間后完成。

          因此,animation-delay屬性:

          animation-delay: 0s, 4s;
          

          注意,如果有兩個(gè)或更多的動(dòng)畫同時(shí)開始,它們的動(dòng)畫延遲將是一樣的。此外,當(dāng)你計(jì)算即將開始的動(dòng)畫的延遲時(shí),把它們視為一個(gè)動(dòng)。例如 :

          animation: x 4s linear forwards, y 4s linear forwards, jump 2s linear forwards;
          

          假設(shè)xy同時(shí)開始。在這種情況下,xy的動(dòng)畫延遲都將為零,而 jump 動(dòng)畫的延遲將為4秒(而不是8秒!)。

          animation-delay: 0s, 0s, 4s;
          

          創(chuàng)建過山車

          掌握了上面的知識(shí),是時(shí)候應(yīng)用一下了。

          了解動(dòng)畫

          過山車路徑由三部分組成:

          • 滑動(dòng)部分
          • 循環(huán)部分
          • 還會(huì)有一些動(dòng)畫,在上面的兩個(gè)動(dòng)畫之間創(chuàng)造水平空間

          我們將首先創(chuàng)建一個(gè)簡單的球,作為我們過山車的 "車"。

          hmtl 部分:

          <div id="the-cart" class="cart"></div>
          

          css 部分:

          .cart {
            background-color: rgb(100, 210, 128);
            height: 50px;
            width: 50px;
            border: 1px solid black;
            border-radius: 50px;
            position: absolute;
            left: 10vw;
            top: 30vh;
          }
          
          

          滑動(dòng)部分

          創(chuàng)建小球滑動(dòng)的部分可以用cubic-bezier函數(shù)來完成! 這個(gè)動(dòng)畫是由2個(gè)動(dòng)畫組成的,一個(gè)是沿x軸的動(dòng)畫,另一個(gè)是沿y軸的動(dòng)畫。X軸動(dòng)畫是一個(gè)沿X軸的普通線性動(dòng)畫。它的關(guān)鍵幀如下:

          @keyframes x {
            to {
              left: 40vw;
            }
          
          

          將其添加到球路徑的 animation 屬性中,如下所示

          animation: x 4s linear forwards
          
          

          y軸動(dòng)畫是我們將使用cubic-bezier函數(shù)的部分。首先定義動(dòng)畫的關(guān)鍵幀。我們希望起始點(diǎn)和結(jié)束點(diǎn)之間的差異很小,以至于球達(dá)到的高度幾乎相同。

          @keyframes y {
            to {
              top: 29.99vh;
            }
          }}
          
          

          現(xiàn)在讓我們來思考一下cubic-bezier函數(shù)。我們希望我們的路徑先向右緩慢移動(dòng),然后當(dāng)它滑動(dòng)時(shí),它應(yīng)該走得更快。

          • 向右緩慢移動(dòng)意味著$P1$將沿x軸移動(dòng)。所以,我們知道它是在(V,0)。
          • 我們需要選擇一個(gè)合適的V,使我們的動(dòng)畫緩慢地向右移動(dòng),但又不能太多,以免占用整個(gè)空間。在這種情況下,我發(fā)現(xiàn)0.55最適合。
          • 為了達(dá)到滑動(dòng)效果,我們需要將P2Y軸下移(負(fù)值),所以P2=(X,-Y)
            • Y應(yīng)該是一個(gè)大值。在這種情況下,我選擇 Y=5000
            • 為了得到X,我們知道我們的動(dòng)畫速度在滑動(dòng)時(shí)應(yīng)該更快,在再次上升時(shí)應(yīng)該更慢。所以,X越接近于零,動(dòng)畫在滑動(dòng)時(shí)就越陡峭。在這種情況下,讓X=0.8

          現(xiàn)在,我們得到了一個(gè)cubic-bezier函數(shù):

          cubic-bezier(0.55, 0, 0.2, -800).
          

          為動(dòng)畫屬性添加關(guān)鍵幀:

          animation: x 4s linear forwards,
              y 4s cubic-bezier(0.55, 0, 0.2, -5000) forwards;
          

          這是我們動(dòng)畫的第一部分,所以動(dòng)畫延遲為零。我們應(yīng)該添加一個(gè)animation-delay屬性,因?yàn)閺南旅娴膭?dòng)畫開始,動(dòng)畫的開始時(shí)間將與第一個(gè)動(dòng)畫不同。

          animation-delay: 0s, 0s;
          

          地址:https://codepen.io/smashingmag/pen/VwxXBQb

          添加水平空間

          在做循環(huán)之前,球應(yīng)該沿著X軸移動(dòng)一小會(huì)兒,所以兩個(gè)動(dòng)畫之間有空間。

          定義關(guān)鍵幀

          @keyframes x2 {
            to {
              left: 50vw;
            }
          }
          

          把它添加到 animation 屬性中:

          animation: x 4s linear forwards,
              y 4s cubic-bezier(0.55, 0, 0.2, -5000) forwards, x2 0.5s linear forwards;
          

          這個(gè)動(dòng)畫應(yīng)該在滑動(dòng)動(dòng)畫之后開始,而滑動(dòng)動(dòng)畫需要4秒,因此,動(dòng)畫延遲將是4秒。

          animation-delay: 0s, 0s, 4s;
          

          地址:https://codepen.io/smashingmag/pen/dyemExY

          循環(huán)部分

          要在CSS中創(chuàng)建一個(gè)圓(循環(huán)),我們需要把圓移到循環(huán)的中心,然后從那里開始做動(dòng)畫。圓的半徑是100px,所以我們把圓的位置改為top: 20vh30是期望的半徑(這里是10vh))。然而,這需要在滑動(dòng)動(dòng)畫完成后發(fā)生,所以我們將創(chuàng)建另一個(gè)持續(xù)時(shí)間為0秒的動(dòng)畫,并添加一個(gè)合適的動(dòng)畫延遲。

          關(guān)鍵幀:

          @keyframes pointOfCircle {
            to {
              top: 20vh;
            }
          }
          

          添加到 animation 動(dòng)畫中:

          animation: x 4s linear forwards,
              y 4s cubic-bezier(0.55, 0, 0.2, -5000) forwards, x2 0.5s linear forwards,
              pointOfCircle 0s linear forwards;
          
          

          添加動(dòng)畫延遲, 4.5s:

          animation-delay: 0s, 0s, 4s, 4.5s;
          

          循環(huán)本身

          創(chuàng)建一個(gè)循環(huán)動(dòng)畫:

          • 創(chuàng)建一個(gè)關(guān)鍵幀,將球移回原來的位置,然后旋轉(zhuǎn)球。
          @keyframes loop {
            from {
              transform: rotate(0deg) translateY(10vh) rotate(0deg);
            }
            to {
              transform: rotate(-360deg) translateY(10vh) rotate(360deg);
            }
          }
          
          

          添加到 animation 中:

          animation: x 4s linear forwards,
              y 4s cubic-bezier(0.55, 0, 0.2, -5000) forwards, x2 0.5s linear forwards,
              pointOfCircle 0s linear forwards, loop 3s linear forwards;
          
          

          添加動(dòng)畫延遲,這里是4.5s:

          animation-delay: 0s, 0s, 4s, 4.5s, 4.5s;
          

          地址:https://codepen.io/smashingmag/pen/mdLxZdR

          添加水平空間

          快完成了,最后 只需要在動(dòng)畫之后沿著x軸移動(dòng)球,這樣球就不會(huì)像上圖中那樣在循環(huán)之后完全停止。

          關(guān)鍵幀:

          @keyframes x3 {
            to {
              left: 70vw;
            }
          }
          

          添加到 animation 中:

          animation: x 4s linear forwards,
              y 4s cubic-bezier(0.55, 0, 0.2, -800) forwards, x2 0.5s linear forwards,
              pointOfCircle 0s linear forwards, loop 3s linear forwards,
              x3 2s linear forwards;
          
          

          加上適當(dāng)?shù)难舆t,這里是7.5s:

          animation-delay: 0s, 0s, 4s, 4.5s, 4.5s, 7.5s;
          

          地址:https://codepen.io/smashingmag/pen/wvjmLKp

          總結(jié)

          在本節(jié)中,我們介紹了如何結(jié)合多個(gè)關(guān)鍵幀來創(chuàng)建一個(gè)復(fù)雜的動(dòng)畫路徑。我們還介紹了貝塞爾以及如何使用它們來創(chuàng)建你自己的緩動(dòng)函數(shù)。建議大家自己多多動(dòng)手,才能更好的掌握 css 動(dòng)畫。

          來源:https://www.smashingmagazine.com/2022/10/advanced-animations-css/


          主站蜘蛛池模板: 亚洲福利一区二区| 文中字幕一区二区三区视频播放| 2021国产精品一区二区在线| 日韩精品一区二区三区中文精品| 91国偷自产一区二区三区| 国产美女露脸口爆吞精一区二区| 人妻少妇精品视频一区二区三区| 色婷婷av一区二区三区仙踪林| 一区二区在线免费视频| 国产伦精品一区二区三区视频猫咪 | 国产suv精品一区二区33| 国产裸体歌舞一区二区| 国产内射在线激情一区| 无码人妻一区二区三区一| 日韩成人无码一区二区三区 | 蜜臀Av午夜一区二区三区| chinese国产一区二区| 久久精品视频一区二区三区 | 日韩一区二区三区免费体验| 日韩精品在线一区二区| 天天看高清无码一区二区三区| 亚洲av片一区二区三区| 中文乱码字幕高清一区二区| 精品国产伦一区二区三区在线观看| 亚洲一区二区精品视频| 精品一区二区三区高清免费观看| 亚洲乱码一区二区三区国产精品| 国产成人AV一区二区三区无码| 中文字幕一区二区三区永久 | 无码丰满熟妇浪潮一区二区AV| 国产一区二区中文字幕| 中文字幕久久久久一区| 无码毛片一区二区三区中文字幕| 国产在线观看精品一区二区三区91| 亚洲中文字幕乱码一区| 国产精品av一区二区三区不卡蜜| 精品一区高潮喷吹在线播放| 亚洲熟妇无码一区二区三区导航| 亚洲一区二区久久| 国产MD视频一区二区三区| 美女AV一区二区三区|