<dfn id="hx5t3"><strike id="hx5t3"><em id="hx5t3"></em></strike></dfn>

    <thead id="hx5t3"></thead><nobr id="hx5t3"><font id="hx5t3"><rp id="hx5t3"></rp></font></nobr>

    <listing id="hx5t3"></listing>

    <var id="hx5t3"></var>
    <big id="hx5t3"></big>

      
      

      <output id="hx5t3"><ruby id="hx5t3"></ruby></output>
      <menuitem id="hx5t3"><dfn id="hx5t3"></dfn></menuitem>

      <big id="hx5t3"></big>

        chokcoco

        chokcoco 查看完整檔案

        深圳編輯  |  填寫畢業院校深圳騰訊公司  |  切圖仔 編輯 github.com/chokcoco 編輯
        編輯

        坎坷切圖仔

        個人動態

        chokcoco 發布了文章 · 3月31日

        巧用 SVG 濾鏡還能制作表情包?

        本文將介紹一些使用 SVG feTurbulence 濾鏡實現的一些有趣、大膽的的動效。

        系列另外兩篇:

        背景

        今天在群里面聊天,看到有人發這個表情包:

        QQ圖片20210327165223

        剛好最近一直在學習 SVG,腦海中就把這個表情包的效果和 feTurbulence 濾鏡關聯了起來。

        如果我們有一張類似上圖表情包的靜態圖,利用 feTurbulence 生成的噪聲函數,運用在靜態的表情包之上,再添加些許動畫,是不是也能制作一張類似的動圖效果呢?

        什么是 SVG feTurbulence 濾鏡?

        如果你對 SVG 濾鏡還不算太了解,可以簡單看看我的這篇文章入門:有意思!強大的 SVG 濾鏡

        這里我們會用到 SVG 中的 feTurbulence 濾鏡。再簡單介紹下。

        feTurbulence 濾鏡

        turbulence 意為湍流,不穩定氣流,而 SVG <feTurbulence> 濾鏡能夠實現半透明的煙熏或波狀圖像。通常用于實現一些特殊的紋理。濾鏡利用 Perlin 噪聲函數創建了一個圖像。噪聲在模擬云霧效果時非常有用,能產生非常復雜的質感,利用它可以實現了人造紋理比如說云紋、大理石紋的合成。

        簡單看個 DEMO:

        <div>Coco</div>
        <div class="turbulence">Coco</div>
        
        <svg>
            <filter id="fractal" filterUnits="objectBoundingBox" x="0%" y="0%" width="100%" height="100%">
                <feTurbulence id="turbulence" type="fractalNoise" baseFrequency="0.03" numOctaves="1" />
                <feDisplacementMap in="SourceGraphic" scale="50"></feDisplacementMap>
            </filter>
        </svg>
        .turbulence {
            filter: url(#fractal);
        }

        左邊是正常的效果,后邊是應用了 <feTurbulence> 的效果,你可以試著點進 Demo,更改 baseFrequencynumOctaves 參數的大小,可以看到不同的效果:

        image

        CodePen Demo -- feTurbulence text demo

        feTurbulence 濾鏡應用于圖片

        我們嘗試把上述 DEMO 中的文字轉換成圖片。我找到了一張靜態的哭的表情包:

        image

        簡單改造下代碼:

        <div></div>
        <svg>
            <filter id="fractal" filterUnits="objectBoundingBox" x="0%" y="0%" width="100%" height="100%">
                <feTurbulence id="turbulence" type="fractalNoise" baseFrequency="0.09" numOctaves="1" ></feTurbulence>
                <feDisplacementMap in="SourceGraphic" scale="15"></feDisplacementMap>
            </filter>
        </svg>
        div {
            background: url(image.jpg);
            filter: url(#fractal);
        }

        效果如下:

        image

        有點那個意思了,我們通過 feTurbulence 濾鏡得到了噪聲圖形,然后通過 feDisplacementMap 濾鏡根據 feTurbulence 所產生的噪聲圖形進行形變,扭曲,液化,得到最終的效果。

        通過調整 feTurbulence 中的 baseFrequencynumOctaves 以及 feDisplacementMap 中的 scale 參數,我們可以調試得到不同的效果。

        接下來,我們再給上述濾鏡添加一個動畫,利用 SVG 的 animate 標簽,動態的改變 baseFrequency 參數:

        <svg>
            <filter id="fractal" filterUnits="objectBoundingBox" x="0%" y="0%" width="100%" height="100%">
                <feTurbulence id="turbulence" type="fractalNoise" baseFrequency="0.1 0.1" numOctaves="1" >
                    <animate
                            attributeName="baseFrequency"
                            from="0.1 0.1"
                            to="0.08 0.01"
                            dur="3.5s"
                            repeatCount="indefinite"/>
                </feTurbulence>
                <feDisplacementMap in="SourceGraphic" scale="15"></feDisplacementMap>
            </filter>
        </svg>

        添加了動畫之后,同樣作用于圖片之上,我們就可以得到如下的效果:

        6

        由于截圖軟件的幀率問題,看著有點慢,你可以戳進 DEMO 看看實際效果,還是挺有意思的,至此我們就簡單的利用 CSS 配合 SVG 的方式,通過一張靜態圖得到了一個動態的表情包啦。

        CodePen Demo -- 使用 SVG 濾鏡 feTurbulence 讓圖片動起來

        巧用 feTurbulence 濾鏡實現各種動效

        嘿,feTurbulence 當然不是僅能實現這個而已,下面我們再探索一些有意思的場景。

        首先,再明確下我們主要使用到的兩個濾鏡 feTurbulencefeDisplacementMap,它們的核心代碼:

        <svg>
            <filter id="feDisplacementMap" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="64">
                <feTurbulence type="fractalNoise" baseFrequency="0.0995" numOctaves="1" result="img" />
                <feDisplacementMap id="feDis" in="SourceGraphic" in2="img" scale="600" />
            </filter>
        </svg>

        其中濾鏡中的幾個參數 -- baseFrequency、numOctaves、scale 的改變其實都會得到不一樣的效果。我們動態的變化其中的一個或多個也都可以得到不同的動畫效果。

        動態改變 feDisplacementMapscale 的參數

        feDisplacementMap 濾鏡是用于改變元素和圖形的像素位置的。該濾鏡通過遍歷原圖形的所有像素點,通過 feTurbulence 濾鏡產生的噪聲函數將原圖像的每個像素點重新映射到一個新的位置,形成一個新的圖形。

        scale 表示新得到的圖像的扭曲程度,這個值越大,圖像越加扭曲不可識別。

        通過設置一個非常大初始值,我們可以完全將輸入的任何源圖像粒子化,看看這個 Demo:

        <div></div>
        <div class="fractal"></div>
        
        <svg viewBox="0 0 200 200" width="200px" height="200px">
            <defs>
                <filter id="fractal" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
                    <feTurbulence type="fractalNoise" baseFrequency="0.995" numOctaves="10" seed="1" stitchTiles="noStitch" result="img" />
                    <feDisplacementMap in="SourceGraphic" in2="img" xChannelSelector="R" yChannelSelector="G" scale="600" />
                </filter>
            </defs>
        </svg>
        div {
            width: 200px;
            height: 200px;
            background: url(image.jpeg)
        }
        
        .fractal {
            filter: url(#fractal);
        }

        左邊為正常的圖像,右邊為作用了設置了 SVG 濾鏡效果的圖像,并且設置了 scale="600",完全將圖片粒子化了:

        image

        這個時候,讓濾鏡的 scale="600" 動態變化回 scale="1"(當此參數為 1 時,圖像表示為正常狀態),也就能實現一個圖形從粒子化到正?;霓D變:

        <svg viewBox="0 0 200 200" width="200px" height="200px">
            <filter id="fractal" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
                <feTurbulence type="fractalNoise" baseFrequency="0.995" numOctaves="10" seed="1" result="img" />
                <feDisplacementMap in="SourceGraphic" in2="img" xChannelSelector="R" yChannelSelector="G" scale="600">
                    <animate attributeName="scale" values="600;0;0" keyTimes="0;0.75;1" begin="0s" dur="2s" repeatCount="indefinite" />
                </feDisplacementMap>
            </filter>
        </svg>

        效果如下:

        CodePen -- SVG Filter feTurbulence & feDisplacementMap 實現圖片粒子化復原動畫

        動態改變 feDisplacementMapscale 的參數實現一些開獎動效

        基于上述的效果,我們可以實現這樣一類效果,譬如一些開獎結果,一開始它是模糊的,但是用戶點擊之后,模糊的結果逐漸從模糊到真實。

        但是點擊事件,由于 SVG Animate 標簽的一些限制,需要借助一些 Javascript 代碼,這里借用 JQuery 簡單做個示意。

        我們有一串開獎數組 745846,實現點擊之后從模糊到真實:

        <div id="fe1" class="fe1">745846</div>
        <svg>
            <filter id="feDisplacementMap" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="64">
                <feTurbulence type="fractalNoise" baseFrequency="0.0995" numOctaves="1" result="img" />
                <feDisplacementMap id="feDis" in="SourceGraphic" in2="img" scale="200" />
            </filter>
        </svg>
        $("#fe1").click((e) => {
            const filter = $("#feDis");
            const startTime = Date.now();
            const duration = 1000;
            const target = 200;
            
            requestAnimationFrame(function aniMove() {
                const t = Math.min(1, (Date.now() - startTime) / duration);
                const nextTarget = target - (t * target) + 1;
                
                filter.attr('scale', nextTarget);
        
                if (t < 1.0) {
                    requestAnimationFrame(aniMove);
                }
            });
        });

        點擊之前的狀態如下:

        image

        點擊之后:

        8

        上述效果,你可以套用到任何地方,完整的 Demo 地址:

        CodePen Demo -- SVG Filter Button Effects

        動態改變 feDisplacementMapscale 的參數實現一些 fadeOut 動畫

        當然,上述的效果也是可以反著來的,就是一張圖(或者任何元素),點擊之后粒子化,然后漸變的消失,進階版的 fadeOut 效果。

        通過動態的改變濾鏡的參數和圖片的透明度,當然,也需要借助一些 JavaScript 代碼,完整的代碼就不貼了,直接上效果圖:

        Kapture 2021-03-29 at 21 02 03

        是不是非常類似滅霸把人物消失的效果?之前看過這樣一篇文章 - 谷歌滅霸彩蛋的效果實現,其中介紹了一種使用 Canvas 實現類似效果的方式,本文這里使用 SVG 濾鏡達成了近似的效果。

        對源碼感興趣的可以猛戳下面的 Demo,效果也是可以方便的移植到其他元素之上:

        CodePen Demo -- 使用 SVG 濾鏡實現任意元素粒子化 FadeOut 效果

        不要吹滅你的靈感和你的想象力; 不要成為你的模型的奴隸。 ——文森特?梵高

        最后

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

        更多精彩 CSS 技術文章匯總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。

        想 Get 到最有意思的 CSS 資訊,千萬不要錯過我的公眾號 -- iCSS前端趣聞 ??

        gzh_sssmall.png

        如果還有什么疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

        查看原文

        贊 1 收藏 0 評論 0

        chokcoco 關注了用戶 · 3月30日

        子君 @zijun_5f156624be160

        微信公眾號: 前端有得玩
        微信賬號:snowzijun
        github倉庫: https://github.com/snowzijun
        寄語:不要吹滅你的靈感和你的想象力; 不要成為你的模型的奴隸。

        關注 563

        chokcoco 贊了文章 · 3月30日

        Vue中使用裝飾器,我是認真的

        產品上線事繁多,測試產品催不離。
        休問Bug剩多少,眼圈如漆身如泥。

        作為一個曾經的Java coder, 當我第一次看到js里面的裝飾器(Decorator)的時候,就馬上想到了Java中的注解,當然在實際原理和功能上面,Java的注解和js的裝飾器還是有很大差別的。本文題目是Vue中使用裝飾器,我是認真的,但本文將從裝飾器的概念開發聊起,一起來看看吧。

        通過本文內容,你將學到以下內容:

        1. 了解什么是裝飾器
        2. 在方法使用裝飾器
        3. class中使用裝飾器
        4. Vue中使用裝飾器
        本文首發于公眾號【前端有的玩】,不想當咸魚,想要換工作,關注公眾號,帶你每日一起刷大廠面試題,關注 === 大廠offer。

        什么是裝飾器

        裝飾器是ES2016提出來的一個提案,當前處于Stage 2階段,關于裝飾器的體驗,可以點擊 https://github.com/tc39/proposal-decorators查看詳情。裝飾器是一種與類相關的語法糖,用來包裝或者修改類或者類的方法的行為,其實裝飾器就是設計模式中裝飾者模式的一種實現方式。不過前面說的這些概念太干了,我們用人話來翻譯一下,舉一個例子。

        在日常開發寫bug過程中,我們經常會用到防抖和節流,比如像下面這樣

        class MyClass {
          follow = debounce(function() {
            console.log('我是子君,關注我哦')
          }, 100)
        }
        
        const myClass = new MyClass()
        // 多次調用只會輸出一次
        myClass.follow()
        myClass.follow()

        上面是一個防抖的例子,我們通過debounce函數將另一個函數包起來,實現了防抖的功能,這時候再有另一個需求,比如希望在調用follow函數前后各打印一段日志,這時候我們還可以再開發一個log函數,然后繼續將follow包裝起來

        /**
         * 最外層是防抖,否則log會被調用多次
         */
        class MyClass {
          follow = debounce(
            log(function() {
              console.log('我是子君,關注我哦')
            }),
            100
          )
        }

        上面代碼中的debouncelog兩個函數,本質上是兩個包裝函數,通過這兩個函數對原函數的包裝,使原函數的行為發生了變化,而js中的裝飾器的原理就是這樣的,我們使用裝飾器對上面的代碼進行改造

        class MyClass {
          @debounce(100)
          @log
          follow() {
            console.log('我是子君,關注我哦')
          }
        }

        裝飾器的形式就是 @ + 函數名,如果有參數的話,后面的括號里面可以傳參

        在方法上使用裝飾器

        裝飾器可以應用到class上或者class里面的屬性上面,但一般情況下,應用到class屬性上面的場景會比較多一些,比如像上面我們說的log,debounce等等,都一般會應用到類屬性上面,接下來我們一起來具體看一下如何實現一個裝飾器,并應用到類上面。在實現裝飾器之前,我們需要先了解一下屬性描述符

        了解一下屬性描述符

        在我們定義一個對象里面的屬性的時候,其實這個屬性上面是有許多屬性描述符的,這些描述符標明了這個屬性能不能修改,能不能枚舉,能不能刪除等等,同時ECMAScript將這些屬性描述符分為兩類,分別是數據屬性和訪問器屬性,并且數據屬性與訪問器屬性是不能共存的。

        數據屬性

        數據屬性包含一個數據值的位置,在這個位置可以讀取和寫入值。數據屬性包含了四個描述符,分別是

        1. configurable

          表示能不能通過delete刪除屬性,能否修改屬性的其他描述符特性,或者能否將數據屬性修改為訪問器屬性。當我們通過let obj = {name: ''}聲明一個對象的時候,這個對象里面所有的屬性的configurable描述符的值都是true

        2. enumerable

          表示能不能通過for in或者Object.keys等方式獲取到屬性,我們一般聲明的對象里面這個描述符的值是true,但是對于class類里面的屬性來說,這個值是false

        3. writable

          表示能否修改屬性的數據值,通過將這個修改為false,可以實現屬性只讀的效果。

        4. value

          表示當前屬性的數據值,讀取屬性值的時候,從這里讀??;寫入屬性值的時候,會寫到這個位置。

        訪問器屬性

        訪問器屬性不包含數據值,他們包含了gettersetter兩個函數,同時configurableenumerable是數據屬性與訪問器屬性共有的兩個描述符。

        1. getter

          在讀取屬性的時候調用這個函數,默認這個函數為undefined

        2. setter

          在寫入屬性值的時候調用這個函數,默認這個函數為undefined

        了解了這六個描述符之后,你可能會有幾個疑問: 我如何去定義修改這些屬性描述符?這些屬性描述符與今天的文章主題有什么關系?接下來是揭曉答案的時候了。

        使用Object.defineProperty

        了解過vue2.0雙向綁定原理的同學一定知道,Vue的雙向綁定就是通過使用Object.defineProperty去定義數據屬性的gettersetter方法來實現的,比如下面有一個對象

        let obj = {
          name: '子君',
          officialAccounts: '前端有的玩'
        }

        我希望這個對象里面的用戶名是不能被修改的,用Object.defineProperty該如何定義呢?

        Object.defineProperty(obj,'name', {
          // 設置writable 是 false, 這個屬性將不能被修改
          writable: false
        })
        // 修改obj.name
        obj.name = "君子"
        // 打印依然是子君
        console.log(obj.name)

        通過Object.defineProperty可以去定義或者修改對象屬性的屬性描述符,但是因為數據屬性與訪問器屬性是互斥的,所以一次只能修改其中的一類,這一點需要注意。

        定義一個防抖裝飾器

        裝飾器本質上依然是一個函數,不過這個函數的參數是固定的,如下是防抖裝飾器的代碼

        /**
        *@param wait 延遲時長
        */
        function debounce(wait) {
          return function(target, name, descriptor) {
            descriptor.value = debounce(descriptor.value, wait)
          }
        }
        // 使用方式
        class MyClass {
          @debounce(100)
          follow() {
            console.log('我是子君,我的公眾號是 【前端有的玩】,關注有驚喜哦')
          }
        }

        我們逐行去分析一下代碼

        1. 首先我們定義了一個 debounce函數,同時有一個參數wait,這個函數對應的就是在下面調用裝飾器時使用的@debounce(100)
        2. debounce函數返回了一個新的函數,這個函數即裝飾器的核心,這個函數有三個參數,下面逐一分析

          1. target: 這個類屬性函數是在誰上面掛載的,如上例對應的是MyClass
          2. name: 這個類屬性函數的名稱,對應上面的follow
          3. descriptor: 這個就是我們前面說的屬性描述符,通過直接descriptor上面的屬性,即可實現屬性只讀,數據重寫等功能
        3. 然后第三行 descriptor.value = debounce(descriptor.value, wait), 前面我們已經了解到,屬性描述符上面的value對應的是這個屬性的值,所以我們通過重寫這個屬性,將其用debounce函數包裝起來,這樣在函數調用follow時實際調用的是包裝后的函數

        通過上面的三步,我們就實現了類屬性上面可使用的裝飾器,同時將其應用到了類屬性上面

        class上使用裝飾器

        裝飾器不僅可以應用到類屬性上面,還可以直接應用到類上面,比如我希望可以實現一個類似Vue混入那樣的功能,給一個類混入一些方法屬性,應該如何去做呢?

        // 這個是要混入的對象
        const methods = {
          logger() {
            console.log('記錄日志')
          }
        }
        
        // 這個是一個登陸登出類
        class Login{
          login() {}
          logout() {}
        }

        如何將上面的methods混入到Login中,首先我們先實現一個類裝飾器

        function mixins(obj) {
          return function (target) {
            Object.assign(target.prototype, obj)  
          }
        }
        
        // 然后通過裝飾器混入
        @mixins(methods)
        class Login{
          login() {}
          logout() {}
        }
        

        這樣就實現了類裝飾器。對于類裝飾器,只有一個參數,即target,對應的就是這個類本身。

        了解完裝飾器,我們接下來看一下如何在Vue中使用裝飾器。

        Vue中使用裝飾器

        使用ts開發Vue的同學一定對vue-property-decorator不會感到陌生,這個插件提供了許多裝飾器,方便大家開發的時候使用,當然本文的中點不是這個插件。其實如果我們的項目沒有使用ts,也是可以使用裝飾器的,怎么用呢?

        配置基礎環境

        除了一些老的項目,我們現在一般新建Vue項目的時候,都會選擇使用腳手架vue-cli3/4來新建,這時候新建的項目已經默認支持了裝飾器,不需要再配置太多額外的東西,如果你的項目使用了eslint,那么需要給eslint配置以下內容。

          parserOptions: {
            ecmaFeatures:{
              // 支持裝飾器
              legacyDecorators: true
            }
          }

        使用裝飾器

        雖然Vue的組件,我們一般書寫的時候export出去的是一個對象,但是這個并不影響我們直接在組件中使用裝飾器,比如就拿上例中的log舉例。

        function log() {
          /**
           * @param target 對應 methods 這個對象
           * @param name 對應屬性方法的名稱
           * @param descriptor 對應屬性方法的修飾符
           */
          return function(target, name, descriptor) {
            console.log(target, name, descriptor)
            const fn = descriptor.value
            descriptor.value = function(...rest) {
              console.log(`這是調用方法【${name}】前打印的日志`)
              fn.call(this, ...rest)
              console.log(`這是調用方法【${name}】后打印的日志`)
            }
          }
        }
        
        export default {
          created() {
            this.getData()
          },
          methods: {
            @log()
            getData() {
              console.log('獲取數據')
            }
          }
        }

        看了上面的代碼,是不是發現在Vue中使用裝飾器還是很簡單的,和在class的屬性上面使用的方式一模一樣,但有一點需要注意,在methods里面的方法上面使用裝飾器,這時候裝飾器的target對應的是methods。

        除了在methods上面可以使用裝飾器之外,你也可以在生命周期鉤子函數上面使用裝飾器,這時候target對應的是整個組件對象。

        一些常用的裝飾器

        下面小編羅列了幾個小編在項目中常用的幾個裝飾器,方便大家使用

        1. 函數節流與防抖

        函數節流與防抖應用場景是比較廣的,一般使用時候會通過throttledebounce方法對要調用的函數進行包裝,現在就可以使用上文說的內容將這兩個函數封裝成裝飾器, 防抖節流使用的是lodash提供的方法,大家也可以自行實現節流防抖函數哦

        import { throttle, debounce } from 'lodash'
        /**
         * 函數節流裝飾器
         * @param {number} wait 節流的毫秒
         * @param {Object} options 節流選項對象
         * [options.leading=true] (boolean): 指定調用在節流開始前。
         * [options.trailing=true] (boolean): 指定調用在節流結束后。
         */
        export const throttle =  function(wait, options = {}) {
          return function(target, name, descriptor) {
            descriptor.value = throttle(descriptor.value, wait, options)
          }
        }
        
        /**
         * 函數防抖裝飾器
         * @param {number} wait 需要延遲的毫秒數。
         * @param {Object} options 選項對象
         * [options.leading=false] (boolean): 指定在延遲開始前調用。
         * [options.maxWait] (number): 設置 func 允許被延遲的最大值。
         * [options.trailing=true] (boolean): 指定在延遲結束后調用。
         */
        export const debounce = function(wait, options = {}) {
          return function(target, name, descriptor) {
            descriptor.value = debounce(descriptor.value, wait, options)
          }
        }

        封裝完之后,在組件中使用

        import {debounce} from '@/decorator'
        
        export default {
          methods:{
            @debounce(100)
            resize(){}
          }
        }

        2. loading

        在加載數據的時候,為了個用戶一個友好的提示,同時防止用戶繼續操作,一般會在請求前顯示一個loading,然后在請求結束之后關掉loading,一般寫法如下

        export default {
          methods:{
            async getData() {
              const loading = Toast.loading()
              try{
                const data = await loadData()
                // 其他操作
              }catch(error){
                // 異常處理
                Toast.fail('加載失敗');
              }finally{
                loading.clear()
              }  
            }
          }
        }

        我們可以把上面的loading的邏輯使用裝飾器重新封裝,如下代碼

        import { Toast } from 'vant'
        
        /**
         * loading 裝飾器
         * @param {*} message 提示信息
         * @param {function} errorFn 異常處理邏輯
         */
        export const loading =  function(message = '加載中...', errorFn = function() {}) {
          return function(target, name, descriptor) {
            const fn = descriptor.value
            descriptor.value = async function(...rest) {
              const loading = Toast.loading({
                message: message,
                forbidClick: true
              })
              try {
                return await fn.call(this, ...rest)
              } catch (error) {
                // 在調用失敗,且用戶自定義失敗的回調函數時,則執行
                errorFn && errorFn.call(this, error, ...rest)
                console.error(error)
              } finally {
                loading.clear()
              }
            }
          }
        }
        

        然后改造上面的組件代碼

        export default {
          methods:{
            @loading('加載中')
            async getData() {
              try{
                const data = await loadData()
                // 其他操作
              }catch(error){
                // 異常處理
                Toast.fail('加載失敗');
              }  
            }
          }
        }

        3. 確認框

        當你點擊刪除按鈕的時候,一般都需要彈出一個提示框讓用戶確認是否刪除,這時候常規寫法可能是這樣的

        import { Dialog } from 'vant'
        
        export default {
          methods: {
            deleteData() {
              Dialog.confirm({
                title: '提示',
                message: '確定要刪除數據,此操作不可回退。'
              }).then(() => {
                console.log('在這里做刪除操作')
              })
            }
          }
        }

        我們可以把上面確認的過程提出來做成裝飾器,如下代碼

        import { Dialog } from 'vant'
        
        /**
         * 確認提示框裝飾器
         * @param {*} message 提示信息
         * @param {*} title 標題
         * @param {*} cancelFn 取消回調函數
         */
        export function confirm(
          message = '確定要刪除數據,此操作不可回退。',
          title = '提示',
          cancelFn = function() {}
        ) {
          return function(target, name, descriptor) {
            const originFn = descriptor.value
            descriptor.value = async function(...rest) {
              try {
                await Dialog.confirm({
                  message,
                  title: title
                })
                originFn.apply(this, rest)
              } catch (error) {
                cancelFn && cancelFn(error)
              }
            }
          }
        }

        然后再使用確認框的時候,就可以這樣使用了

        export default {
          methods: {
            // 可以不傳參,使用默認參數
            @confirm()
            deleteData() {
              console.log('在這里做刪除操作')
            }
          }
        }

        是不是瞬間簡單多了,當然還可以繼續封裝很多很多的裝飾器,因為文章內容有限,暫時提供這三個。

        裝飾器組合使用

        在上面我們將類屬性上面使用裝飾器的時候,說道裝飾器可以組合使用,在Vue組件上面使用也是一樣的,比如我們希望在確認刪除之后,調用接口時候出現loading,就可以這樣寫(一定要注意順序)

        export default {
          methods: {
            @confirm()
            @loading()
            async deleteData() {
              await delete()
            }
          }
        }
        本節定義的裝飾器,均已應用到這個項目中 https://github.com/snowzijun/vue-vant-base, 這是一個基于Vant開發的開箱即用移動端框架,你只需要fork下來,無需做任何配置就可以直接進行業務開發,歡迎使用,喜歡麻煩給一個star。

        我是子君,今天就寫這么多,本文首發于【前端有的玩】,這是一個專注于前端技術,前端面試相關的公眾號,同時關注之后即刻拉你加入前端交流群,我們一起聊前端,歡迎關注。

        結語

        不要吹滅你的靈感和你的想象力; 不要成為你的模型的奴隸。 ——文森特?梵高
        查看原文

        贊 27 收藏 17 評論 0

        chokcoco 發布了文章 · 3月29日

        有意思!不規則邊框的生成方案

        本文將介紹一種配合 SVG 濾鏡實現各種不規則圖形添加邊框的小技巧。

        本文完整的 DEMO,你可以戳這里:transparent 配合 SVG feMorphology 濾鏡生成不規則邊框

        需求背景,給不規則圖形添加邊框

        在我們日常開發中,時長會遇到一些非矩形、非圓形的圖案。類似下面這些:

        image

        使用純 CSS,搭配一些技巧,是可以制作出上面的圖形的,當然這只是需求的第一步。

        緊接著,可能會有要給上述圖形添加邊框的訴求,這個時候,CSS 就很難辦到了。

        嘗試使用 drop-shadow 添加邊框

        第一種方法,我們可以嘗試使用 drop-shadow,給不規則的圖形,添加一個外陰影。

        我們以一個箭頭圖形為例使用 CSS 簡單實現它的其中一種方式如下:

        <div class="arrow-button"></div>
        .arrow-button {
            position: relative;
            width: 180px;
            height: 64px;
            background: #f49714;
        
            &::after {
                content: "";
                position: absolute;
                width: 32px;
                height: 64px;
                top: 0;
                right: -32px;
                background: 
                    linear-gradient(-45deg, transparent 0, transparent 22px, #f49714 22px, #f49714 100%),
                    linear-gradient(-135deg, transparent 0, transparent 22px, #f49714 22px, #f49714 100%);
                background-size: 32px 32px;
                background-repeat: no-repeat;
                background-position: 0 bottom, 0 top;
            }
        }

        image

        我們通過給 .arrow-button 添加一個 drop-shadow,可以實現給不規則圖形添加一個陰影,效果如下:

        .arrow-button {
            ...
            filter: drop-shadow(0px 0px 2px #000);
            ...
        }

        image

        drop-shadow 方案的局限性

        使用 drop-shadow 方案的局限性在于,drop-shadow 只能對不規則圖形生成陰影,無法生成不帶模糊的邊框效果。

        上述圖形添加 drop-shadow 的效果如下,與我們想要的實體不帶模糊的邊框還是差了一點:

        image

        使用 SVG feMorphology 濾鏡添加邊框

        我們還可以換個思路,復制一個原圖形,再將其稍微放大一點點改變為邊框的顏色,然后兩個圖形疊加在一起,就能夠生成一個帶邊框的不規則圖形了。

        CSS 中也有能夠放大元素的能力 transform: scale(),但是本身實現一個原圖形的代碼可能已經非常復雜了,再疊加一個可能會顯得不太優雅,我們得另辟蹊徑,尋找其他類似的實現方案。

        這里,我們嘗試使用 SVG的 feMorphology 濾鏡來實現給不規則圖形添加邊框。

        如果你對 SVG 濾鏡還不算太了解,可以簡單看看我的這篇文章入門:有意思!強大的 SVG 濾鏡

        簡單介紹下 feMorphology 濾鏡

        feMorphology 濾鏡

        feMorphology 為形態濾鏡,它的輸入源通常是圖形的 alpha 通道,用來它的兩個操作可以使源圖形腐蝕(變?。┗驍U張(加粗)。

        使用屬性 operator 確定是要腐蝕效果還是擴張效果。使用屬性 radius 表示效果的程度,可以理解為筆觸的大小。

        • operator:erode 腐蝕模式,dilate 為擴張模式,默認為 erode
        • radius:筆觸的大小,接受一個數字,表示該模式下的效果程度,默認為 0

        我們將這個濾鏡簡單的應用到文字上看看效果:

        <div class="g-text">
            <p>Normal Text</p>
            <p class="dilate">Normal Text</p>
            <p class="erode">Normal Text</p>
        </div>
        
        <svg width="0" height="0">
            <filter id="dilate">
                <feMorphology in="SourceAlpha" result="DILATED" operator="dilate" radius="3"></feMorphology>
            </filter>
            <filter id="erode">
                <feMorphology in="SourceAlpha" result="ERODE" operator="erode" radius="1"></feMorphology>
            </filter>
        </svg>
        p {
            font-size: 64px;
        }
        .dilate {
            filter: url(#dilate);
        }
        .erode {
            filter: url(#erode);
        }

        效果如下:最左邊的是正常文字,中間的是擴張的模式,右邊的是腐蝕模式,看看效果,非常好理解:

        image

        借用 feMorphology 的擴張能力給不規則圖形添加邊框

        利用 feMorphology 的擴張能力,我們可以提前準備好一個 SVG feMorphology 濾鏡,其作用就是上述所說的:

        復制一個原圖形,再將其稍微放大一點點改變為邊框的顏色,然后兩個圖形疊加在一起,就能夠生成一個帶邊框的不規則圖形了。

        并且 SVG 濾鏡可以非常簡單的通過 CSS Filter 的 url 模式引入到各個不同的圖形當中去,復用性非常的高。

        該濾鏡的簡單代碼如下:

        <svg width="0" height="0">
            <filter id="outline">
                <feMorphology in="SourceAlpha" result="DILATED" operator="dilate" radius="1"></feMorphology>
                <feMerge>
                    <feMergeNode in="DILATED" />
                    <feMergeNode in="SourceGraphic" />
                </feMerge>
            </filter>
        </svg>

        簡單淺析一下這段 SVG 濾鏡代碼:

        1. <feMorphology in="SourceAlpha" result="DILATED" operator="dilate" radius="1"></feMorphology> 將原圖的不透明部分作為輸入,采用了 dilate 擴張模式且程度為 radius="1",生成了一個比原圖大 1px 的黑色圖塊
        2. 使用 feMerge 將黑色圖塊和原圖疊加在一起

        我們還是給上述的 .arrow-button 添加一個 CSS filter filter: url(#outline),引入我們創建的 SVG 濾鏡:

        .arrow-button {
            ...
            filter: url(#outline);
            ...
        }
        url 是 CSS 濾鏡屬性的關鍵字之一,url 模式是 CSS 濾鏡提供的能力之一,允許我們引入特定的 SVG 過濾器,這極大的增強 CSS 中濾鏡的能力。

        看看效果:

        image

        Wow,這下成功了,通過 feMorphology 濾鏡,我們成功的實現了給不規則的圖形添加了邊框效果,我能可以通過控制濾鏡中的 radius="1" 來控制邊框的大小。

        再將上述濾鏡運用在各種不規則圖形下看看效果:

        image

        效果還算可以,就是顏色是黑色的。如果我們希望邊框的顏色是其他顏色,有沒有辦法呢?

        輔以 feFloodfeComposite 改變邊框顏色

        通過 feFloodfeComposite 兩個 SVG 濾鏡,可以給生成的圖塊上不同的顏色,代碼如下:

        <svg width="0" height="0">
            <filter id="outline">
                <feMorphology in="SourceAlpha" result="DILATED" operator="dilate" radius="1"></feMorphology>
        
                <feFlood flood-color="green" flood-opacity="1" result="flood"></feFlood>
                <feComposite in="flood" in2="DILATED" operator="in" result="OUTLINE"></feComposite>
        
                <feMerge>
                    <feMergeNode in="OUTLINE" />
                    <feMergeNode in="SourceGraphic" />
                </feMerge>
            </filter>
        </svg>

        通過 feFlood 中的 flood-color="green",即可控制生成的邊框(圖塊)的顏色,這里設置為了綠色。應用到各個圖形上的效果如下:

        image

        至此,我們實現了通過 SVG 濾鏡實現對不規則圖形添加不同顏色的邊框。

        完整的 DEMO,你可以戳這里:transparent 配合 SVG feMorphology 濾鏡生成不規則邊框

        總結一下

        簡單的總結一下:

        • 使用 drop-shadow 可以實現給不規則圖形添加陰影,但是無法實現給不規則圖形添加實體不帶模糊的邊框
        • 使用 feMorphology SVG 濾鏡可以實現給給不規則圖形添加邊框效果,通過控制 radius="1" 可以調節邊框的大小
        • 使用 feMorphology 輔以 feFloodfeComposite 濾鏡改變邊框顏色
        • 通過 CSS Filter 的 url 模式,可以快速的將 SVG 濾鏡引入 HTML 元素,例如 filter: url(#outline)

        值得注意的是,由于圖形高寬不是 1:1 的,并且 feMorphologydilate 模式也不會根據元素的高寬等比例的擴張,所以生成的邊框是不一定在各處的均勻相等的,而 feMorphologyradius 屬性可以傳入兩個值,使用空格分離,分別表示橫向與縱向的擴張大小,實際使用的時候可以微調一下。

        最后

        本文更多的是提供一種不規則邊框的生成方案思路,當然,具體遇到這種情況大部分還是會以切圖為主,不過多了解掌握一種可行方法也不是壞事。

        好了,本文到此結束,一個簡單的小技巧,希望對你有幫助 :)

        更多精彩 CSS 技術文章匯總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。

        如果還有什么疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

        查看原文

        贊 4 收藏 2 評論 0

        chokcoco 發布了文章 · 3月24日

        有意思!強大的 SVG 濾鏡

        想寫一篇關于 SVG 濾鏡的文章已久,SVG 濾鏡的存在,讓本來就非常強大的 CSS 如虎添翼。讓僅僅使用 CSS/HTML/SVG 創作的效果更上一層樓。題圖為袁川老師使用 SVG 濾鏡實現的云彩效果 -- CodePen Demo -- Cloud (SVG filter + CSS)。

        什么是 SVG 濾鏡

        SVG 濾鏡與 CSS 濾鏡類似,是 SVG 中用于創建復雜效果的一種機制。很多人看到 SVG 濾鏡復雜的語法容易心生退意。本文力圖使用最簡潔明了的方式讓大家盡量弄懂 SVG 濾鏡的使用方式。

        本文默認讀者已經掌握了一定 SVG 的基本概念和用法。

        SVG 濾鏡的種類

        SVG 濾鏡包括了:

        feBlend
        feColorMatrix
        feComponentTransfer
        feComposite
        feConvolveMatrix
        feDiffuseLighting
        feDisplacementMap
        feFlood
        feGaussianBlur
        feImage
        feMerge
        feMorphology
        feOffset
        feSpecularLighting
        feTile
        feTurbulence
        feDistantLight
        fePointLight
        feSpotLight

        看著內容很多,有點類似于 CSS 濾鏡中的不同功能:blur()、contrast()、drop-shadow() 。

        SVG 濾鏡的語法

        我們需要使用 <defs><filter> 標簽來定義一個 SVG 濾鏡。

        通常所有的 SVG 濾鏡元素都需要定義在 <defs> 標記內。

        現在,基本上現代瀏覽器,即使不使用 <defs> 包裹 <filter>,也能夠定義一個 SVG 濾鏡。

        這個 <defs> 標記是 definitions 這個單詞的縮寫,可以包含很多種其它標簽,包括各種濾鏡。

        其次,使用 <filter> 標記用來定義 SVG 濾鏡。 <filter> 標簽需要一個 id 屬性,它是這個濾鏡的標志。SVG 圖形使用這個 id 來引用濾鏡。

        看一個簡單的 DEMO:

        <div class="cssFilter"></div>
        <div class="svgFilter"></div>
        
        <svg>
            <defs>
                <filter id="blur">
                    <feGaussianBlur in="SourceGraphic" stdDeviation="5" />
                </filter>
            </defs>
        </svg>
        div {
            width: 100px;
            height: 100px;
            background: #000;
        }
        .cssblur {
            filter: blur(5px);
        }
        .svgFilter{
            filter: url(#blur);
        }

        這里,我們在 defsfilter 標簽內,運用了 SVG 的 feGaussianBlur 濾鏡,也就是模糊濾鏡, 該濾鏡有兩個屬性 instdDeviation。其中 in="SourceGraphic" 屬性指明了模糊效果要應用于整個圖片,stdDeviation 屬性定義了模糊的程度。最后,在 CSS 中,使用了 filter: url(#blur) 去調用 HTML 中定義的 id 為 blur 的濾鏡。

        為了方便理解,也使用 CSS 濾鏡 filter: blur(5px) 實現了一個類似的濾鏡,方便比較,結果圖如下:

        image

        CodePen Demo - SVG 濾鏡

        嘿,可以看到,使用 SVG 的模糊濾鏡,實現了一個和 CSS 模糊濾鏡一樣的效果。

        CSS filter 的 url 模式

        上文的例子中使用了 filter: url(#blur) 這種模式引入了一個 SVG 濾鏡效果,url 是 CSS 濾鏡屬性的關鍵字之一,url 模式是 CSS 濾鏡提供的能力之一,允許我們引入特定的 SVG 過濾器,這極大的增強 CSS 中濾鏡的能力。

        相當于所有通過 SVG 實現的濾鏡效果,都可以快速的通過 CSS 濾鏡 URL 模式一鍵引入。

        多個濾鏡搭配工作

        和 CSS 濾鏡一樣,SVG 濾鏡也是支持多個濾鏡搭配混合使用的。

        所以我們經常能看到一個 <filter> 標簽內有大量的代碼。很容易就懵了~

        再來看個簡單的例子:

        <div></div>
        
        <svg>
            <defs>
                <!-- Filter declaration -->
                <filter id="MyFilter">
        
                    <!-- offsetBlur -->
                    <feGaussianBlur in="SourceAlpha" stdDeviation="5" result="blur" />
                    <feOffset in="blur" dx="10" dy="10" result="offsetBlur" />
        
                    <!-- merge SourceGraphic + offsetBlur -->
                    <feMerge>
                        <feMergeNode in="offsetBlur" />
                        <feMergeNode in="SourceGraphic" />
                    </feMerge>
                </filter>
            </defs>
        </svg>
        div {
            width: 200px;
            height: 200px;
            background: url(xxx);
            filter: url(#MyFilter);
        }

        我們先來看看整個濾鏡的最終結果,結果長這樣:

        image

        CSS 可能一行代碼就能實現的事情,SVG 居然用了這么多代碼。(當然,這里 CSS 也不好實現,不是簡單容器的陰影,而是 PNG 圖片圖形的輪廓陰影)

        分解步驟

        首先看這一段:

        <!-- offsetBlur -->
        <feGaussianBlur in="SourceAlpha" stdDeviation="5" result="blur" />
        <feOffset in="blur" dx="10" dy="10" result="offsetBlur" />

        首先 <feGaussianBlur in="SourceAlpha" stdDeviation="5" result="blur" /> 這一段,我們上面也講到了,會生成一個模糊效果,這里多了一個新的屬性 result='blur',這個就是 SVG 的一個特性,不同濾鏡作用的效果可以通過 result 產出一個中間結果(也稱為 primitives 圖元),其他濾鏡可以使用 in 屬性導入不同濾鏡產出的 result,繼續操作。

        緊接著,<feOffset> 濾鏡還是很好理解的,使用 in 拿到了上一步的結果 result = 'blur',然后做了一個簡單的位移。

        這里就有一個非常重要的知識點:在不同濾鏡中利用 resultin 屬性,可以實現在前一個基本變換操作上建立另一個操作,比如我們的例子中就是添加模糊后又添加位移效果。

        結合兩個濾鏡,產生的圖形效果,其實是這樣的:

        image

        實際效果中還出現了原圖,所以這里我們還使用了 <feMerge> 標簽,合并了多個效果。也就是上述這段代碼:

        <!-- merge SourceGraphic + offsetBlur -->
        <feMerge>
            <feMergeNode in="offsetBlur" />
            <feMergeNode in="SourceGraphic" />
        </feMerge>

        feMerge 濾鏡允許同時應用濾鏡效果而不是按順序應用濾鏡效果。利用 result 存儲別的濾鏡的輸出可以實現這一點,然后在一個 <feMergeNode> 子元素中訪問它。

        • <feMergeNode in="offsetBlur" /> 表示了上述兩個濾鏡的最終輸出結果 offsetBlur ,也就是陰影的部分
        • <feMergeNode in="SourceGraphic" /> 中的 in="SourceGraphic" 關鍵詞表示圖形元素自身將作為 <filter> 原語的原始輸入

        整體再遵循后輸入的層級越高的原則,最終得到上述結果。示意流程圖如下:

        image

        至此,基本就掌握了 SVG 濾鏡的工作原理,及多個濾鏡如何搭配使用。接下來,只需要搞懂不同的濾鏡能產生什么樣的效果,有什么不同的屬性,就能大致對 SVG 濾鏡有個基本的掌握!

        關于 SVG 濾鏡還需要知道的

        上面大致過了一下 SVG 濾鏡的使用流程,過程中提到了一些屬性,可能也漏掉了一些屬性的講解,本章節將補充說明一下。

        濾鏡標簽通用屬性

        有一些屬性是每一個濾鏡標簽都有,都可以進行設置的。

        屬性作用
        x, y提供左上角的坐標來定義在哪里渲染濾鏡效果。 (默認值:0)
        width, height繪制濾鏡容器框的高寬(默認都為 100%)
        result用于定義一個濾鏡效果的輸出名字,以便將其用作另一個濾鏡效果的輸入(in)
        in指定濾鏡效果的輸入源,可以是某個濾鏡導出的 result,也可以是下面 6 個值

        in 屬性的 6 個取值

        SVG filter 中的 in 屬性,指定濾鏡效果的輸入源,可以是某個濾鏡導出的 result,也可以是下面 6 個值:

        in 取值作用
        SourceGraphic該關鍵詞表示圖形元素自身將作為 <filter> 原語的原始輸入
        SourceAlpha該關鍵詞表示圖形元素自身將作為 <filter> 原語的原始輸入。SourceAlphaSourceGraphic 具有相同的規則除了 SourceAlpha 只使用元素的非透明部分
        BackgroundImage與 SourceGraphic 類似,但可在背景上使用。 需要顯式設置
        BackgroundAlpha與 SourceAlpha 類似,但可在背景上使用。 需要顯式設置
        FillPaint將其放置在無限平面上一樣使用填充油漆
        StrokePaint將其放在無限平面上一樣使用描邊繪畫
        后 4 個基本用不上~

        更多 SVG 濾鏡介紹講解

        上面已經提到了幾個濾鏡,我們簡單回顧下:

        • <feGaussianBlur > - 模糊濾鏡
        • <feOffset > - 位移濾鏡
        • <feMerge> - 多濾鏡疊加濾鏡

        接下來再介紹一些比較常見,有意思的 SVG 濾鏡。

        feBlend 濾鏡

        <feBlend> 為混合模式濾鏡,與 CSS 中的混合模式相類似。

        在 CSS 中,我們有混合模式 mix-blend-modebackground-blend-mode 。我有過非常多篇關于 CSS 混合模式相關的一些應用。如果你還不太了解 CSS 中的混合模式,可以先看看這幾篇文章:

        SVG 中的混合模式種類比 CSS 中的要少一些,只有 5 個,其作用與 CSS 混合模式完全一致:

        • normal — 正常
        • multiply — 正片疊底
        • screen — 濾色
        • darken — 變暗
        • lighten— 變亮

        簡單一個 Demo,我們有兩張圖,利用不同的混合模式,可以得到不一樣的混合結果 :

        <div></div>
        
        <svg>
            <defs>
                <filter id="lighten" x="0" y="0" width="200" height="250">
                    <feImage width="200" height="250" xlink:href="image1.jpg" result="img1" />
                    <feImage width="200" height="250" xlink:href="image2.jpg" result="img2" />
                    <feBlend mode="lighten" in="img1" in2="img2"/>
                </filter>
            </defs>
        </svg>
        .container {
            width: 200px;
            height: 250px;
            filter: url(#lighten);
        }

        這里還用到了一個 <feImage> 濾鏡,它的作用是提供像素數據作為輸出,如果外部來源是一個 SVG 圖像,這個圖像將被柵格化。

        image

        上述運用了 feBlend 濾鏡中的 mode="lighten" 后的結果,兩個圖像疊加作用了 lighten 混合模式:

        image

        看看全部 5 中混合模式的效果:

        image

        CodePen Demo -- SVG Filter feBlend Demo

        feColorMatrix

        <feColorMatrix> 濾鏡也是 SVG 濾鏡中非常有意思的一個濾鏡,顧名思義,它的名字中包含了矩陣這個單詞,表示該濾鏡基于轉換矩陣對顏色進行變換。每一像素的顏色值(一個表示為[紅,綠,藍,透明度] 的矢量) 都經過矩陣乘法 (matrix multiplated) 計算出的新顏色。

        這個濾鏡稍微有點復雜,我們一步一步來看。

        <feColorMatrix> 濾鏡有 2 個私有屬性 typevalues,type 它支持 4 種不同的類型:saturate | hueRotate | luminanceToAlpha | matrix,其中部分與 CSS Filter 中的一些濾鏡效果類似。

        type 類型作用values 的取值范圍
        saturate轉換圖像飽和度0.0 - 1.0
        hueRotate轉換圖像色相0.0 - 360
        luminanceToAlpha阿爾法通道亮度(不知道如何翻譯 :sad)只有一個效果,無需改變 values 的值
        matrix使用矩陣函數進行色彩變換需要應用一個 4 x 5 的矩陣

        在這里,我做了一個簡單的關于 <feColorMatrix> 前 3 個屬性 saturate | hueRotate | luminanceToAlpha 的效果示意 DEMO -- CodePen - feColorMatrix Demo,可以感受下它們的具體的效果:

        1gif

        saturate、hueRotate 濾鏡和 CSS 中的 filter 中的 saturate、hue-rotate 的作用是一模一樣的。

        feColorMatrix 中的 type=matrix

        feColorMatrix 中的 type=matrix 理解起來要稍微更復雜點,它的 values 需要傳入一個 4x5 的矩陣。

        像是這樣:

        <filter id="colorMatrix">
          <feColorMatrix type="matrix" values="1 0 0 0 0, 0 1 0 0 0, 0 0 1 0 0, 0 0 0 1 0"/>
        </filter>

        要理解如何運用這些填寫矩陣,就不得不直面另外一個問題 -- 圖像的表示。

        數字圖像的本質是一個多維矩陣。在圖像顯示時,我們把圖像的 R 分量放進紅色通道里,B 分量放進藍色通道里,G 分量放進綠色通道里。經過一系列處理,顯示在屏幕上的就是我們所看到的彩色圖像了。

        而 feColorMatrix 中的 matrix 矩陣,就是用來表示不同通道的值每一個分量的值,最終通過計算得到我們熟知的 rgba() 值。

        計算邏輯為:

        /* R G B A 1 */ 
        1 0 0 0 0 // R = 1*R + 0*G + 0*B + 0*A + 0 
        0 1 0 0 0 // G = 0*R + 1*G + 0*B + 0*A + 0 
        0 0 1 0 0 // B = 0*R + 0*G + 1*B + 0*A + 0 
        0 0 0 1 0 // A = 0*R + 0*G + 0*B + 1*A + 0

        中文的文章,對 feColorMatrix 的 matrix 講解最好的應該就是大漠老師的這篇 -- 詳解feColorMatrix,對具體的表示法感興趣的可以看看。

        僅僅是使用的話,這里還有一個可視化的 DEMO -- CodePen - feColorMatrix Demo,幫助大家理解記憶:

        2


        到目前為止,大部分 SVG 濾鏡的展示講解都是 CSS 現有能力能夠實現的,那 SVG 濾鏡的獨特與魅力到底在哪呢?有什么是 CSS 能力無法做到的么?下面來看看另外幾個有意思的 SVG 濾鏡。

        feSpecularLighting/feDiffuseLighting 光照濾鏡

        feSpecularLighting 與 feDiffuseLighting 都意為光照濾鏡,使用它們可以照亮一個源圖形,不同的是,feSpecularLighting 為鏡面照明,而 feDiffuseLighting 為散射光照明。

        • feDiffuseLighting:來自外部光源,適合模擬太陽光或者燈光照明
        • feSpecularLighting:指定從反射面反射的二次光

        簡單看其中一個 Demo,代碼看著有點多,但是一步一步也很好理解:

        <div></div>
        <div class="svg-filter"></div>
        <svg>
            <defs>
                <filter id="filter">
                    <!--Lighting effect-->
                    <feSpecularLighting in="SourceGraphic" specularExponent="20" specularConstant="0.75" result="spec">
                      <fePointLight x="0" y="0" z="200" />
                    </feSpecularLighting>
                    <!--Composition of inputs-->
                    <feComposite in="SourceGraphic" in2="spec" operator="arithmetic" k1="0" k2="1" k3="1" k4="0" />
                </filter>
            </defs>
        </svg>
        div {
            background: url(avator.png);
        }
        .svg-filter {
            filter: url(#filter);
        }

        左邊是原圖,右邊是應用了光照濾鏡之后的效果。

        image

        CodePen - feSpotLight SVG Light Source

        feMorphology 濾鏡

        feMorphology 為形態濾鏡,它的輸入源通常是圖形的 alpha 通道,用來它的兩個操作可以使源圖形腐蝕(變?。┗驍U張(加粗)。

        使用屬性 operator 確定是要腐蝕效果還是擴張效果。使用屬性 radius 表示效果的程度,可以理解為筆觸的大小。

        • operator:erode 腐蝕模式,dilate 為擴張模式,默認為 erode
        • radius:筆觸的大小,接受一個數字,表示該模式下的效果程度,默認為 0

        我們將這個濾鏡簡單的應用到文字上看看效果:

        <div class="g-text">
            <p>Normal Text</p>
            <p class="dilate">Normal Text</p>
            <p class="erode">Normal Text</p>
        </div>
        
        <svg width="0" height="0">
            <filter id="dilate">
                <feMorphology in="SourceAlpha" result="DILATED" operator="dilate" radius="3"></feMorphology>
            </filter>
            <filter id="erode">
                <feMorphology in="SourceAlpha" result="ERODE" operator="erode" radius="1"></feMorphology>
            </filter>
        </svg>
        p {
            font-size: 64px;
        }
        .dilate {
            filter: url(#dilate);
        }
        .erode {
            filter: url(#erode);
        }

        效果如下:最左邊的是正常文字,中間的是擴張的模式,右邊的是腐蝕模式,看看效果,非常好理解:

        image

        當然,我們還可以將其運用在圖片之上,這時,并非是簡單的讓圖像的筆觸變粗或者變細,

        • 對于 erode 模式,會將圖片的每一個像素向更暗更透明的方向變化,
        • dilate 模式,則是將每個向像素周圍附近更亮更不透明的方向變化

        簡單看個示例動畫 DEMO,我們有兩張圖,分別作用 operator="erode"operator="dilate",并且動態的去改變它們的 radius,其中一個的代碼示意如下:

        <svg width="450" height="300" viewBox="0 0 450 300">
            <filter id="morphology">
                <feMorphology operator="erode" radius="0">
                    <animate attributeName="radius" from="0" to="5" dur="5s" repeatCount="indefinite" />
                </feMorphology>
            </filter>
        
            <image xlink:href="image.jpg" width="90%" height="90%" x="10" y="10" filter="url(#morphology)"></image>
        </svg>

        3

        上圖左邊是擴張模式,右邊是腐蝕模式:

        CodePen Demo -- SVG feMorphology Animation

        feTurbulence 濾鏡

        turbulence 意為湍流,不穩定氣流,而 SVG <feTurbulence> 濾鏡能夠實現半透明的煙熏或波狀圖像。 通常用于實現一些特殊的紋理。濾鏡利用 Perlin 噪聲函數創建了一個圖像。噪聲在模擬云霧效果時非常有用,能產生非常復雜的質感,利用它可以實現了人造紋理比如說云紋、大理石紋的合成。

        有了 feTurbulence,我們可以自使用 SVG 創建紋理圖形作為置換圖,而不需要借助外部圖形的紋理效果,即可創建復雜的圖形效果。

        這個濾鏡,我個人認為是 SVG 濾鏡中最有意思的一個,因為它允許我們自己去創造出一些紋理,并且疊加在其他效果之上,生成出非常有意思的動效。

        feTurbulence 有三個屬性是我們特別需要注意的:type、baseFrequency、numOctaves

        • type:實現的濾鏡的類型,可選fractalNoise 分形噪聲,或者是 turbulence 湍流噪聲。

          • fractalNoise:分形噪聲更加的平滑,它產生的噪聲質感更接近云霧
          • turbulence:湍流噪聲
        • baseFrequency: 表示噪聲函數的基本頻率的參數,頻率越小,產生的圖形越大,頻率越大,產生的噪聲越復雜其圖形也越小越精細,通常的取值范圍在 0.02 ~ 0.2
        • numOctaves:表示噪聲函數的精細度,數值越高,產生的噪聲更詳細。 默認值為1

        這里有一個非常好的網站,用于示意 feTurbulence 所產生的兩種噪聲的效果:http://apike.ca/ - feTurbulence

        兩種噪聲的代碼基本一致,只是 type 類型不同:

        <filter id="fractal" >
          <feTurbulence id="fe-turb-fractal" type="fractalNoise" baseFrequency="0.00025" numOctaves="1"/>
        </filter>
        <filter id="turbu">
          <feTurbulence id="fe-turb-turbulence" type="fractalNoise" baseFrequency="0.00025" numOctaves="1"/>
        </filter>

        我們通過改變 baseFrequencynumOctaves 參數看看實際產生的兩種噪聲的效果:

        同時,baseFrequency 允許我們傳入兩個值,我們可以只改變某一方向上的頻率,具體的你可以戳這個 Demo 看看:CodePen -- feTurbulence baseFrequency & numOctaves

        單單一個 <feTurbulence> 濾鏡其實是比較難搞懂這濾鏡想干什么的,需要將這個濾鏡作為紋理或者輸入,和其他濾鏡一起搭配使用,實現一些效果,下面我們來看看:

        使用 feTurbulence 濾鏡實現文字流動的效果

        首先,嘗試將 feTurbulence 所產生的紋理和文字相結合。

        簡單的代碼如下:

        <div>Coco</div>
        <div class="turbulence">Coco</div>
        
        <svg>
            <filter id="fractal" filterUnits="objectBoundingBox" x="0%" y="0%" width="100%" height="100%">
                <feTurbulence id="turbulence" type="fractalNoise" baseFrequency="0.03" numOctaves="1" />
                <feDisplacementMap in="SourceGraphic" scale="50"></feDisplacementMap>
            </filter>
        </svg>
        .turbulence {
            filter: url(#fractal);
        }

        左邊是正常的效果,后邊是應用了 <feTurbulence> 的效果,你可以試著點進 Demo,更改 baseFrequencynumOctaves 參數的大小,可以看到不同的效果:

        image

        CodePen Demo -- feTurbulence text demo

        feDisplacementMap 映射置換濾鏡

        上面的 Demo 還用到了 feDisplacementMap 濾鏡,也需要簡單的講解下。

        feDisplacementMap 為映射置換濾鏡,想要用好這個濾鏡不太容易,需要掌握非常多的關于 PhotoShop 紋理創建或者是圖形色彩相關的知識。該濾鏡用來自圖像中從 in2 的輸入值到空間的像素值置換圖像從 in 輸入值到空間的像素值。

        說人話就是 feDisplacementMap 實際上是用于改變元素和圖形的像素位置的。該濾鏡通過遍歷原圖形的所有像素點,使用 feDisplacementMap 重新映射到一個新的位置,形成一個新的圖形。

        在上述的 feTurbulence 濾鏡與文字的結合使用中,我們通過 feTurbulence 噪聲得到了噪聲圖形,然后通過 feDisplacementMap 濾鏡根據 feTurbulence 所產生的噪聲圖形進行形變,扭曲,液化,得到最終的效果。

        MDN 上有這個濾鏡轉化的一個公式(感興趣的可以研究下,我啃不動了):

        P'(x,y) ← P( x + scale * (XC(x,y) - 0.5), y + scale * (YC(x,y) - 0.5))

        使用 feTurbulence 濾鏡實現褶皺紙張的紋理

        好,我們繼續 feTurbulence ,使用這個濾鏡,我們可以生成各種不同的紋理,我們可以嘗試使用 feTurbulence 濾鏡搭配光照濾鏡實現褶皺的紙張紋理效果,代碼也非常少:

        <div></div>
        <svg>
            <filter id='roughpaper'>
                <feTurbulence type="fractalNoise" baseFrequency='0.04' result='noise' numOctaves="5" />
        
                <feDiffuseLighting in='noise' lighting-color='#fff' surfaceScale='2'>
                    <feDistantLight azimuth='45' elevation='60' />
                </feDiffuseLighting>
            </filter>
        </svg>
        div {
            width: 650px;
            height: 500px;
            filter: url(#roughpaper);
        }

        效果如下:

        image

        CodePen Demo -- Rough Paper Texture with SVG Filters

        你可以在 Sara Soueidan 的一次關于 SVG Filter 的分享上,找到制作它的教程:Youtube -- SVG Filters Crash Course

        使用 feTurbulence 濾鏡實現按鈕hover效果

        使用 feTurbulence 濾鏡搭配 feDisplacementMap 濾鏡,還可以制作一些非常有意思的按鈕效果。

        嘗試實現一些故障風格的按鈕,其中一個按鈕的代碼如下:

        <div class="fe1">Button</div>
        <div class="fe2">Button</div>
        
        <svg>
            <defs>
                <filter id="fe1">
                    <feTurbulence id="animation" type="fractalNoise" baseFrequency="0.00001 9.9999999" numOctaves="1" result="warp">
                        <animate attributeName="baseFrequency" from="0.00001 9.9999" to="0.00001 0.001" dur="2s" repeatCount="indefinite"/>
                    </feTurbulence>
                    <feOffset dx="-90" dy="-90" result="warpOffset"></feOffset>
                    <feDisplacementMap xChannelSelector="R" yChannelSelector="G" scale="30" in="SourceGraphic" in2="warpOffset"></feDisplacementMap>
                </filter>
            </defs>
        </svg>
        .fe1 {
            width: 200px;
            height: 64px;
            outline: 200px solid transparent;
        }
        
        .fe1:hover {
            filter: url(#fe1);
        }

        通過 hover 按鈕的時候,給按鈕添加濾鏡效果,并且濾鏡本身帶有一個無限循環的動畫:

        完整的代碼你可以戳這里:CodePen Demo - SVG Filter Button Effects

        使用 feTurbulence 濾鏡實現云彩效果

        最后,我們回到題圖上的云彩效果,使用 feTurbulence 濾鏡,我們可以非常逼真的使用 SVG 模擬出真實的云彩效果。

        首先,通過隨機生成的多重 box-shadow,實現這一一個圖形:

        <div></div>
        div {
            width: 1px;
            height: 1px;
            box-shadow: rgb(240 255 243) 80vw 11vh 34vmin 16vmin, rgb(17 203 215) 33vw 71vh 23vmin 1vmin, rgb(250 70 89) 4vw 85vh 21vmin 9vmin, rgb(198 241 231) 8vw 4vh 22vmin 12vmin, rgb(198 241 231) 89vw 11vh 31vmin 19vmin, rgb(240 255 243) 5vw 22vh 38vmin 19vmin, rgb(250 70 89) 97vw 35vh 33vmin 16vmin, rgb(250 70 89) 51vw 8vh 35vmin 14vmin, rgb(17 203 215) 75vw 57vh 40vmin 4vmin, rgb(250 70 89) 28vw 18vh 31vmin 11vmin, rgb(250 70 89) 8vw 89vh 31vmin 2vmin, rgb(17 203 215) 13vw 8vh 26vmin 19vmin, rgb(240 255 243) 98vw 12vh 35vmin 5vmin, rgb(17 203 215) 35vw 29vh 27vmin 18vmin, rgb(17 203 215) 67vw 58vh 22vmin 15vmin, rgb(198 241 231) 67vw 24vh 25vmin 7vmin, rgb(17 203 215) 76vw 52vh 22vmin 7vmin, rgb(250 70 89) 46vw 86vh 26vmin 20vmin, rgb(240 255 243) 50vw 20vh 25vmin 1vmin, rgb(250 70 89) 74vw 14vh 25vmin 16vmin, rgb(240 255 243) 31vw 100vh 29vmin 20vmin
        }

        這個工作,你可以交給 SASS、LESS 或者 JavaScript 這些能夠有循環函數能力的語言去生成,它的效果大概是這樣:

        image

        緊接著,通過 feTurbulence 產生分形噪聲圖形,使用 feDisplacementMap 進行映射置換,最后給圖形疊加上這個濾鏡效果。

        <svg width="0">
          <filter id="filter">
            <feTurbulence type="fractalNoise" baseFrequency=".01" numOctaves="10" />
            <feDisplacementMap in="SourceGraphic" scale="240" />
          </filter>
        </svg>
        div {
            filter: url(#filter);
        }

        即可得到這樣的云彩效果:

        image

        完整的代碼,你可以戳這里到袁川老師的 CodePen 觀看:Cloud (SVG filter + CSS)

        總結一下

        關于 SVG 濾鏡入門的第一篇總算差不多了,本文簡單的介紹了一下 SVG 濾鏡的使用方式以及一些常見的 SVG 濾鏡并給出了最簡單的一些使用效果,希望大家看完能對 SVG 濾鏡有一個簡單的認識。

        本文羅列的濾鏡效果更多的是單個效果或者很少幾個組合在一起的效果,實際的使用或者應用到應用場景下其實會是更多濾鏡的的組合產生出的一個效果。

        后面的文章將會更加細致的去探討分析多個 SVG 濾鏡組合效果,探討更復雜的排列組合。

        文章的題目叫SVG 濾鏡從入門到放棄因為 SVG 濾鏡學起來確實太繁瑣太累了,它不像 CSS 濾鏡或者混合模式那么容易上手那么簡單。當然也由于 SVG 濾鏡的功能非常強大,定制化能力強以及它已經存在了非常之久有關。SVG 濾鏡的兼容性也很好,它們其實是早于 CSS3 一些特殊效果之前就已經存在的。

        CSS 其實一直在向 SVG 的一些特殊能力靠攏,用更簡單的語法讓人更易上手,不過 SVG 濾鏡還是有其獨特的魅力所在。后續將會有更多關于 SVG 濾鏡的文章。也希望讀到這里的同學不要放棄!

        參考資料

        最后

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

        更多精彩 CSS 技術文章匯總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。

        想 Get 到最有意思的 CSS 資訊,千萬不要錯過我的公眾號 -- iCSS前端趣聞 ??

        如果還有什么疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

        查看原文

        贊 14 收藏 10 評論 0

        chokcoco 發布了文章 · 3月12日

        小技巧!CSS 整塊文本溢出省略特性探究

        今天的文章很有意思,講一講整塊文本溢出省略打點的一些有意思的細節。

        文本超長打點

        我們都知道,到今天(2021/03/06),CSS 提供了兩種方式便于我們進行文本超長的打點省略。

        對于單行文本,使用單行省略:

        {
            width: 200px;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        image

        而對于多行文本的超長省略,使用 -webkit-line-clamp 相關屬性,兼容性也已經非常好了:

        {
            width: 200px;
            overflow : hidden;
            text-overflow: ellipsis;
            display: -webkit-box;
            -webkit-line-clamp: 2;
            -webkit-box-orient: vertical;
        }

        image

        CodePen Demo -- inline-block 實現整塊的溢出打點

        問題一:超長文本整塊省略

        基于上述的超長打點省略方案之下,會有一些變化的需求。譬如,我們有如下結構:

        <section>
            <a href="/" class="avatar"></a>
            <div class="info">
                <p class="person-card__name">Sb Coco</p>
                <p class="person-card__desc">
                    <span>FE</span>
                    <span>UI</span>
                    <span>UX Designer</span>
                    <span>前端工程師</span>
                </p>
            </div>
        </section>

        image

        對于上述超出的情況,我們希望對于超出文本長度的整一塊 -- 前端工程師,整體被省略。

        如果我們直接使用上述的方案,使用如下的 CSS,結果會是這樣,并非我們期待的整塊省略:

        .person-card__desc {
            width: 200px;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        image

        display: inline 改為 display: inline-block 實現整塊省略

        這里,如果我們需要實現一整塊的省略,只需要將包裹整塊標簽元素的 spandisplayinline 改為 inline-block 即可。

        .person-card__desc span {
            display: inline-block;
        }

        image

        這樣,就可以實現,基于整塊的內容的溢出省略了。完整的 Demo,你可以戳這里:

        CodePen Demo - 整塊超長溢出打點省略

        問題二:iOS 不支持整塊超長溢出打點省略

        然而,上述方案并非完美的。經過實測,上述方案在 iOSSafari 下,沒能生效,表現為這樣:

        image

        查看規范 - CSS Basic User Interface Module Level 3 - text-overflow,究其原因,在于 text-overflow 只能對內聯元素進行打點省略。(Chrome 對此可能做了一些優化,所以上述非 iOS 和 Safari 的場景是正常的)

        所以猜測是因為經過了 display: inline-block 的轉化后,已經不再是嚴格意義上的內聯元素了。

        解決方案,使用多行省略替代單行省略

        當然,這里經過試驗后,發現還是有解的,我們在開頭還提到了一種多行省略的方案,我們將多行省略的代碼替換單行省略,只是行數 -webkit-line-clamp: 2 改成一行即可 -webkit-line-clamp: 1。

        .person-card__desc {
            width: 200px;
            white-space: normal;
            overflow : hidden;
            text-overflow: ellipsis;
            display: -webkit-box;
            -webkit-line-clamp: 1;
            -webkit-box-orient: vertical;
        }
        .person-card__desc span {
            display: inline-block;
        }

        這樣,在 iOS/Safari 下也能完美實現整塊的超長打點省略:

        image

        CodePen Demo -- iOS 下的整塊超長溢出打點省略方案

        值得注意的是,在使用 -webkit-line-clamp 的方案的時候,一定要配合 white-space: normal 允許換行,而不是不換行。這一點,非常重要。

        這樣,我們就實現了全兼容的整塊的超長打點省略了。

        當然, -webkit-line-clamp 本身也是存在一定的兼容性問題的,實際使用的時候還需要具體去取舍。

        最后

        好了,本文到此結束,一個簡單的 CSS 小技巧,希望對你有幫助 :)

        想 Get 到最有意思的 CSS 資訊,千萬不要錯過我的公眾號 -- iCSS前端趣聞 ??

        gzh_sssmall.png

        更多精彩 CSS 技術文章匯總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。

        如果還有什么疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

        查看原文

        贊 19 收藏 14 評論 0

        chokcoco 發布了文章 · 3月8日

        奇思妙想 CSS 文字動畫

        之前有些過兩篇關于字體的文章,是關于如何定義字體的:

        本文將會和這篇 -- CSS 奇思妙想邊框動畫類似,講一些文字效果,利用不同的屬性搭配,實現各式各樣的文字動效。

        Google Font

        在寫各種 DEMO 的時候,有的時候一些特殊的字體能更好的體現動畫的效果。這里講一個快速引入不同格式字體的小技巧。

        就是 Google Font 這個網站,上面有非常多的不同的開源字體:

        image

        當我們相中了一個我們喜歡的字體,它也提供了非??焖俚谋憬莸囊敕绞?。選中對應的字體,選擇 +Select this style,便可以通過 link@import 兩種方式引入:

        image

        使用 link 標簽引入:

        <link rel="preconnect" >
        <link  rel="stylesheet">

        OR,在 CSS 代碼中,使用 @import 引入:

        <style>
        @import url('https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@200&display=swap');
        </style>

        上述兩種方式內部其實都是使用的 @font-face 進行了字體的定義。

        我們可以通過 @font-face 快速聲明指定一個自定義字體。類似這樣:

        @font-face {
          font-family: "Open Sans";
          src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"),
               url("/fonts/OpenSans-Regular-webfont.woff") format("woff");
        }

        這樣,利用 Google Font,我們就可以便捷的享受各種字體了。

        我在我的個人項目或者一些 DEMO 中,需要一些藝術字體或者特殊字體展示不一樣的效果時,經常會使用這種方式。而至于在業務中,是否需要引入一些特殊字體,就見仁見智了。


        接下來,就會分門別類的看看,字體在 CSS 中,和不同是屬性相結合,能夠鼓搗出什么樣的效果。

        文字與陰影

        通過將字體與字體陰影 text-shadow,相結合,陰影的不同運用方式,產生不一樣的效果。

        我們通過一系列 Demo 來看看。

        長陰影文字效果

        通過多層次,顏色逐漸變化(透明)的陰影變化,可以生成長陰影:

        div {
          text-shadow: 0px 0px #992400, 1px 1px rgba(152, 36, 1, 0.98), 2px 2px rgba(151, 37, 2, 0.96), 3px 3px rgba(151, 37, 2, 0.94), 4px 4px rgba(150, 37, 3, 0.92), 5px 5px rgba(149, 38, 4, 0.9), 6px 6px rgba(148, 38, 5, 0.88), 7px 7px rgba(148, 39, 5, 0.86), 8px 8px rgba(147, 39, 6, 0.84), 9px 9px rgba(146, 39, 7, 0.82), 10px 10px rgba(145, 40, 8, 0.8), 11px 11px rgba(145, 40, 8, 0.78), 12px 12px rgba(144, 41, 9, 0.76), 13px 13px rgba(143, 41, 10, 0.74), 14px 14px rgba(142, 41, 11, 0.72), 15px 15px rgba(142, 42, 11, 0.7), 16px 16px rgba(141, 42, 12, 0.68), 17px 17px rgba(140, 43, 13, 0.66), 18px 18px rgba(139, 43, 14, 0.64), 19px 19px rgba(138, 43, 15, 0.62), 20px 20px rgba(138, 44, 15, 0.6), 21px 21px rgba(137, 44, 16, 0.58), 22px 22px rgba(136, 45, 17, 0.56), 23px 23px rgba(135, 45, 18, 0.54), 24px 24px rgba(135, 45, 18, 0.52), 25px 25px rgba(134, 46, 19, 0.5), 26px 26px rgba(133, 46, 20, 0.48), 27px 27px rgba(132, 47, 21, 0.46), 28px 28px rgba(132, 47, 21, 0.44), 29px 29px rgba(131, 48, 22, 0.42), 30px 30px rgba(130, 48, 23, 0.4), 31px 31px rgba(129, 48, 24, 0.38), 32px 32px rgba(129, 49, 24, 0.36), 33px 33px rgba(128, 49, 25, 0.34), 34px 34px rgba(127, 50, 26, 0.32), 35px 35px rgba(126, 50, 27, 0.3), 36px 36px rgba(125, 50, 28, 0.28), 37px 37px rgba(125, 51, 28, 0.26), 38px 38px rgba(124, 51, 29, 0.24), 39px 39px rgba(123, 52, 30, 0.22), 40px 40px rgba(122, 52, 31, 0.2), 41px 41px rgba(122, 52, 31, 0.18), 42px 42px rgba(121, 53, 32, 0.16), 43px 43px rgba(120, 53, 33, 0.14), 44px 44px rgba(119, 54, 34, 0.12), 45px 45px rgba(119, 54, 34, 0.1), 46px 46px rgba(118, 54, 35, 0.08), 47px 47px rgba(117, 55, 36, 0.06), 48px 48px rgba(116, 55, 37, 0.04), 49px 49px rgba(116, 56, 37, 0.02), 50px 50px rgba(115, 56, 38, 0);
        }

        image

        當然,多重陰影以及每重的顏色我們很難一個一個手動去寫,在寫長陰影的時候通常需要借助 SASS、LESS 去幫助節省時間:

        @function makelongrightshadow($color) {
            $val: 0px 0px $color;
        
            @for $i from 1 through 50 {
                $color: fade-out(desaturate($color, 1%), .02);
                $val: #{$val}, #{$i}px #{$i}px #{$color};
            }
        
            @return $val;
        }
        div {
            text-shadow: makeLongShadow(hsl(14, 100%, 30%));
        }

        立體陰影文字效果

        如果多層陰影,但是顏色變化沒那么強烈,能夠塑造一種立體的效果。

        div {
          text-shadow: 0 -1px 0 #ffffff, 0 1px 0 #2e2e2e, 0 2px 0 #2c2c2c, 0 3px 0 #2a2a2a, 0 4px 0 #282828, 0 5px 0 #262626, 0 6px 0 #242424, 0 7px 0 #222222, 0 8px 0 #202020, 0 9px 0 #1e1e1e, 0 10px 0 #1c1c1c, 0 11px 0 #1a1a1a, 0 12px 0 #181818, 0 13px 0 #161616, 0 14px 0 #141414, 0 15px 0 #121212);
        }

        image

        內嵌陰影文字效果

        合理的陰影顏色和背景底色搭配,搭配,可以實現類似內嵌效果的陰影。

        div {
          color: #202020;
          background-color: #2d2d2d;
          letter-spacing: .1em;
          text-shadow: -1px -1px 1px #111111, 2px 2px 1px #363636;
        }

        image

        CodePen Demo -- 5 text shadow effects in css3

        氖光效果(Neon)

        氖光效果,英文名叫 Neon,是我在 Codepen 上看到的最多的效果之一。它的原理非常簡單,卻可以產生非??犰诺男Ч?。

        我們只需要設置 3~n 層陰影效果,每一層的模糊半徑(文字陰影的第三個參數)間隔較大,并且每一層的陰影顏色相同即可。

        p {
            color: #fff;
            text-shadow: 
                0 0 10px #0ebeff,
                0 0 20px #0ebeff,
                0 0 50px #0ebeff,
                0 0 100px #0ebeff,
                0 0 200px #0ebeff
        }

        當然,通常運用 Neon 效果時,背景底色都是偏黑色。

        image

        合理運用 Neon 效果,就可以制作非常多有意思的動效。譬如作用于鼠標 hover 上去的效果:

        p {
            transition: .2s;
         
            &:hover {
                text-shadow: 0 0 10px #0ebeff, 0 0 20px #0ebeff, 0 0 50px #0ebeff, 0 0 100px #0ebeff, 0 0 200px #0ebeff;
            }
        }

        CodePen Demo -- Neon Demo

        CodePen 上有一個 2K+ 贊的 DEMO,實現了非常贊的 Neon 效果,可以戳進去看看 CodePen -- Neon Glow。

        也可以選取適當合適的字體,配合動畫效果,實現一種漸進的亮燈效果:

        <p>
          <span id="n">n</span>
          <span id="e">e</span>
          <span id="o">o</span>
          <span id="n2">n</span>
        </p>
        p:hover span {
          animation: flicker 1s linear forwards;
        }
        p:hover #e {
          animation-delay: .2s;
        }
        p:hover #o {
          animation-delay: .5s;
        }
        p:hover #n2 {
          animation-delay: .6s;
        }
        
        @keyframes flicker {
          0% {
            color: #333;
          }
          5%, 15%, 25%, 30%, 100% {
            color: #fff;
            text-shadow: 
              0px 0px 5px var(--color),
              0px 0px 10px var(--color),
              0px 0px 20px var(--color),
              0px 0px 50px var(--color);
              
          }
          10%, 20% {
            color: #333;
            text-shadow: none;
          }
        }

        截 GIF 圖的幀率不太夠,看著效果不太好,可以點進下面的 DEMO 感受下:

        CodePen Demo -- Neon Demo

        文字與背景

        CSS 中的背景 background,也提供了一些屬性用于增強文字的效果。

        background-clip 與文字

        背景中有個屬性為 background-clip, 其作用就是設置元素的背景(背景圖片或顏色)的填充規則。

        box-sizing 的取值非常類似,通常而言,它有 3 個取值,border-box,padding-box,content-box,后面規范新增了一個 background-clip。時至今日,部分瀏覽器仍需要添加前綴 webkit 進行使用 -webkit-background-clip。

        使用了這個屬性的意思是,以區塊內的文字作為裁剪區域向外裁剪,文字的背景即為區塊的背景,文字之外的區域都將被裁剪掉。

        看個最簡單的 Demo ,沒有使用 background-clip:text :

        <div>Clip</div>
        
        <style>
        div {
          font-size: 180px;
          font-weight: bold;
          color: deeppink;
          background: url($img) no-repeat center center;
          background-size: cover;
        }
        </style>

        效果如下:

        image

        CodePen Demo

        使用 background-clip:text

        我們稍微改造下上面的代碼,添加 -webkit-background-clip:text

        div {
          font-size: 180px;
          font-weight: bold;
          color: deeppink;
          background: url($img) no-repeat center center;
          background-size: cover;
          background-clip: text;
        }

        效果如下:

        image

        CodePen Demo

        看到這里,可能有人就納悶了,這不就是文字設置 color 屬性嘛。

        別急,由于文字設置了顏色,擋住了 div 塊的背景,如果將文字設置為透明呢?文字是可以設置為透明的 color: transparent 。

        div {
          color: transparent;
          background-clip: text;
        }

        效果如下:

        image

        CodePen Demo

        通過將文字設置為透明,原本 div 的背景就顯現出來了,而文字以外的區域全部被裁剪了,這就是 background-clip:text 的作用。

        利用 background-clip 圖文搭配

        這樣,我們可以選取合適的圖片合適的字體,實現任意風格的文字效果。

        image

        CodePen Demo -- background-clip: text & Image text

        又或者,利用這個效果實現一張創意海報:

        利用 background-clip 實現漸變文字

        再者,利用這個屬性,也可以輕松的實現漸變色的文字:

        {
            background: linear-gradient(45deg, #009688, yellowgreen, pink, #03a9f4, #9c27b0, #8bc34a);
            background-clip: text;
        }

        image

        配合 background-position 或者 filter: hue-rotate(),讓漸變動起來:

        {
            background: linear-gradient(45deg, #009688, yellowgreen, pink, #03a9f4, #9c27b0, #8bc34a);
            background-clip: text;
            animation: huerotate 5s infinite;
        }
        
        @keyframes huerotate {
            100% {
                filter: hue-rotate(360deg);
            }
        }

        CodePen Demo -- background-clip: text 文字漸變色

        利用 background-clip 給文字增加高光動畫

        利用 background-clip, 我們還可以輕松的給文字增加高光動畫。

        譬如這樣:

        其本質也是利用了 background-clip,偽代碼如下:

        <p data-text="Lorem ipsum dolor"> Lorem ipsum dolor </p>
        p {
            position: relative;
            color: transparent;
            background-color: #E8A95B;
            background-clip: text;
        }
        p::after {
            content: attr(data-text);
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            background-image: linear-gradient(120deg, transparent 0%, transparent 6rem, white 11rem, transparent 11.15rem, transparent 15rem, rgba(255, 255, 255, 0.3) 20rem, transparent 25rem, transparent 27rem, rgba(255, 255, 255, 0.6) 32rem, white 33rem, rgba(255, 255, 255, 0.3) 33.15rem, transparent 38rem, transparent 40rem, rgba(255, 255, 255, 0.3) 45rem, transparent 50rem, transparent 100%);
            background-clip: text;
            background-size: 150% 100%;
            background-repeat: no-repeat;
            animation: shine 5s infinite linear;
        }
        @keyframes shine {
            0% {
                background-position: 50% 0;
            }
            100% {
                background-position: -190% 0;
            }
        }

        去掉偽元素的 background-clip: text,就能看懂原理:

        11

        CodePen Demo -- shine Text && background-clip

        mask 與文字

        還有一個與背景相關的屬性 -- mask 。

        之前在多篇文章都提到了 mask,比較詳細的一篇是 -- 奇妙的 CSS MASK,本文不對 mask 的基本概念做過多講解,向下閱讀時,如果對一些 mask 的用法感到疑惑,可以再去看看。

        只需要記住核心的,使用 mask 最重要結論就是:添加了 mask 屬性的元素,其內容會與 mask 表示的漸變的 transparent 的重疊部分,并且重疊部分將會變得透明。

        利用 mask,我們可以實現各種文字的出場特效:

        <div>
            <p>Hello MASK</p>
        </div>

        核心的 CSS 代碼:

        div {
            mask: radial-gradient(circle at 50% 0%, #000, transparent 30%);
            animation: scale 6s infinite;
        }
        @keyframes scale {
            0% {
                mask-size: 100% 100%;
            }
            60%,
            100% {
                mask-size: 150% 800%;
            }
        }

        改變不同的方向,又或者是這樣:

        CodePen Demo -- MASK Text Effect

        文字與混合模式(mix-blend-mode)及濾鏡(filter)

        接下來,就到了非常有意思的混合模式及濾鏡了。這兩個屬性給 CSS 世界增添了非常多的趣味性,活靈活用,會感嘆 CSS 居然如此的強大美妙。

        之前有多非常多篇關于混合模式濾鏡的文章,一些基礎的用法就不再贅述。

        給文字添加邊框,生成鏤空文字

        在 CSS 中,我們可以利用 -webkit-text-stroke,給文字快速的添加邊框,利用這個,可以快速生成鏤空型的文字:

        p {
            -webkit-text-stroke: 3px #373750;
        }

        image

        當然,我們看到,用到的屬性 -webkit-text-stroke 帶了 webkit 前綴,存在一定的兼容性問題。

        所以,在更早的時候,我們還會使用 text-shadow,生成鏤空文字。

        p {
            text-shadow: 0 0 5px #fff;
        }

        image

        可以看到,因為使用的是陰影,所以有很明顯的虛化的感覺,存在一定的瑕疵。

        還有一種非常繞的方法,利用混合模式加上濾鏡,也能生成鏤空文字。

        p {
            position: relative;
            color: #fff;
        
            &::after {
                content: 'Magic Text';
                position: absolute;
                left: 0;
                top: 0;
                color: #fff;
                mix-blend-mode: difference;
                filter: blur(1px);
            }
        }

        image

        這里利用 filter: blur(1px) 生成了一個比原字體稍微大一點點的字體覆蓋在原字體之上,再利用 mix-blend-mode: difference 消除掉了同色的部分,只留下了利用模糊濾鏡多出來的那一部分。

        mix-blend-mode: difference: 差值模式(Difference),作用是查看每個通道中的顏色信息,比較底色和繪圖色,用較亮的像素點的像素值減去較暗的像素點的像素值。與白色混合將使底色反相;與黑色混合則不產生變化。

        示意動圖如下:

        CodePen Demo -- Hollow TEXT EFFECT

        利用混合模式,生成漸變色鏤空文字

        好,回到上面的 -webkit-text-stroke,拿到了鏤空文字后,我們還可以利用混合模式 mix-blend-mode: multiply 生成漸變色的文字。

        mix-blend-mode: multiply: 正片疊底(multiply),將兩個顏色的像素值相乘,然后除以255得到的結果就是最終色的像素值。通常執行正片疊底模式后的顏色比原來兩種顏色都深。任何顏色和黑色正片疊底得到的仍然是黑色,任何顏色和白色執行正片疊底則保持原來的顏色不變,而與其他顏色執行此模式會產生暗室中以此種顏色照明的效果。
        p {
            position: relative;
            -webkit-text-stroke: 3px #9a9acc;
        
            &::before{
                content: ' ';
                width: 100%;
                height: 100%;
                position: absolute;
                left: 0;
                top: 0;
                background-image: linear-gradient(45deg, #ff269b, #2ab5f5, #ffbf00);
                mix-blend-mode: multiply;
            }
        }

        image

        在這里,mix-blend-mode: multiply 發揮的作用和 mask 非常的類似,我們其實是生成了一幅漸變圖案,但是只有在文字輪廓內,漸變顏色才會顯現。

        當然,上述效果和整體的黑色底色也是有關系的。

        示意圖如下:

        CodePen Demo -- Hollow TEXT EFFECT

        利用混合模式,生成光影效果文字

        OK,在上述的基礎上,我們可以繼續疊加混合模式,這次我們利用剩余的一個 ::after 偽類,再添加一個 mix-blend-mode: color-dodge 混合模式,給文字加上最后的點綴,實現美妙的光影效果。

        mix-blend-mode: color-dodge: 顏色減淡模式(Color Dodge),查看每個通道的顏色信息,通過降低“對比度”使底色的顏色變亮來反映繪圖色,和黑色混合沒變化。。

        核心的偽代碼:

        p {
            position: relative;
            -webkit-text-stroke: 3px #7272a5;
        
            &::before {
                content: ' ';
                background-image: linear-gradient(45deg, #ff269b, #2ab5f5, #ffbf00);
                mix-blend-mode: multiply;
            }
        
            &::after {
                content: "";
                position: absolute;
                background: radial-gradient(circle, #fff, #000 50%);
                background-size: 25% 25%;
                mix-blend-mode: color-dodge;
                animation: mix 8s linear infinite;
            }
        }
        
        @keyframes mix {
            to {
                transform: translate(50%, 50%);
            }
        }

        看看效果:

        這里就要感嘆 mix-blend-mode: color-dodge 的神奇了,去掉這個混合模式,其實是這樣的:

        9

        CodePen -- Hollow TEXT EFFECT

        好,就上面這個效果,還可以繼續嗎?答案是可以的。限于篇幅,本文不再繼續在這個效果上深入,感興趣的可以拿著上面的 DEMO 自己再搗鼓搗鼓。

        利用混合模式實現文字與底色反色的效果

        這里還是利用 mix-blend-mode: difference 差值模式,實現一種文字與底色反色的 Title 效果。

        mix-blend-mode: difference: 差值模式(Difference),作用是查看每個通道中的顏色信息,比較底色和繪圖色,用較亮的像素點的像素值減去較暗的像素點的像素值。與白色混合將使底色反相;與黑色混合則不產生變化。

        代碼非常的簡單,我們實現一個黑白相間的背景,文本的顏色為白色,配合上差值模式,即可實現黑底上的文字為白色,白底上的文字為黑色的效果。

        p  {
            background: repeating-radial-gradient(circle at 200% 200%, #000 0, #000 150px, #fff 150px, #fff 300px);
        
            &::before {
                content: "LOREM IPSUM";
                color: #fff;
                mix-blend-mode: difference;
            }
        }

        可以用于一些標題效果:

        image

        CodePen Demo -- Radial-gradient + Mix-blend-mode

        利用混合模式實現動態類抖音風格 Glitch 效果

        OK,接下來,我們再嘗試下其他混合模式的搭配。在 CSS 故障藝術 一文中,提到了一種故障藝術。

        什么是故障藝術?我們熟知的抖音的 LOGO 正是故障藝術其中一種表現形式。它有一種魔幻的感覺,看起來具有閃爍、震動的效果,很吸引人眼球。

        關鍵點

        • 利用 mix-blend-mode: lighten 混合模式實現兩段文字結構重疊部分為白色
        • 利用元素位移完成錯位移動動畫,形成視覺上的沖擊效果

        看看效果:

        textglitch

        本文篇幅有點長,代碼就不上了,完整 DEMO 在這里:

        類抖音 LOGO 文字故障效果

        當然,我們也不是一定要使用混合模式去使得融合部分為白色,可以僅僅是使用這個配色效果,基于上面效果的另外一個版本,沒有使用混合模式。

        關鍵點

        • 利用了偽元素生成了文字的兩個副本
        • 視覺效果由位移、遮罩、混合模式完成
        • 配色借鑒了抖音 LOGO 的風格

        textglitch2

        完整 DEMO 在這里:

        CSS文字故障效果

        僅僅使用配色沒有使用混合模式的好處在于,對于每一個文字的副本,有了更大的移動距離和可以處理的空間。

        Glitch Art 風格的 404 效果

        稍微替換一下文本文案為 404,再添加上一些濾鏡效果(hue-rotate()、blur())嘿嘿,找到了一個可能實際可用的場景:

        效果一:

        404

        效果二:

        404

        兩個 404 效果的 Demo 如下:

        小技巧,在使用混合模式的時,有的時候,效果不希望和背景混合在一起,可以使用 isolation: isolate 進行隔離。

        使用濾鏡生成文字融合效果

        你所不知道的 CSS 濾鏡技巧與細節 一文中,介紹了利用濾鏡實現的一種融合效果。

        利用了模糊濾鏡疊加對比度濾鏡產生的融合效果。

        單獨將兩個濾鏡拿出來,它們的作用分別是:

        1. filter: blur(): 給圖像設置高斯模糊效果。
        2. filter: contrast(): 調整圖像的對比度。

        但是,當他們“合體”的時候,產生了奇妙的融合現象。簡單的例子:

        filtermix

        CodePen Demo -- filter mix between blur and contrast

        利用這個特性,可以實現一些文字融合的效果:

        利用這個方法,我們還可以設計一些文字融合的效果:

        wordanimation

        bluranimation

        具體實現你可以看這里:

        CodePen Demo -- word animation | word filter

        文字與 SVG

        最后,我們再來看看文字與 SVG。

        在 SVG 與 CSS 的搭配中,有一類非常適合拿來做動畫的屬性,也就是 stroke-* 相關的幾個屬性,利用它們,我們只需要掌握簡單的 SVG 語法,就可以快速制作相關的線條動畫。

        我們利用 SVG 中幾個和邊框、線條相關的屬性,來實現文字的線條動畫,下面羅列一下,其實大部分和 CSS 對比一下非常好理解,只是換了個名字:

        • stroke-width:類比 css 中的 border-width,給 svg 圖形設定邊框寬度;
        • stroke:類比 css 中的 border-color,給 svg 圖形設定邊框顏色;
        • stroke-linejoin | stroke-linecap:設定線段連接處的樣式;
        • stroke-dasharray:值是一組數組,沒數量上限,每個數字交替表示劃線與間隔的寬度;
        • stroke-dashoffset:則是虛線的偏移量
        具體的更深入的介紹,可以看看這篇:【Web動畫】SVG 線條動畫入門

        線條文字動畫

        接下來,我們利用 stroke-* 相關屬性,實現一個簡單的線條文字動畫。

        <svg viewBox="0 0 400 200">
            <text x="0" y="70%"> Lorem </text>
        </svg>    
        svg text {
            animation: stroke 5s infinite alternate;
            letter-spacing: 10px;
            font-size: 150px;
        }
        @keyframes stroke {
            0% {
                fill: rgba(72, 138, 20, 0);
                stroke: rgba(54, 95, 160, 1);
                stroke-dashoffset: 25%;
                stroke-dasharray: 0 50%;
                stroke-width: 1;
            }
            70% {
                fill: rgba(72, 138, 20, 0);
                stroke: rgba(54, 95, 160, 1);
                stroke-width: 3;
            }
            90%,
            100% {
                fill: rgba(72, 138, 204, 1);
                stroke: rgba(54, 95, 160, 0);
                stroke-dashoffset: -25%;
                stroke-dasharray: 50% 0;
                stroke-width: 0;
            }
        }

        動畫的核心就是,利用動態變化文字的 stroke-dasharraystroke-dashoffset 形成視覺上的線條變換,動畫的最后再給文字上色??纯葱Ч?/p>

        CodePen Demo -- SVG Text Line Effect

        最后

        本文介紹了一些我認為比較有意思的文字動畫小技巧,當然 CSS 中還有非常多有意思的文字效果,限于篇幅,不一一展開。

        本文到此結束,希望對你有幫助 :),想 Get 到最有意思的 CSS 資訊,千萬不要錯過我的公眾號 -- iCSS前端趣聞 ??

        123.png

        更多精彩 CSS 技術文章匯總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。

        如果還有什么疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

        查看原文

        贊 37 收藏 30 評論 2

        chokcoco 發布了文章 · 3月3日

        巧用 -webkit-box-reflect 倒影實現各類動效

        在很久之前的一篇文章,有講到 -webkit-box-reflect 這個屬性 -- 從倒影說起,談談 CSS 繼承 inherit

        -webkit-box-reflect 是一個非常有意思的屬性,它讓 CSS 有能力像鏡子一樣,反射我們元素原本繪制的內容。

        上一次寫它,它的兼容性還非常非常的慘淡,但是到今天,雖然還是一個 Non-standard 的語法,但是兼容性已經大有改觀,并且利用它,我們可以實現非常多有意思的效果。

        截止至 2021-02-19,它的兼容性已經達到了 91.02%,看看 CANIUSE -webkit-box-reflect

        image

        接下來進入正文。

        -webkit-box-reflect 基本用法

        -webkit-box-reflect 的語法非常簡單,最基本的用法像是這樣:

        div {
            -webkit-box-reflect: below;
        }

        其中,below 可以是 below | above | left | right 代表下上左右,也就是有 4 個方向可以選。

        假設我們有如下一張圖片:

        <div></div>
        div {
            background-image: url('https://images.pokemontcg.io/xy2/12_hires.png');
        }

        image

        加上 -webkit-box-reflect: right,也就是右側的倒影:

        div {
            background-image: url('https://images.pokemontcg.io/xy2/12_hires.png');
            -webkit-box-reflect: right;
        }

        效果如下,生成了一個元素右側的鏡像元素:

        image

        設置倒影距離

        在方向后面,還可以接一個具體的數值大小,表示倒影與原元素間的距離。

        div {
            background-image: url('https://images.pokemontcg.io/xy2/12_hires.png');
            -webkit-box-reflect: right 10px;
        }

        加上 10px 之后,倒影與原元素間將間隔 10px

        image

        設置倒影虛實

        還有一個非常重要的功能,就是方位后面,還能再設置一個漸變值,利用這個漸變值,可以實現倒影的一個虛化效果,這一點非常重要。

        div {
            background-image: url('https://images.pokemontcg.io/xy2/12_hires.png');
            -webkit-box-reflect: below 2px linear-gradient(transparent, rgba(0, 0, 0, .5));
        }

        看看效果,有了虛實變化之后,這樣就更像是一個倒影.

        其實,這里的漸變就是給倒影的圖片添加了一個 MASK 屬性,MASK 屬性的 transparent 部分,圖片將變得透明,而實色部分,則保持原圖。

        關于 CSS MASK 屬性,如果你還有什么疑問,建議看看這篇文章:奇妙的 CSS MASK

        image

        CodePen Demo -- -webkit-box-reflect Demo

        使用 -webkit-box-reflect 實現一些有意思的動效

        掌握了基本的語法后,我們就可以利用其實現一些有意思的動效,下面簡單羅列一下。

        我發現這個屬性特別適合運用在一些暗黑系風格的頁面中。能夠讓很多動效看起來高大上很多。(個人審美)

        在按鈕中運用 -webkit-box-reflect

        配合一些動態邊框動畫的按鈕,能夠營造一種很科幻的效果:

        感興趣的可以自行戳源碼了解了解:

        CodePen demo -webkit-box-reflect Neon Button Hover Effect

        在文字中運用 -webkit-box-reflect

        在暗黑系的標題文字中,運用上 -webkit-box-reflect ,瞬間高大上了不少。

        image

        CodePen demo - Font & -webkit-box-reflect

        在 3D 中運用 -webkit-box-reflect

        嘿嘿,接下來,我們甚至可以把 -webkit-box-reflect 運用中 3D 效果中,完全不一樣的觀感體驗。

        我們給一個 3D 照片墻,加上倒影效果:

        CodePen demo - 3DView & -webkit-box-reflect

        使用 -webkit-box-reflect 創造藝術圖案

        有趣的 CSS 藝術,它又來了。

        在袁川老師的這篇文章中 -- Chinese Window Lattice And CSS,介紹了利用 -webkit-box-reflect 生成剪紙藝術這樣一種思路。

        由于 -webkit-box-reflect 可以生成倒影,那么我們利用它進行不斷的套娃,一層疊一層,那么只需要生成一個基本的元素,就可以利用倒影產生出各種不同的效果。

        假設,我們有如下結構:

        <div class="g-wrap1">
            <div class="g-wrap2">
                <div class="g-wrap3">
                    <div class="g-wrap4"></div>
                </div>
            </div>
        </div>

        我們只需要給 .g-wrap4 實現一個圖形,例如這樣:

        .g-wrap4 {
            background: 
                radial-gradient(circle at 0 0, #000 30%, transparent 30%, transparent 40%, #000 40%, #000 50%, transparent 50%),
                radial-gradient(circle at 100% 100%, #000 10%, transparent 10%, transparent 30%, #000 30%, #000 40%, transparent 40%);
        }

        image

        然后就是 4 層套娃, 首先給 .g-wrap4 加上一層倒影 -webkit-box-reflect

        .g-wrap4 {
            -webkit-box-reflect: right 0px;
        }

        得到:

        image

        繼續套娃,給 .g-wrap3 加上一層倒影 -webkit-box-reflect

        .g-wrap4 {
            -webkit-box-reflect: right 0px;
        }
        .g-wrap3 {
            -webkit-box-reflect: below 0px;
        }

        image

        繼續,給 .g-wrap2 加上一層倒影 -webkit-box-reflect

        .g-wrap4 {
            -webkit-box-reflect: right 0px;
        }
        .g-wrap3 {
            -webkit-box-reflect: below 0px;
        }
        .g-wrap2 {
            -webkit-box-reflect: left 0px;
        }

        image

        最后,給 .g-wrap1 加上一層倒影 -webkit-box-reflect

        .g-wrap4 {
            -webkit-box-reflect: right 0px;
        }
        .g-wrap3 {
            -webkit-box-reflect: below 0px;
        }
        .g-wrap2 {
            -webkit-box-reflect: left 0px;
        }
        .g-wrap1 {
            -webkit-box-reflect: above 0px;
        }

        就可以得到一個通過 4 層倒影得到的圖形:

        image

        這樣,通過不同的基礎圖形,發揮我們的想象力,就可以生成各式各樣的剪紙對稱圖形:

        image

        完整的代碼,可以戳這里:

        CodePen Demo -- -webkit-box-reflect artist

        最后

        本文到此結束,希望對你有幫助 :),本文介紹了 webkit-box-reflect 的一些有意思的用法,可能業務中不太實用,但是非常有趣。

        想 Get 到最有意思的 CSS 資訊,千萬不要錯過我的公眾號 -- iCSS前端趣聞 ??

        image.png

        更多精彩 CSS 技術文章匯總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。

        如果還有什么疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

        查看原文

        贊 12 收藏 5 評論 1

        chokcoco 發布了文章 · 2月24日

        前端優秀實踐不完全指南

        本文其實應該叫,Web 用戶體驗設計提升指南。

        一個 Web 頁面,一個 APP,想讓別人用的爽,也就是所謂的良好的用戶體驗,我覺得他可能包括但不限于:

        • 急速的打開速度
        • 眼前一亮的 UI 設計
        • 酷炫的動畫效果
        • 豐富的個性化設置
        • 便捷的操作b
        • 貼心的細節
        • 關注殘障人士,良好的可訪問性
        • ...

        所謂的用戶體驗設計,其實是一個比較虛的概念,是秉承著以用戶為中心的思想的一種設計手段,以用戶需求為目標而進行的設計。設計過程注重以用戶為中心,用戶體驗的概念從開發的最早期就開始進入整個流程,并貫穿始終。

        良好的用戶體驗設計,是產品每一個環節共同努力的結果。

        除去一些很難一蹴而就的,本文將就頁面展示、交互細節、可訪問性三個方面入手,羅列一些在實際的開發過程中,積攢的一些有益的經驗。通過本文,你將能收獲到:

        1. 了解到一些小細節是如何影響用戶體驗的
        2. 了解到如何在盡量小的開發改動下,提升頁面的用戶體驗
        3. 了解到一些優秀的交互設計細節
        4. 了解基本的無障礙功能及頁面可訪問性的含義
        5. 了解基本的提升頁面可訪問性的方法

        頁面展示

        就整個頁面的展示,頁面內容的呈現而言,有一些小細節是需要我們注意的。

        整體布局

        先來看看一些布局相關的問題。

        對于大部分 PC 端的項目,我們首先需要考慮的肯定是最外層的一層包裹。假設就是 .g-app-wrapper。

        <div class="g-app-wrapper">
            <!-- 內部內容 -->
        </div>

        首先,對于 .g-app-wrapper,有幾點,是我們在項目開發前必須弄清楚的:

        1. 項目是全屏布局還是定寬布局?
        2. 對于全屏布局,需要適配的最小的寬度是多少?

        對于定寬布局,就比較方便了,假設定寬為 1200px,那么:

        .g-app-wrapper {
            width: 1200px;
            margin: 0 auto;
        }

        利用 margin: 0 auto 實現布局的水平居中。在屏幕寬度大于 1200px 時,兩側留白,當然屏幕寬度小于 1200px 時,則出現滾動條,保證內部內容不亂。

        layout1

        對于現代布局,更多的是全屏布局。其實現在也更提倡這種布局,即使用可隨用戶設備的尺寸和能力而變化的自適應布局。

        通常而言是左右兩欄,左側定寬,右側自適應剩余寬度,當然,會有一個最小的寬度。那么,它的布局應該是這樣:

        <div class="g-app-wrapper">
            <div class="g-sidebar"></div>
            <div class="g-main"></div>
        </div>
        .g-app-wrapper {
            display: flex;
            min-width: 1200px;
        }
        .g-sidebar {
            flex-basis: 250px;
            margin-right: 10px;
        }
        .g-main {
            flex-grow: 1;
        }

        layout2

        利用了 flex 布局下的 flex-grow: 1,讓 .main 進行伸縮,占滿剩余空間,利用 min-width 保證了整個容器的最小寬度。

        當然,這是最基本的自適應布局。對于現代布局,我們應該盡可能的考慮更多的場景。做到:

        image

        底部 footer

        下面一種情形也是非常常見的一個情景。

        頁面存在一個 footer 頁腳部分,如果整個頁面的內容高度小于視窗的高度,則 footer 固定在視窗底部,如果整個頁面的內容高度大于視窗的高度,則 footer 正常流排布(也就是需要滾動到底部才能看到 footer)。

        看看效果:

        margintopauto

        嗯,這個需求如果能夠使用 flex 的話,使用 justify-content: space-between 可以很好的解決,同理使用 margin-top: auto 也非常容易完成:

        <div class="g-container">
            <div class="g-real-box">
                ...
            </div>
            <div class="g-footer"></div>
        </div>
        .g-container {
            height: 100vh;
            display: flex;
            flex-direction: column;
        }
        
        .g-footer {
            margin-top: auto;
            flex-shrink: 0;
            height: 30px;
            background: deeppink;
        }

        Codepen Demo -- sticky footer by flex margin auto

        當然,實現它的方法有很多,這里僅給出一種推薦的解法。

        處理動態內容 - 文本超長

        對于所有接收后端接口字段的文本展示類的界面。都需要考慮全面(防御性編程:所有的外部數據都是不可信的),正常情況如下,是沒有問題的。

        image

        但是我們是否考慮到了文本會超長?超長了會折行還是換行?

        image

        對于單行文本,使用單行省略:

        {
            width: 200px;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        image

        當然,目前對于多行文本的超長省略,兼容性也已經非常好了:

        {
            width: 200px;
            overflow : hidden;
            text-overflow: ellipsis;
            display: -webkit-box;
            -webkit-line-clamp: 2;
            -webkit-box-orient: vertical;
        }

        image

        處理動態內容 - 保護邊界

        對于一些動態內容,我們經常使用 min/max-widthmin/max-height 對容器的高寬限度進行合理的控制。

        在使用它們的時候,也有一些細節需要考慮到。

        譬如經常會使用 min-width 控制按鈕的最小寬度:

        .btn {
            ...
            min-width: 120px;
        }

        image

        當內容比較少的時候是沒問題的,但是當內容比較長,就容易出現問題。使用了 min-width 卻沒考慮到按鈕的過長的情況:

        image

        這里就需要配合 padding 一起:

        .btn {
            ...
            min-width: 88px;
            padding: 0 16px
        }

        借用Min and Max Width/Height in CSS中一張非常好的圖,作為釋義:

        min-width-2

        0 內容展示

        這個也是一個常常被忽略的地方。

        頁面經常會有列表搜索,列表展示。那么,既然存在有數據的正常情況,當然也會存在搜索不到結果或者列表無內容可展示的情形。

        對于這種情況,一定要注意 0 結果頁面的設計,同時也要知道,這也是引導用戶的好地方。對于 0 結果頁面,分清楚:

        • 數據為空:其中又可能包括了用戶無權限、搜索無結果、篩選無結果、頁面無數據
        • 異常狀態:其中又可能包括了網絡異常、服務器異常、加載失敗等待

        不同的情況可能對應不同的 0 結果頁面,附帶不同的操作引導。

        譬如網絡異常:

        image

        或者確實是 0 結果:

        image

        關于 0 結果頁面設計,可以詳細看看這篇文章:如何設計產品的空白頁面?

        小小總結一下,上述比較長的篇幅一直都在闡述一個道理,開發時,不能僅僅關注正?,F象,要多考慮各種異常情況,思考全面。做好各種可能情況的處理。

        圖片相關

        圖片在我們的業務中應該是非常的常見了。有一些小細節是需要注意的。

        給圖片同時設置高寬

        有的時候和產品、設計會商定,只能使用固定尺寸大小的圖片,我們的布局可能是這樣:

        image

        對應的布局:

        <ul class="g-container">
            <li>
                <img data-original="http://placehold.it/150x100">
                <p>圖片描述</p>
            </li>
        </ul>
        ul li img {
            width: 150px;
        }

        當然,萬一假設后端接口出現一張非正常大小的圖片,上述不加保護的布局就會出問題:

        image

        所以對于圖片,我們總是建議同時寫上高和寬,避免因為圖片尺寸錯誤帶來的布局問題:

        ul li img {
            width: 150px;
            height: 100px;
        }

        同時,給 <img> 標簽同時寫上高寬,可以在圖片未加載之前提前占住位置,避免圖片從未加載狀態到渲染完成狀態高寬變化引起的重排問題。

        object-fit

        當然,限制高寬也會出現問題,譬如圖片被拉伸了,非常的難看:

        image

        這個時候,我們可以借助 object-fit,它能夠指定可替換元素的內容(也就是圖片)該如何適應它的父容器的高寬。

        ul li img {
            width: 150px;
            height: 100px;
            object-fit: cover;
        }

        利用 object-fit: cover,使圖片內容在保持其寬高比的同時填充元素的整個內容框。

        image

        object-fit 還有一個配套屬性 object-position,它可以控制圖片在其內容框中的位置。(類似于 background-position),m默認是 object-position: 50% 50%,如果你不希望圖片居中展示,可以使用它去改變圖片實際展示的 position 。

        ul li img {
            width: 150px;
            height: 100px;
            object-fit: cover;
            object-position: 50% 100%;
        }

        image

        像是這樣,object-position: 100% 50% 指明從底部開始展示圖片。這里有一個很好的 Demo 可以幫助你理解 object-position。

        CodePen Demo -- Object position

        考慮屏幕 dpr -- 響應式圖片

        正常情況下,圖片的展示應該沒有什么問題了。但是對于有圖片可展示的情況下,我們還可以做的更好。

        在移動端或者一些高清的 PC 屏幕(蘋果的 MAC Book),屏幕的 dpr 可能大于 1。這種時候,我們可能還需要考慮利用多倍圖去適配不同 dpr 的屏幕。

        正好,<img> 標簽是有提供相應的屬性 srcset 讓我們進行操作的。

        <img data-original='photo@1x.png'
           srcset='photo@1x.png 1x,
                   photo@2x.png 2x,
                   photo@3x.png 3x' 
        />

        當然,這是比較舊的寫法,srcset 新增了新的 w 寬度描述符,需要配合 sizes 一起使用,所以更好的寫法是:

        <img 
                src = "photo.png" 
                sizes = “(min-width: 600px) 600px, 300px" 
                srcset = “photo@1x.png 300w,
                               photo@2x.png 600w,
                               photo@3x.png 1200w,
        >

        利用 srcset,我們可以給不同 dpr 的屏幕,提供最適合的圖片。

        上述出現了一些概念,dpr,圖片的 srcset ,sizes 屬性,,不太了解的可以移步 前端基礎知識概述

        圖片丟失

        好了,當圖片鏈接沒問題時,已經處理好了。接下來還需要考慮,當圖片鏈接掛了,應該如何處理。

        處理的方式有很多種。最好的處理方式,是我最近在張鑫旭老師的這篇文章中 -- 圖片加載失敗后CSS樣式處理最佳實踐 看到的。這里簡單講下:

        1. 利用圖片加載失敗,觸發 <img> 元素的 onerror 事件,給加載失敗的 <img> 元素新增一個樣式類
        2. 利用新增的樣式類,配合 <img> 元素的偽元素,展示默認兜底圖的同時,還能一起展示 <img> 元素的 alt 信息
        <img data-original="test.png" alt="圖片描述" onerror="this.classList.add('error');">
        img.error {
            position: relative;
            display: inline-block;
        }
        
        img.error::before {
            content: "";
            /** 定位代碼 **/
            background: url(error-default.png);
        }
        
        img.error::after {
            content: attr(alt);
            /** 定位代碼 **/
        }

        我們利用偽元素 before ,加載默認錯誤兜底圖,利用偽元素 after,展示圖片的 alt 信息:

        image

        OK,到此,完整的對圖片的處理就算完成了,完整的 Demo 你可以戳這里看看:

        CodePen Demo -- 圖片處理

        交互設計優化

        接下來一個大環節是關于一些交互的細節。對于交互設計,一些比較通用的準則:

        • Don’t make me think
        • 符合用戶的習慣與預期
        • 操作便利
        • 做適當的提醒
        • 不強迫用戶

        過渡與動畫

        在我們的交互過程中,適當的增加過渡與動畫,能夠很好的讓用戶感知到頁面的變化。

        譬如我們頁面上隨處可見 loading 效果,其實就是這樣一種作用,讓用戶感知頁面正在加載,或者正在處理某些事務。

        滾動優化

        滾動也是操作網頁中非常重要的一環??纯从心男┛梢詢灮狞c:

        滾動平滑:使用 scroll-behavior: smooth 讓滾動絲滑

        使用 scroll-behavior: smooth,可以讓滾動框實現平穩的滾動,而不是突兀的跳動??纯葱Ч?,假設如下結構:

        <div class="g-container">
          <nav>
            <a href="#1">1</a>
            <a href="#2">2</a>
            <a href="#3">3</a>
          </nav>
          <div class="scrolling-box">
            <section id="1">First section</section>
            <section id="2">Second section</section>
            <section id="3">Third section</section>
          </div>
        </div>

        不使用 scroll-behavior: smooth,是突兀的跳動切換:

        scrol

        給可滾動容器添加 scroll-behavior: smooth,實現平滑滾動:

        {
            scroll-behavior: smooth;
        }

        scroll2

        使用 scroll-snap-type 優化滾動效果

        sroll-snap-type 可能算得上是新的滾動規范里面最核心的一個屬性樣式。

        scroll-snap-type:屬性定義在滾動容器中的一個臨時點(snap point)如何被嚴格的執行。

        光看定義有點難理解,簡單而言,這個屬性規定了一個容器是否對內部滾動動作進行捕捉,并且規定了如何去處理滾動結束狀態。讓滾動操作結束后,元素停止在適合的位置。

        看個簡單示例:

        當然,scroll-snap-type 用法非常多,可控制優化的點很多,限于篇幅無法一一展開,具體更詳細的用法可以看看我的另外一篇文章 -- 使用 sroll-snap-type 優化滾動

        控制滾動層級,避免頁面大量重排

        這個優化可能稍微有一點難理解。需要了解 CSS 渲染優化的相關知識。

        先說結論,控制滾動層級的意思是盡量讓需要進行 CSS 動畫(可以是元素的動畫,也可以是容器的滾動)的元素的 z-index 保持在頁面最上方,避免瀏覽器創建不必要的圖形層(GraphicsLayer),能夠很好的提升渲染性能。

        這一點怎么理解呢,一個元素觸發創建一個 Graphics Layer 層的其中一個因素是:

        • 元素有一個 z-index 較低且包含一個復合層的兄弟元素

        根據上述這點,我們對滾動性能進行優化的時候,需要注意兩點:

        1. 通過生成獨立的 GraphicsLayer,利用 GPU 加速,提升滾動的性能
        2. 如果本身滾動沒有性能問題,不需要獨立的 GraphicsLayer,也要注意滾動容器的層級,避免因為層級過高而被其他創建了 GraphicsLayer 的元素合并,被動的生成一個 Graphics Layer ,影響頁面整體的渲染性能

        如果你對這點還有點懵,可以看看這篇文章 -- 你所不知道的 CSS 動畫技巧與細節

        點擊交互優化

        在用戶點擊交互方面,也有一些有意思的小細節。

        優化手勢 -- 不同場景應用不同 cursor

        對于不同的內容,最好給與不同的 cursor 樣式,CSS 原生提供非常多種常用的手勢。

        在不同的場景使用不同的鼠標手勢,符合用戶的習慣與預期,可以很好的提升用戶的交互體驗。

        首先對于按鈕,就至少會有 3 種不同的 cursor,分別是可點擊,不可點擊,等待中:

        {
            cursor: pointer;    // 可點擊
            cursor: not-allowed;    // 不可點擊
            cursor: wait;    // loading
        }

        image

        除此之外,還有一些常見的,對于一些可輸入的 Input 框,使用 cursor: text,對于提示 Tips 類使用 cursor: help,放大縮小圖片 zoom-in、zoom-out 等等:

        image

        一些常用的簡單列一列:

        • 按鈕可點擊: cursor: pointer
        • 按鈕禁止點擊:cursor: not-allowed
        • 等待 Loading 狀態:cursor: wait
        • 輸入框:cursor: text;
        • 圖片查看器可放大可縮?。?code>cursor: zoom-in/ zoom-out
        • 提示:cursor: help;

        當然,實際 cursor 還支持非常多種,可以在 MDN 或者下面這個 CodePen Demo 中查看這里看完整的列表:

        CodePen Demo -- Cursor Demo

        點擊區域優化 -- 偽元素擴大點擊區域

        按鈕是我們網頁設計中十分重要的一環,而按鈕的設計也與用戶體驗息息相關。

        考慮這樣一個場景,在搖晃的車廂上或者是單手操作著屏幕,有的時候一個按鈕,死活也點不到。

        讓用戶更容易的點擊到按鈕無疑能很好的增加用戶體驗及可提升頁面的訪問性,尤其是在移動端,按鈕通常都很小,但是受限于設計稿或者整體 UI 風格,我們不能直接去改變按鈕元素的高寬。

        那么這個時候有什么辦法在不改變按鈕原本大小的情況下去增加他的點擊熱區呢?

        這里,偽元素也是可以代表其宿主元素來響應的鼠標交互事件的。借助偽元素可以輕松幫我們實現,我們可以這樣寫:

        .btn::before{
          content:"";
          position:absolute;
          top:-10px;
          right:-10px;
          bottom:-10px;
          left:-10px;
        }

        當然,在 PC 端下這樣子看起來有點奇怪,但是合理的用在點擊區域較小的移動端則能取到十分好的效果,效果如下:

        608782-20160527112625428-906375003

        在按鈕的偽元素沒有其它用途的時候,這個方法確實是個很好的提升用戶體驗的點。

        快速選擇優化 -- user-select: all

        操作系統或者瀏覽器通常會提供一些快速選取文本的功能,看看下面的示意圖:

        layout3

        快速單擊兩次,可以選中單個單詞,快速單擊三次,可以選中一整行內容。但是如果有的時候我們的核心內容,被分隔符分割,或者潛藏在一整行中的一部分,這個時候選取起來就比較麻煩。

        利用 user-select: all,可以將需要一次選中的內容進行包裹,用戶只需要點擊一次,就可以選中該段信息:

        .g-select-all {
            user-select: all
        }

        給需要一次選中的信息,加上這個樣式后的效果,這個細節作用在一些需要復制粘貼的場景,非常好用:

        layout4

        CodePen -- user-select: all 示例

        選中樣式優化 -- ::selection

        當然,如果你想更進一步,CSS 還有提供一個 ::selection 偽類,可以控制選中的文本的樣式(只能控制color, background, text-shadow),進一步加深效果。

        layout5

        CodePen -- user-select: all && ::selection 控制選中樣式

        添加禁止選擇 -- user-select: none

        有快速選擇,也就會有它的對立面 -- 禁止選擇。

        對于一些可能頻繁操作的按鈕,可能出現如下尷尬的場景:

        • 文本按鈕的快速點擊,觸發了瀏覽器的雙擊快速選擇,導致文本被選中:

        btn-click

        • 翻頁按鈕的快速點擊,觸發了瀏覽器的雙擊快速選擇:

        img-click

        對于這種場景,我們需要把不可被選中元素設置為不可被選中,利用 CSS 可以快速的實現這一點:

        {
            -webkit-user-select: none; /* Safari */
            -ms-user-select: none; /* IE 10 and IE 11 */
            user-select: none; /* Standard syntax */
        }

        這樣,無論點擊的頻率多快,都不會出現尷尬的內容選中:

        btn-click-unselect

        跳轉優化

        現階段,單頁應用(Single Page Application)的應用非常廣泛,Vue 、React 等框架大行其道。但是一些常見的寫法,也容易衍生一些小問題。

        譬如,點擊按鈕、文本進行路由跳轉。譬如,經常會出現這種代碼:

        <template>
            ...
            <button @click="gotoDetail">
                Detail
            </button>
            ...
        <template>
        ...
        gotoDetail() {
            this.$router.push({
              name: 'xxxxx',
            });
        }

        大致邏輯就是給按鈕添加一個事件,點擊之后,跳轉到另外一個路由。當然,本身這個功能是沒有任何問題的,但是沒有考慮到用戶實際使用的場景。

        實際使用的時候,由于是一個頁面跳轉,很多時候,用戶希望能夠保留當前頁面的內容,同時打開一個新的窗口,這個時候,他會嘗試下的鼠標右鍵,選擇在新標簽頁中打開頁面,遺憾的是,上述的寫法是不支持鼠標右鍵打開新頁面的。

        原因在于瀏覽器是通過讀取 <a> 標簽的 href 屬性,來展示類似在新標簽頁中打開頁面這種選項,對于上述的寫法,瀏覽器是無法識別它是一個可以跳轉的鏈接。簡單的示意圖如下:

        image

        所以,對于所有路由跳轉按鈕,建議都使用 <a> 標簽,并且內置 href 屬性,填寫跳轉的路由地址。實際渲染出來的 DOM 可能是需要類似這樣:

        <a href="/xx/detail">Detail</a>

        易用性

        易用性也是交互設計中需要考慮的一個非常重要的環節,能做的有非常多。簡單的羅列一下:

        • 注意界面元素的一致性,降低用戶學習成本
        • 延續用戶日常的使用習慣,而不是重新創造
        • 給下拉框增加一些預設值,降低用戶填寫成本
        • 同類的操作合并在一起,降低用戶的認知成本
        • 任何操作之后都要給出反饋,讓用戶知道操作已經生效

        先探索,后表態

        這一點非常的有意思,什么叫先探索后表態呢?就是我們不要一上來就強迫用戶去做一些事情,譬如登錄。

        想一想一些常用網站的例子:

        • 類似虎牙、Bilibili 等視頻網站,可以先藍光體驗,一定觀看時間后才會要求登錄
        • 電商網站,只有到付款的時候,才需要登錄

        上述易用性先探索,后表態的內容,部分來源于:Learn From What Leading Companies A/B Test,可以好好讀一讀。

        字體優化

        字體的選擇與使用其實是非常有講究的。

        如果網站沒有強制必須使用某些字體。最新的規范建議我們更多的去使用系統默認字體。也就是 CSS Fonts Module Level 4 -- Generic font families 中新增的 font-family: system-ui 關鍵字。

        font-family: system-ui 能夠自動選擇本操作系統下的默認系統字體。

        默認使用特定操作系統的系統字體可以提高性能,因為瀏覽器或者 webview 不必去下載任何字體文件,而是使用已有的字體文件。 font-family: system-ui 字體設置的優勢之處在于它與當前操作系統使用的字體相匹配,對于文本內容而言,它可以得到最恰當的展示。

        舉兩個例子,天貓的字體定義與 Github 的字體定義:

        • 天貓font-family: "PingFang SC",miui,system-ui,-apple-system,BlinkMacSystemFont,Helvetica Neue,Helvetica,sans-serif;
        • Githubfont-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;

        簡單而言,它們總體遵循了這樣一個基本原則:

        1、盡量使用系統默認字體

        使用系統默認字體的主要原因是性能,并且系統字體的優點在于它與當前操作系統使用的相匹配,因此它的文本展示必然也是一個讓人舒適展示效果。

        2、兼顧中西,西文在前,中文在后

        中文或者西文(英文)都要考慮到。由于大部分中文字體也是帶有英文部分的,但是英文部分又不怎么好看,但是英文字體中大多不包含中文。通常會先進行英文字體的聲明,選擇最優的英文字體,這樣不會影響到中文字體的選擇,中文字體聲明則緊隨其次。

        3、兼顧多操作系統

        選擇字體的時候要考慮多操作系統。例如 MAC OS 下的很多中文字體在 Windows 都沒有預裝,為了保證 MAC 用戶的體驗,在定義中文字體的時候,先定義 MAC 用戶的中文字體,再定義 Windows 用戶的中文字體;

        4、兼顧舊操作系統,以字體族系列?serif?和?sans-serif?結尾

        當使用一些非常新的字體時,要考慮向下兼容,兼顧到一些極舊的操作系統,使用字體族系列?serif?和?sans-serif?結尾總歸是不錯的選擇。

        對于上述的一些字體可能會有些懵,譬如 -apple-system, BlinkMacSystemFont,這是因為不同瀏覽器廠商對規范的實現有所不同,對于字體定義更多的相關細節,可以再看看這篇文章 -- Web 字體 font-family 再探秘

        可訪問性(A11Y)

        可訪問性,在我們的網站中,屬于非常重要的一環,但是大部分前端(其實應該是設計、前端、產品)同學都會忽視它。

        我潛伏在一個叫無障礙設計小組的群里,其中包含了很多無障礙設計師以及患有一定程度視覺、聽力、行動障礙的用戶,他們在群里經常會表達出一個觀點,就是國內的大部分 Web 網站及 APP 基本沒有考慮過殘障人士的使用(或者可訪問性做的非常差),非常的令人揪心。

        尤其在我們一些重交互、重邏輯的網站中,我們需要考慮用戶的使用習慣、使用場景,從高可訪問性的角度考慮,譬如假設用戶沒有鼠標,僅僅使用鍵盤,能否順暢的使用我們的網站?

        假設用戶沒有鼠標,這個真不一定是針對殘障人士,很多情況下,用戶拿鼠標的手可能在干其他事情,比如在吃東西,又或者在 TO B 類的業務,如超市收銀、倉庫收貨,很可能用戶拿鼠標的手操作著其他設備(掃碼槍)等等。

        本文不會專門闡述無障礙設計的方方面面,只是從一些我覺得前端工程師需要關注的,并且僅需要花費少量代價就能做好的一些無障礙設計細節。記住,無障礙設計對所有人都更友善。

        色彩對比度

        顏色,也是我們天天需要打交道的屬性。對于大部分視覺正常的用戶,可能對頁面的顏色敏感度還沒那么高。但是對于一小部分色弱、色盲用戶,他們對于網站的顏色會更加敏感,不好的設計會給他們訪問網站帶來極大的不便。

        什么是色彩對比度

        是否曾關心過頁面內容的展示,使用的顏色是否恰當?色弱、色盲用戶能否正??辞鍍热??良好的色彩使用,在任何時候都是有益的,而且不僅僅局限于對于色弱、色盲用戶。在戶外用手機、陽光很強看不清,符合無障礙標準的高清晰度、高對比度文字就更容易閱讀。

        這里就有一個概念 -- 顏色對比度,簡單地說,描述就是兩種顏色在亮度(Brightness)上的差別。運用到我們的頁面上,大多數的情況就是背景色(background-color)與內容顏色(color)的對比差異。

        最權威的互聯網無障礙規范 —— WCAG AA規范規定,所有重要內容的色彩對比度需要達到 4.5:1 或以上(字號大于18號時達到 3:1 或以上),才算擁有較好的可讀性。

        借用一張圖 -- 知乎 -- 助你輕松做好無障礙的15個UI設計工具推薦

        很明顯,上述最后一個例子,文字已經非常的不清晰了,正常用戶都已經很難看得清了。

        檢查色彩對比度的工具

        Chrome 瀏覽器從很早開始,就已經支持檢查元素的色彩對比度了。以我當前正在寫作的頁面為例子,Github Issues 編輯頁面的兩個按鈕:

        image

        審查元素,分別可以看到兩個按鈕的色彩對比度:

        image

        可以看到,綠底白字按鈕的色彩對比度是沒有達到標準的,也被用黃色的嘆號標識了出來。

        除此之外,在審查元素的 Style 界面的取色器,改變顏色,也能直觀的看到當前的色彩對比度:

        image

        焦點響應

        類似百度、谷歌的首頁,進入頁面后會默認讓輸入框獲得焦點:

        image

        并非所有的有輸入框的頁面,都需要進入頁面后進行聚焦,但是焦點能夠讓用戶非常明確的知道,當前自己在哪,需要做些什么。尤其是對于無法操作鼠標的用戶。

        頁面上可以聚焦的元素,稱為可聚焦元素,獲得焦點的元素,則會觸發該元素的 focus 事件,對應的,也就會觸發該元素的 :focus 偽類。

        瀏覽器通常會使用元素的 :focus 偽類,給元素添加一層邊框,告訴用戶,當前的獲焦元素在哪里。

        我們可以通過鍵盤的 Tab 鍵,進行焦點的切換,而獲焦元素則可以通過元素的 :focus 偽類的樣式,告訴用戶當前焦點位置。

        當然,除了 Tab 鍵之外,對于一些多輸入框、選擇框的表單頁面,我們也應該想著如何簡化用戶的操作,譬如用戶按回車鍵時自動前進到下一字段。一般而言,用戶必須執行的觸按越少,體驗越佳。

        下面的截圖,完全由鍵盤操作完成

        a11y

        通過元素的 :focus 偽類以及鍵盤 Tab 鍵切換焦點,用戶可以非常順暢的在脫離鼠標的情況下,對頁面的焦點切換及操作。

        然而,在許多 reset.css 中,經常能看到這樣一句 CSS 樣式代碼,為了樣式的統一,消除了可聚焦元素的 :focus 偽類:

        :focus {
            outline: 0;
        }

        我們給上述操作的代碼。也加上這樣一句代碼,全程再用鍵盤操作一下

        a11y2

        除了在 input 框有光標提示,當使用 Tab 進行焦點切換到 select 或者到 button 時,由于沒有了 :focus 樣式,用戶將完全懵逼,不知道頁面的焦點現在處于何處。

        保證非鼠標用戶體驗,合理運用 :focus-visible

        當然,造成上述結果很重要的一個原因在于。:focus 偽類不論用戶在使用鼠標還是使用鍵盤,只要元素獲焦,就會觸發。

        而其本身的默認樣式又不太能被產品或者設計接受,導致了很多人會在焦點元素觸發 :focus 偽類時,通過改變 border 的顏色或者其他一些方式替代或者直接禁用。而這樣做,從可訪問性的角度來看,對于非鼠標用戶,無疑是災難性的。

        基于此,在W3 CSS selectors-4 規范 中,新增了一個非常有意思的 :focus-visible 偽類。

        :focus-visible:這個選擇器可以有效地根據用戶的輸入方式(鼠標 vs 鍵盤)展示不同形式的焦點。

        有了這個偽類,就可以做到,當用戶使用鼠標操作可聚焦元素時,不展示 :focus 樣式或者讓其表現較弱,而當用戶使用鍵盤操作焦點時,利用 :focus-visible,讓可獲焦元素獲得一個較強的表現樣式。

        看個簡單的 Demo:

        <button>Test 1</button>
        button:active {
          background: #eee;
        }
        button:focus {
          outline: 2px solid red;
        }

        使用鼠標點擊:

        a11y3

        可以看到,使用鼠標點擊的時候,觸發了元素的 :active 偽類,也觸發了 :focus偽類,不太美觀。但是如果設置了 outline: none 又會使鍵盤用戶的體驗非常糟糕。嘗試使用 :focus-visible 偽類改造一下:

        button:active {
          background: #eee;
        }
        button:focus {
          outline: 2px solid red;
        }
        button:focus:not(:focus-visible) {
          outline: none;
        }

        看看效果,分別是在鼠標點擊 Button 和使用鍵盤控制焦點點擊 Button:

        a11y4

        CodePen Demo -- :focus-visible example

        可以看到,使用鼠標點擊,不會觸發 :foucs,只有當鍵盤操作聚焦元素,使用 Tab 切換焦點時,outline: 2px solid red 這段代碼才會生效。

        這樣,我們就既保證了正常用戶的點擊體驗,也保證了一批無法使用鼠標的用戶的焦點管理體驗。

        值得注意的是,有同學會疑惑,這里為什么使用了 :not 這么繞的寫法而不是直接這樣寫呢:

        button:focus {
          outline: unset;
        }
        button:focus-visible {
          outline: 2px solid red;
        }

        為的是兼容不支持 :focus-visible 的瀏覽器,當 :focus-visible 不兼容時,還是需要有 :focus 偽類的存在。

        使用 WAI-ARIA 規范增強語義 -- div 等非可獲焦元素模擬獲焦元素

        還有一個非常需要注意的點。

        現在很多前端同學在前端開發的過程中,喜歡使用非可獲焦元素模擬獲焦元素,譬如:

        • 使用 div 模擬 button 元素
        • 使用 ul 模擬下拉列表 select 等等

        當下很多組件庫都是這樣做的,譬如 element-ui 和 ant-design。

        在使用非可獲焦元素模擬獲焦元素的時候,一定要注意,不僅僅只是外觀長得像就完事了,其行為表現也需要符合原本的 button、select 等可聚焦元素的性質,能夠體現元素的語義,能夠被聚焦,能夠通過 Tab 切換等等。

        基于大量類似的場景,有了 WAI-ARIA 標準,WAI-ARIA是一個為殘疾人士等提供無障礙訪問動態、可交互Web內容的技術規范。

        簡單來說,它提供了一些屬性,增強標簽的語義及行為:

        • 可以使用 tabindex 屬性控制元素是否可以聚焦,以及它是否/在何處參與順序鍵盤導航
        • 可以使用 role 屬性,來標識元素的語義及作用,譬如使用 <div id="saveChanges" tabindex="0" role="button">Save</div> 來模擬一個按鈕
        • 還有大量的 aria-* 屬性,表示元素的屬性或狀態,幫助我們進一步地識別以及實現元素的語義化,優化無障礙體驗

        使用工具查看標簽的語義

        我們來看看 Github 頁面是如何定義一個按鈕的,以 Github Issues 頁面的 Edit 按鈕為例子:

        image

        這一塊,清晰的描述了這個按鈕在可訪問性相關的一些特性,譬如 Contrast 色彩對比度,按鈕的描述,也就是 Name,是給屏幕閱讀器看到的,Role 標識是這個元素的屬性,它是一個按鈕,Keyboard focusable 則表明他能否被鍵盤的 Tab 按鈕給捕獲。

        分析使用非可聚焦元素模擬的按鈕

        這里,我隨便選取了我們業務中一個使用 span 模擬按鈕的場景,是一個面包屑導航,點擊可進行跳轉,發現慘不忍睹:

        image

        HTML 代碼:

        <span class="ssc-breadcrumb-item-link"> Inbound </span>

        image

        基本上可訪問性為 0,作為一個按鈕,它不可被聚焦,無法被鍵盤用戶選中,沒有具體的語義,色彩對比度太低,可能視障用戶無法看清。并且,作為一個能進行頁面跳轉的按鈕,它沒有不是 a 標簽,沒有 href 屬性。

        即便對于面包屑導航,我們可以不將它改造成 <a> 標簽,也需要做到最基本的一些可訪問性改造:

        <span role="button" aria-label="goto inbound page" tabindex="0" class="ssc-breadcrumb-item-link"> Inbound </span>

        不要忘了再改一下顏色,達到最低色彩對比度以上,再看看:

        image

        OK,這樣,一個最最最基本的,滿足最低可訪問性需求的按鈕算是勉強達標,當然,這個按鈕可以再更進一步進行改造,涉及了更深入的可訪問性知識,本文不深入展開。

        分析組件庫的 A11Y

        最后,在我們比較常用的 Vue - element-ui、React - ant-design 中,我們來看看 ant-design 在提升可訪問性相關的一些功能。

        以 Select 選擇框組件為例,ant-design 利用了大量的 WAI-ARIA 屬性,使得用 div 模擬的下拉框不僅僅在表現上符合一個下拉框,在語義、行為上都符合一個下拉框,簡單的一個例子:

        image

        看看使用 div 模擬下拉框的 DOM 部分:

        image

        再看看在交互體驗上:

        a11y5

        上述操作全是在鍵盤下完成,看著平平無奇,實際上組件庫在正常響應可獲焦元素切換的同時,給用 div 模擬的 select 加了很多鍵盤事件的響應,可以利用回車,上下鍵等對可選項進行選擇。其實是下了很多功夫。

        對于 A11Y 相關的內容,篇幅及內容非常之多,本文無法一一展開,感興趣的可以通讀下下列文章:

        總結一下

        本文從頁面展示、交互細節、可訪問性三個大方面入手,羅列一些在實際的開發過程中,積攢的一些有益的經驗。雖然不夠全面,不過從一開始也就沒想著大而全,主要是一些可能有用但是容易被忽視的點,也算是一個不錯的查缺補漏小指南。

        當然,很多都是我個人的觀點想法,可能有一些理解存在一些問題,一些概念沒有解讀到位,也希望大家幫忙指出。

        最后

        本文到此結束,希望對你有幫助 :)

        想 Get 到最有意思的 CSS 資訊,千萬不要錯過我的公眾號 -- iCSS前端趣聞 ??

        gzh_small.png

        更多精彩 CSS 技術文章匯總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。

        如果還有什么疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

        查看原文

        贊 80 收藏 53 評論 7

        chokcoco 發布了文章 · 2月20日

        使用 mask 實現視頻彈幕人物遮罩過濾

        經??匆恍?LOL 比賽直播的小伙伴,肯定都知道,在一些彈幕網站(Bilibili、虎牙)中,當人物與彈幕出現在一起的時候,彈幕會“巧妙”的躲到人物的下面,看著非常的智能。

        簡單的一個截圖例子:

        image

        其實,這里是運用了 CSS 中的 MASK 屬性實現的。

        mask 簡單用法介紹

        之前在多篇文章都提到了 mask,比較詳細的一篇是 -- 奇妙的 CSS MASK,本文不對 mask 的基本概念做過多講解,向下閱讀時,如果對一些 mask 的用法感到疑惑,可以再去看看。

        這里只簡單介紹下 mask 的基本用法:

        最基本,使用 mask 的方式是借助圖片,類似這樣:

        {
            /* Image values */
            mask: url(mask.png);                       /* 使用位圖來做遮罩 */
            mask: url(masks.svg#star);                 /* 使用 SVG 圖形中的形狀來做遮罩 */
        }

        當然,使用圖片的方式后文會再講。借助圖片的方式其實比較繁瑣,因為我們首先還得準備相應的圖片素材,除了圖片,mask 還可以接受一個類似 background 的參數,也就是漸變。

        類似如下使用方法:

        {
            mask: linear-gradient(#000, transparent)                      /* 使用漸變來做遮罩 */
        }

        那該具體怎么使用呢?一個非常簡單的例子,上述我們創造了一個從黑色到透明漸變色,我們將它運用到實際中,代碼類似這樣:

        下面這樣一張圖片,疊加上一個從透明到黑色的漸變,

        {
            background: url(image.png) ;
            mask: linear-gradient(90deg, transparent, #fff);
        }

        image

        應用了 mask 之后,就會變成這樣:

        image

        這個 DEMO,可以先簡單了解到 mask 的基本用法。

        這里得到了使用 mask 最重要結論:添加了 mask 屬性的元素,其內容會與 mask 表示的漸變的 transparent 的重疊部分,并且重疊部分將會變得透明。

        值得注意的是,上面的漸變使用的是 linear-gradient(90deg, transparent, #fff),這里的 #fff 純色部分其實換成任意顏色都可以,不影響效果。

        CodePen Demo -- 使用 MASK 的基本使用

        使用 mask 實現人物遮罩過濾

        了解了 mask 的用法后,接下來,我們運用 mask,簡單實現視頻彈幕中,彈幕碰到人物,自動被隱藏過濾的例子。

        首先,我簡單的模擬了一個召喚師峽谷,以及一些基本的彈幕:

        mask1

        方便示意,這里使用了一張靜態圖,表示了召喚師峽谷的地圖,并非真的視頻,而彈幕則是一條一條的 <p> 元素,和實際情況一致。偽代碼大概是這樣:

        <!-- 地圖 -->
        <div class="g-map"></div>
        <!-- 包裹所有彈幕的容器 -->
        <div class="g-barrage-container">
            <!-- 所有彈幕 -->
            <div class="g-barrage">6666</div>
            ...
            <div class="g-barrage">6666</div>
        </div>

        為了模擬實際情況,我們再用一個 div 添加一個實際的人物,如果不做任何處理,其實就是我們看視頻打開彈幕的感受,人物被視頻所遮擋:

        mask2

        注意,這里我添加了一個人物亞索,并且用 animation 模擬了簡單的運動,在運動的過程中,人物是被彈幕給遮擋住的。

        接下來,就可以請出 mask 了。

        我們利用 mask 制作一個 radial-gradient ,使得人物附近為 transparent,并且根據人物運動的 animation,給 mask 的 mask-position 也添加上相同的 animation 即可。最終可以得到這樣的效果:

        .g-barrage-container {
            position: absolute;
            mask: radial-gradient(circle at 100px 100px, transparent 60px, #fff 80px, #fff 100%);
            animation: mask 10s infinite alternate;
        }
        
        @keyframes mask {
            100% {
                mask-position: 85vw 0;
            }
        }

        mask3

        實際上就是給放置彈幕的容器,添加一個 mask 屬性,把人物所在的位置標識出來,并且根據人物的運動不斷的去變換這個 mask 即可。我們把 mask 換成 background,原理一看就懂。

        • 把 mask 替換成 background 示意圖:

        mask4

        background 透明的地方,即 mask 中為 transparent 的部分,實際就是彈幕會被隱藏遮罩的部分,而其他白色部分,彈幕不會被隱藏,正是完美的利用了 mask 的特性。

        其實這項技術和視頻本身是無關的,我們只需要根據視頻計算需要屏蔽掉彈幕的位置,得到相應的 mask 參數即可。如果去掉背景和運動的人物,只保留彈幕和 mask,是這樣的:

        mask6

        需要明確的是,使用 mask,不是將彈幕部分給遮擋住,而是利用 mask,指定彈幕容器之下,哪些部分正常展示,哪些部分透明隱藏。

        最后,完整的 Demo 你可以戳這里:

        CodePen Demo -- mask 實現彈幕人物遮罩過濾

        實際生產環境中的運用

        當然,上面我們簡單的還原了利用 mask 實現彈幕遮罩過濾的效果。但是實際情況比上述的場景復雜的多,因為人物英雄的位置是不確定的,每一刻都在變化。所以在實際生產環境中,mask 圖片的參數,其實是由后端實時對視頻進行處理計算出來的,然后傳給前端,前端再進行渲染。

        對于運用了這項技術的直播網站,我們可以審查元素,看到包裹彈幕的容器的 mask 屬性,每時每刻都在發生變化:

        mask5

        返回回來的其實是一個 SVG 圖片,大概長這個樣子:

        image

        這樣,根據視頻人物的實時位置變化,不斷計算新的 mask,再實時作用于彈幕容器之上,實現遮罩過濾。

        最后

        本文到此結束,希望對你有幫助 :),本文介紹了 CSS mask 的一個實際生產環境中,非常有意義的一次實踐,也表明很多新的 CSS 技術,運用得當,還是能給業務帶來非常有益的幫助的。

        想 Get 到最有意思的 CSS 資訊,千萬不要錯過我的公眾號 -- iCSS前端趣聞 ??

        gzh_small.png

        更多精彩 CSS 技術文章匯總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。

        如果還有什么疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

        查看原文

        贊 54 收藏 29 評論 7

        chokcoco 發布了文章 · 2月5日

        CSS奇思妙想 -- 使用 background 創造各種美妙的背景

        本文屬于 CSS 繪圖技巧其中一篇,系列文章:

        將介紹一些利用 CSS 中的 background、mix-blend-mode、mask 及一些相關屬性,制作一些稍微復雜、酷炫的背景。

        通過本文,你將會了解到 CSS background 中更為強大的一些用法,并且學會利用 background 相關的一些屬性,采用不同的方式,去創造更復雜的背景圖案。在這個過程中,你會更好的掌握不同的漸變技巧,更深層次的理解各種不同的漸變。

        同時,借助強大的 CSS-Doodle,你將學會如何運用一套規則,快速創建大量不同的隨機圖案,感受 CSS 的強大,走進 CSS 的美。

        背景基礎知識

        我們都知道,CSS 中的 background 是非常強大的。

        首先,復習一下基礎,在日常中,我們使用最多的應該就是下面 4 種:

        • 純色背景 background: #000

        • 線性漸變 background: linear-gradient(#fff, #000) :

        • 徑向漸變 background: radial-gradient(#fff, #000) :

        • 角向漸變 background: conic-gradient(#fff, #000) :

        背景進階

        當然。掌握了基本的漸變之后,我們開始向更復雜的背景圖案進發。我最早是在《CSS Secret》一書中接觸學習到使用漸變去實現各種背景圖案的。然后就是不斷的摸索嘗試,總結出了一些經驗。

        在嘗試使用漸變去制作更復雜的背景之前,列出一些比較重要的技巧點:

        • 漸變不僅僅只能是單個的 linear-gradient 或者單個的 radial-gradient,對于 background 而言,它是支持多重漸變的疊加的,一點非常重要;
        • 靈活使用 repeating-linear-gradeintrepeating-radial-gradeint),它能減少很多代碼量
        • transparent 透明無處不在
        • 嘗試 mix-blend-modemask,創建復雜圖案的靈魂
        • 使用隨機變量,它能讓一個 idea 變成無數美麗的圖案

        接下來,開始組合之旅。

        使用 mix-blend-mode

        mix-blend-mode ,混合模式。最常見于 photoshop 中,是 PS 中十分強大的功能之一。在 CSS 中,我們可以利用混合模式將多個圖層混合得到一個新的效果。

        關于混合模式的一些基礎用法,你可以參考我的這幾篇文章:

        然后,我們來嘗試第一個圖案,先簡單體會一下 mix-blend-mode 的作用。

        我們使用 repeating-linear-gradient 重復線性漸變,制作兩個角度相反的背景條紋圖。正常而言,不使用混合模式,將兩個圖案疊加在一起,看看會發生什么。

        額,會發生什么就有鬼了 。顯而易見,由于圖案不是透明的,疊加在一起之后,由于層疊的關系,只能看到其中一張圖。

        好,在這個基礎上,我們給最上層的圖案,添加 mix-blend-mode: multiply,再來一次,看看這次會發生什么。

        可以看到,添加了混合模式之后,兩張背景圖通過某種算法疊加在了一起,展現出了非常漂亮的圖案效果,也正是我們想要的效果。

        CodePen Demo - Repeating-linear-gradient background & mix-blend-mode

        嘗試不同的 mix-blend-mode

        那為什么上面使用的是 mix-blend-mode: multiply 呢?用其他混合模式可以不可以?

        當然可以。這里僅僅只是一個示例,mix-blend-mode: multiply 在 PS 中意為正片疊底,屬于圖層混合模式的變暗模式組之一。

        我們使用上面的 DEMO,嘗試其他的混合模式,可以得到不同的效果。

        可以看到,不同的混合模式的疊加,效果相差非常之大。當然,運用不同的混合模式,我們也就可以創造出效果各異的圖案。

        CodePen Demo - Repeating-linear-gradient background & mix-blend-mode

        借助 CSS-Doodle 隨機生成圖案

        到這,就不得不引出一個寫 CSS 的神器 -- CSS-Doodle,我在其他非常多文章中也多次提到過 CSS-doodle,簡單而言,它是一個基于 Web-Component 的庫。允許我們快速的創建基于 CSS Grid 布局的頁面,并且提供各種便捷的指令及函數(隨機、循環等等),讓我們能通過一套規則,得到不同 CSS 效果。

        還是以上面的 DEMO 作為示例,我們將 repeating-linear-gradient 生成的重復條紋背景的顏色、粗細、角度隨機化、采用的混合模式也是隨機選取,然后利用 CSS-Doodle,快速隨機的創建各種基于此規則的圖案:

        可以點進去嘗試一下,點擊鼠標即可隨機生成不同的效果:

        CodePen Demo -- CSS Doodle - CSS MIX-BLEND-MODE Background

        嘗試使用徑向漸變

        當然,上面使用的是線性漸變,同樣,我們也可以使用徑向漸變運用同樣的套路。

        我們可以使用徑向漸變,生成多重的徑向漸變。像是這樣:

        給圖片應用上 background-size,它就會像是這樣:

        像上文一樣,我們稍微對這個圖形變形一下,然后疊加兩個圖層,給最上層的圖形,添加 CSS 樣式 mix-blend-mode: darken

        CodePen Demo -- radial-gradient & mix-blend-mode Demo

        借助 CSS-Doodle 隨機生成圖案

        再來一次,我們使用 CSS-Doodle,運用上述的規則在徑向漸變,也可以得到一系列有意思的背景圖。

        可以點進去嘗試一下,點擊鼠標即可隨機生成不同的效果:

        CodePen Demo -- CSS Doodle - CSS MIX-BLEND-MODE Background 2

        當然,上述的疊加都是非常簡單的圖案的疊加,但是掌握了這個原理之后,就可以自己嘗試,去創造更復雜的融合。

        上述的疊加效果是基于大片大片的實色的疊加,當然 mix-blend-mode 還能和真正的漸變碰撞出更多的火花。

        在不同的漸變背景中運用混合模式

        在不同的漸變背景中運用混合模式?那會產生什么樣美妙的效果呢?

        運用得當,它可能會像是這樣:

        umm,與上面的條紋圖案完全不一樣的風格。

        你可以戳進 gradienta.io 來看看,這里全是使用 CSS 創建的漸變疊加的背景圖案庫。

        使用混合模式疊加不同的漸變圖案

        下面,我們也來實現一個。

        首先,我們使用線性漸變或者徑向漸變,隨意創建幾個漸變圖案,如下所示:

        接著,我們兩兩之間,從第二層開始,使用一個混合模式進行疊加,一共需要設定 5 個混合模式,這里我使用了 overlay, multiply, difference, difference, overlay??纯疮B加之后的效果,非常的 Nice:

        CodePen Demo -- Graideint background mix

        由于上面動圖 GIF 的壓縮率非常高,所以看上去鋸齒很明顯圖像很模糊,你可以點進上面的鏈接看看。

        然后,我們可以再給疊加后的圖像再加上一個 filter: hue-rotate(),讓他動起來,放大一點點看看效果,絢麗奪目的光影效果:

        CodePen Demo -- Graideint background mix 2

        借助 CSS-Doodle 隨機生成圖案

        噔噔噔,沒錯,這里我們又可以繼續把 CSS-Doodle 搬出來了。

        隨機的漸變,隨機的混合模式,疊加在一起,燥起來吧。

        使用 CSS-Doodle 隨機創建不同的漸變,在隨機使用不同的混合模式,讓他們疊加在一起,看看效果:

        當然,由于是完全隨機生成的效果,所以部分時候生成出來的不算太好看或者直接是純色的。不過大部分還是挺不錯的

        CodePen Demo -- CSS Doodle Mix Gradient


        感謝堅持,看到這里。上述上半部分主要使用的混合模式,接下來,下半部分,將主要使用 mask,精彩繼續。


        使用 mask

        除去混合模式,與背景相關的,還有一個非常有意思的屬性 -- MASK。

        mask 譯為遮罩。在 CSS 中,mask 屬性允許使用者通過遮罩或者裁切特定區域的圖片的方式來隱藏一個元素的部分或者全部可見區域。

        對 mask 的一些基礎用法還不太熟悉的,可以先看看我的這篇文章 -- 奇妙的 CSS MASK。

        簡單而言,mask 可以讓圖片我們可以靈活的控制圖片,設定一部分展示出來,另外剩余部分的隱藏。

        使用 mask 對圖案進行切割

        舉個例子。假設我們使用 repeating-linear-gradient 漸變制作這樣一個漸變圖案:

        它的 CSS 代碼大概是這樣:

        :root {
            $colorMain: #673ab7;
        }
        {
            background: 
                repeating-linear-gradient(0, $colorSub 0, $colorSub 3px, transparent 3px, transparent 10px),
                repeating-linear-gradient(60deg, $colorSub 0, $colorSub 3px, transparent 3px, transparent 10px),
                repeating-linear-gradient(-60deg, $colorSub 0, $colorSub 3px, transparent 3px, transparent 10px);
        }

        如果我們給這個圖案,疊加一個這樣的 mask :

        {
            mask: conic-gradient(from -135deg, transparent 50%, #000);
        }

        上述 mask 如果是使用 background 表示的話,是這樣 background: conic-gradient(from -135deg, transparent 50%, #000), 圖案是這樣:

        兩者疊加在一起,按照 mask 的作用,背景與 mask 生成的漸變的 transparent 的重疊部分,將會變得透明。將會得到這樣一種效果:

        CodePen Demo -- mask & background Demo

        我們就完成了 background 與 mask 的結合。運用 mask 切割 background 的效果,我們就能制作出非常多有意思的背景圖案:

        CodePen Demo -- mask & background Demo

        mask-composite OR -webkit-mask-composite

        接下來,在運用 mask 切割圖片的同時,我們會再運用到 -webkit-mask-composite 屬性。這個是非常有意思的元素,非常類似于 mix-blend-mode / background-blend-mode。

        -webkit-mask-composite: 屬性指定了將應用于同一元素的多個蒙版圖像相互合成的方式。

        通俗點來說,他的作用就是,當一個元素存在多重 mask 時,我們就可以運用 -webkit-mask-composite 進行效果疊加。

        注意,這里的一個前提,就是當 mask 是多重 mask 的時候(類似于 background,mask 也是可以存著多重 mask),-webkit-mask-composite 才會生效。這也就元素的 mask 可以指定多個,逗號分隔。

        假設我們有這樣一張背景圖:

        :root {
            $colorMain: #673ab7;
            $colorSub: #00bcd4;
        }
        div {
            background: linear-gradient(-60deg, $colorMain, $colorSub);
        }

        我們的 mask 如下:

        {
            mask: 
                    repeating-linear-gradient(30deg, #000 0, #000 10px, transparent 10px, transparent 45px),
                    repeating-linear-gradient(60deg, #000 0, #000 10px, transparent 10px, transparent 45px),
                    repeating-linear-gradient(90deg, #000 0, #000 10px, transparent 10px, transparent 45px);
        }

        mask 表述成 background 的話大概是這樣:

        如果,不添加任何 -webkit-mask-composite,疊加融合之后的效果是這樣:

        如果添加一個 -webkit-mask-composite: xor,則會變成這樣:

        可以看到,線條的交匯疊加處,有了不一樣的效果。

        CodePen Demo -- background & -webkit-mask-composite

        借助 CSS-Doodle 隨機生成圖案

        了解了基本原理之后,上 CSS-Doodle,我們利用多重 mask 和 -webkit-mask-composite,便可以創造出各式各樣的美妙背景圖案:

        是不是很類似萬花筒?

        借助了 CSS-Doodle,我們只設定大致的規則,輔以隨機的參數,隨機的大小。接著就是一幅幅美妙的背景圖應運而生。

        下面是運用上述規則的嘗試的一些圖案:

        CodePen Demo -- CSS Doodle - CSS MASK Background

        當然,可以嘗試變換外形,譬如讓它長得像個手機殼。

        下面兩個 DEMO 也是綜合運用了上述的一些技巧的示例,仿佛一個個手機殼的圖案。

        CodePen Demo -- CSS Doodle - CSS MASK Background 2

        CodePen Demo -- CSS Doodle - CSS MASK Background 3

        總結一下

        背景 background 不僅僅只是純色、線性漸變、徑向漸變、角向漸變?;旌夏J?、濾鏡、遮罩也并不孤獨。

        background 配合混合模式 mix-blend-mode,background-blend-mode、濾鏡 filter、以及遮罩 mask 的時候,它們就可以組合變幻出各種不同的效果。

        到目前為止,CSS 已經越來越強大,它不僅僅可以用于寫業務,也可以創造很多有美感的事物,只要我們愿意去多加嘗試,便可以創造出美妙的圖案。

        最后

        好了,本文到此結束,看到這里,你是不是也躍躍欲試?想自己親手嘗試一下?

        想 Get 到最有意思的 CSS 資訊,千萬不要錯過我的公眾號 -- iCSS前端趣聞 ??

        更多精彩 CSS 技術文章匯總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。

        如果還有什么疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

        查看原文

        贊 23 收藏 15 評論 2

        chokcoco 發布了文章 · 2月1日

        你可能不知道的 transition 技巧與細節

        CSS 中,transition 屬性用于指定為一個或多個 CSS 屬性添加過渡效果。

        最為常見的用法,也就是給元素添加一個 transition,讓其某個屬性從狀態 A 變化到狀態 B 時,不再是非常直接突兀,而是帶有一個補間動畫。

        舉個例子:

        <div></div>
        div {
            width: 140px;
            height: 64px;
            transition: .8s transform linear;
        }
        div:hover {
            transform: translate(120px, 0);
        }

        當然,除了上述基本的用法,其實 CSS transition 還有一些非常有意思的細節和有趣的用法。下面讓我一一娓娓道來。

        并非所有屬性都支持 transition

        并非所有屬性都支持 transition。和 animation 類似,這里有一個列表,列出了所有支持 transition 的屬性 -- CSS animated properties

        當然,有的時候,還有更例外的。某些支持 transition 的屬性在某些特定狀態下,也是不支持 transition 的。非常典型的就是 height: autowidth: auto。

        CSS 奇技淫巧:動態高度過渡動畫 一文中,提到了這樣一個場景:

        元素的動態高度過渡動畫失效,偽代碼大概是這樣:

        {
            height: unset;
            transition: height 0.3s linear;
        
            &.up {
                height: 0;
            }
            &.down {
                height: unset;
            }
        }

        明明給 height 屬性設置了 transition,但是過渡動畫沒有觸發,而是直接一步到位展開:

        原因在于, CSS transtion 不支持元素的高度或者寬度為 auto 的變化。

        對于這種場景,我們可以使用 max-height 進行 hack。

        這里有一個非常有意思的小技巧。既然不支持 height: auto,那我們就另辟蹊徑,利用 max-height 的特性來做到動態高度的伸縮,譬如:

        {
            max-height: 0;
            transition: max-height 0.3s linear;
        
            &.up {
                max-height: 0;
            }
            &.down {
                max-height: 1000px;
            }
        }

        具體的詳情你可以看看 -- CSS 奇技淫巧:動態高度過渡動畫。

        transition 可以精細化控制每一個屬性

        繼續。在 transition 中,我們可以使用 transition: all 1s linear 這樣,統一給元素下面的所有支持過渡的屬性添加過渡效果(時間及緩動函數)。

        同時,我們也可以分別精細化控制每一個屬性:

        {
            // 可以這樣
            transition: all 1s linear;
        
            // 也可以這樣
            transition: height 1s linear, transform 0.5s ease-in, color 2s ease-in-out;
        }

        并且,和動畫類似,每一個過渡都是支持延遲觸發的:

        div {
            // 延遲 1s 觸發過渡,過渡動畫的時間為 0.8 秒
            transition: .8s transform 1s linear;
        }
        div:hover {
            transform: translate(120px, 0);
        }

        可以看到不管是過渡觸發,還是過渡復位,都會等待 1 秒再觸發。

        利用這個技巧,我們就可以實現一些過渡效果的結合。首先我們實現這樣一個寬高變化的過渡動畫:

        <div></div>
        div {
            position: relative;
            width: 200px;
            height: 64px;
            box-shadow: inset 0 0 0 3px #ddd;
        }
        div::before {
            content: "";
            position: absolute;
            width: 0;
            height: 0;
            top: 0; left: 0; width: 0; height: 0;
            box-sizing: border-box;
            transition: width .25s, height .25s, border-bottom-color;
            transition-delay: .25s, 0s, .25s;
        }
        div:hover::before {
            width: 200px;
            height: 64px;
            border-left: 3px solid #00e2ff;
            border-bottom: 3px solid #00e2ff;
        }

        我們分別控制元素的偽元素的高度、寬度、及下邊框的變化,并且給寬度過渡動畫和下邊框的顏色動畫設置了 0.25 秒的延遲,這樣元素的高度會先進行過渡,由于整體的過渡動畫世界時間也是 0.25s,所以高度過渡動畫結束后,才會開始寬度過渡動畫,下邊框也才會出現顏色變化。

        這樣就能把他們的過渡動畫銜接在一起,體現到元素的 border 之上,看看效果:

        利用同樣的原理,我們再把元素的另外一個偽元素也利用上,但是他們的動畫世界,整體需要再全部加上 0.5 秒,等到上述的過渡動畫執行完畢后才執行:

        div::after {
            right: 0;
            bottom: 0;
        }
        div:hover::after{
            transition: height .25s, width .25s, border-top-color .25s;
            transition-delay: 0.5s, 0.75s, 0.75s;
            width: 200px;
            height: 64px;
            border-top: 3px solid #00e2ff;
            border-right: 3px solid #00e2ff;
        }

        這樣,我們可以把兩個偽元素的過渡動畫合并,得到一個完整的 border 動畫如下:

        完整的 Demo 你可以戳這里:CodePen Demo -- 借助 transition-delay 實現按鈕 border 動畫效果

        所以,合理控制每一個屬性,就能組合得到各種有趣的效果。

        動態改變 transition-duration

        還有一個非常有意思的技巧,我們可以利用元素的一些偽類,動態的去改變元素的 transition-duration。

        舉個例子:

        div {
            width: 140px;
            height: 64px;
            border: 2px solid red;
            transition: 3s all linear;
        }
        div:hover {
            transition-duration: .5s; 
            border: 2px solid blue;
        }

        當鼠標 hover 元素時,將元素的過渡動畫的持續時間 transition-duration 從 3s 改成 0.5s,這樣可以做到元素 hover 的時候,過渡動畫持續的時間是 0.5s,但是過渡復位的持續時間卻是 3s:

        利用這個小技巧,我們嘗試制作一些有意思的效果。

        純 CSS 實現的簽名板

        利用上述的,小技巧,我們可以實現一個純 CSS 的簽名板。

        首先,在高寬都為 500px 的容器中,實現一個 100x100 的 HTML 網格布局,利用 flex、grid 都行,這里為了方便,我借助了 Pug 模板引擎。

        div.g-box
            -for(var i=0; i<100; i++)
                div.g-row
                    -for(var j=0; j<100; j++)
                        div.g-item

        為了方便示意,我把每個格子加了個 border,實際上,背景和格子都是白色的:

        為了實現簽名的效果,我們給每個格子 g-item 加上 hover 事件,hover 時改變當前格子背景色。同時,最重要的是,**在正常狀態設置一個非常大的 transition-duration,而在 hover 的時候,設置一個非常小的 transition-duration,偽代碼如下:

        .g-item {
            transition: 999999s;
        }
        .g-item:hover {
            background: #000;
            transition: 0s;
        }

        看看效果:

        這樣就實現了,鼠標 hover 上去的時候,因為 transition: 0s 的緣故,背景色被快速的改變,而當 hover 效果離開, transition: 999999s 重新生效,黑色則會以一個非常非常慢的速度失效,以至于慢到感受不到它在發生變化。

        當然,要實現簽名的話,目前來看還欠缺點什么,我們需要實現鼠標 hover 到畫板上不會立即開始出發元素的背景色變化,只有鼠標按下時(保持 :active 狀態),才開始遵循鼠標軌跡改變顏色。當鼠標停止點擊,則停止畫畫。

        這個有個巧妙的方法可以實現,我們在畫布上,再疊加一層 div,層級 z-index 比畫布更高,當鼠標 hover 到畫布上,其實是 hover 到這個遮罩層上,當鼠標按下,觸發 :active 事件時,給元素添加一個 :activce 事件,將遮罩層移除即可。

        偽代碼如下:

        div.g-wrap
        div.g-box
            -for(var i=0; i<100; i++)
                div.g-row
                    -for(var j=0; j<100; j++)
                        div.g-item
        .g-wrap {
            position: absolute;
            top: 0;
            left: 0;
            bottom: 0;
            right: 0;
            z-index: 10;
        
            &:active {
                display: none;
            }
        }

        這樣,一個完整的簽名板,或者說是畫板就實現了:

        完整的代碼實現,并且利用 CSS 添加上了 reset 功能,你可以戳這里:CodePen Demo -- Pure CSS signature

        利用這個技巧,其實就可以用 CSS 實現追隨鼠標軌跡的功能(雖然很雞肋>_<),我們再可以和其他很多屬性混合起來,譬如混合模式和濾鏡。

        像是這樣,來自好友 alphardex 的一個效果,利用了上述技巧,疊加了混合模式和濾鏡實現:

        CodePen Demo -- Snow Scratch

        最后

        本文到此結束,希望對你有幫助 :),想 Get 到最有意思的 CSS 資訊,千萬不要錯過我的公眾號 -- iCSS前端趣聞 ??

        更多精彩 CSS 技術文章匯總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。

        如果還有什么疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

        查看原文

        贊 21 收藏 11 評論 0

        chokcoco 發布了文章 · 1月27日

        CSS奇思妙想 -- 使用 CSS 創造藝術

        本文屬于 CSS 繪圖技巧其中一篇。之前有過一篇:在 CSS 中使用三角函數繪制曲線圖形及展示動畫

        想寫一篇關于 CSS 創造藝術的文章已久,本文主要介紹如何借助 CSS-doodle ,利用 CSS 快速創造美妙的 CSS 圖形。

        中心布局

        本文的所有技巧都會圍繞這個布局展開,屬于一類技巧。

        首先,我們需要這樣一種中心布局。簡單的 HTML 結構如下:

        <div class="g-container">
            <div class="g-box"></div>
            <div class="g-box"></div>
            <div class="g-box"></div>
            <div class="g-box"></div>
            <div class="g-box"></div>
        </div>
        .g-container {
            position: relative;
            width: 300px;
            height: 300px;
        }
        .g-box {
            position: absolute;
            top:50%;
            left: 50%;
            margin-left: -150px;
            margin-top: -150px;
            width: 100%;
            height: 100%;
            border: 1px solid #000;
        }

        利用絕對定位和 margin,將元素全部水平垂直居中疊在一起(因為后面會用到 transform,所以選取了這種水平垂直居中的方式),結果如下:

        好吧,看著平平無奇,但是基于這種布局,我們可以衍生出非常多有意思的圖案。

        改變元素大小

        最簡單的,就是我們可以改變元素的大小。

        CSS 代碼寫著太累,所以我們簡單的借助 pug HTML 模板引擎和 SASS。

        div.g-container
            -for(var i=0; i<10; i++)
                div.g-box  
        $count: 10;
        @for $i from 1 to $count + 1 {
            .g-box:nth-child(#{$i}) {
                --width: #{$i * 30}px;
                width: var(--width);
                height: var(--width);
                margin-left: calc(var(--width) / -2);
                margin-top: calc(var(--width) / -2);
            }
        }

        容器下包含 10 個子元素,每個子元素大小逐漸遞增,很容易得到如下結果:

        改變元素顏色

        接著,我們繼續改變元素的顏色,讓它呈現漸變顏色逐級遞進,可以是邊框顏色

        @for $i from 1 to $count + 1 {
            .g-box:nth-child(#{$i}) {
                 ...
                 border-color: hsla(
                    calc(#{$i * 25}),
                    50%,
                    65%,
                    1
                );
            }
        }

        得到這樣的效果:

        也可以是改變背景 background 的顏色:

        @for $i from 1 to $count + 1{
            .g-box:nth-child(#{$i}) {
                ...
                background: hsla(
                    calc(#{$i * 25}),
                    50%,
                    65%,
                    1
                );
                z-index: #{$count - $i};
            }
        }

        改變元素角度

        好,接下來,就可以開始變換角度了,我們利用 transform,將元素旋轉不同的角度:

        @for $i from 1 to $count + 1{
            .g-box:nth-child(#{$i}) {
                ....
                transform: rotate(#{$i * 7}deg);
            }
        }

        效果如下:

        CodePen Demo -- CSS Pattern

        OK,到這里,基本的一些概念就引入的差不多了,總而言之,利用多元素居中布局,改變元素的大小、顏色、透明度、角度、陰影、濾鏡、混合模式等等等等,只要你能想到的,都可以。

        接下來,我們再引入本文的另外一個主角 -- CSS-doodle 。

        CSS-doodle 是一個基于 Web-Component 的庫。允許我們快速的創建基于 CSS Grid 布局的頁面,以實現各種 CSS 效果(或許可以稱之為 CSS 藝術)。

        其最終效果的代碼,本質是都還是 CSS。具體的一些概念可以點擊主頁看看,一看就懂。

        使用 CSS-doole 實現多元素水平垂直居中布局

        我們將上面的布局利用 CSS-doodle 再實現一次,要實現 50個元素的居中對齊,只需要如下簡單的聲明即可:

        <css-doodle>
            :doodle {
                @grid: 1x50 / 100vmin;
            }
            @place-cell: center;
        </css-doodle>

        上面的意思大概是,在 100vmin x 100vmin 大小的容器下,聲明一個 1x50 的 grid 網格布局,利用 @place-cell: center 將它們全部水平垂直居中,也就是會疊加在一起。

        這樣可能看不出效果,我們再給每個元素設置不同的大小,給它們都加上一個簡單的 border

        <css-doodle>
            :doodle {
                @grid: 1x50 / 100vmin;
            }
            @place-cell: center;
            @size: calc(100% - @calc(@index() - 1) * 2%);
            border: 1px solid #000;
        </css-doodle>
        • @size: calc(100% - @calc(@index() - 1) * 2%) 表示每個子元素寬高的大?。ㄒ部梢詥为氃O置高寬),@index 是個變量,表示當前元素的序號,從 1 - 50,表示沒個元素分別為容器的 2% 高寬、4% 高寬一直到 100% 高寬。
        • border: 1px solid #000 就是正常的 CSS 代碼,里面沒有變量,作用于每一個元素

        效果如下:

        Oh No,眼睛開始花了。這樣,我們就快速的實現了前面鋪墊時候利用 HTML 代碼和繁瑣的 CSS 生成的圖形效果。

        CSS 藝術

        接下來,就開始美妙的 CSS 藝術。

        改變元素的旋轉角度及邊框顏色

        我們利用上述代碼繼續往下,為了更好的展示效果,首先整體容器的底色改為黑色,接著改變元素的旋轉角度。每個元素旋轉 30deg x @index。

        代碼非常的短,大概是這樣:

        <css-doodle>
            :doodle {
                @grid: 1x100 / 100vmin;
            }
            @place-cell: center;
            @size: calc(100% - @calc(@index() - 1) * 1%);
            transform: rotate(calc(@index() * 30deg));
            border: 1px solid #fff;
        </css-doodle>
        

        不太好看,接著,我們試著給每個元素,漸進的設置不同的 border 顏色,并且透明度 opacity 逐漸降低,,這里我們會用到 hsla 顏色表示法:

        <css-doodle>
            :doodle {
                @grid: 1x100 / 100vmin;
            }
            @place-cell: center;
            @size: calc(100% - @calc(@index() - 1) * 1%);
            transform: rotate(calc(@index() * 30deg));
            border: 1px solid hsla(
                calc(calc(100 - @index()) * 2.55), 
                calc(@index() * 1%), 
                50%,
                calc(@index() / 100)
            );
        </css-doodle>

        再看看效果:

        所有貼圖都存在一定色差,可以點進 Demo 里看看~

        Wow,第一幅看上去還不錯的作品出現了。

        當然,每一個不同的角度,都能產生不一樣的效果,通過 CSS-doodle,可以快速生成不同隨機值,隨機產生不同的效果。我們稍微改變一下上述代碼,將 transform 那一行改一下,引入了一個隨機值:

        <css-doodle>
            :doodle {
                --rotate: @r(0, 360)deg;
            }
            transform: rotate(calc(@index() * var(--rotate)));
        </css-doodle>
        • 利用 @r(0, 360)deg,能隨機生成一個介于 0 到 360 之間的隨機數,后面可以直接跟上單位,也就變成了一個隨機角度值
        • transform: rotate(calc(@index() * var(--rotate))),利用 calc 規則引入隨機生成的 CSS 變量,當然,再不刷新頁面的前提下,每一次這個值都是固定的

        這樣,我們每次刷新頁面,就可以得到不同的效果了(當然,CSS-doodle 做了優化,添加短短幾行代碼就可以通過點擊頁面刷新效果),改造后的效果,我們每次點擊都可以得到一個新的效果:

        CodePen Demo -- CSS Doodle - CSS Magic Pattern

        強烈建議你點進 Demo,自己點點鼠標感受一下 :)

        background 顏色奇偶不同

        好,我們再換個思路,這次不改變 border 的顏色,而是通過選擇器控制奇數序號的元素和偶數序號的元素,分別給予它們不一樣的背景色:

        <css-doodle>
            :doodle {
                @grid: 1x100 / 100vmin;
            }
            @place-cell: center;
            @size: calc(100% - @calc(@index() - 1) * 1%);
            transform: rotate(calc(@index() * 60deg));
        
            background: rgba(0, 0, 0, calc((@index * 0.01)));
            @even {
                background: rgba(255, 255, 255, calc((@index * 0.01)));
            }
        </css-doodle>

        利用 @even {} 可以快速選中偶數序號的元素,然后給他賦予白色底色,而奇數元素則賦予黑色底色,看看效果:

        還是一樣的思路,我們可以將隨機值賦予 transform 的旋轉角度,利用黑白疊加,看看再不同角度下,都會有什么效果:

        CodePen Demo -- CSS Doodle - CSS Magic Pattern

        當然,在隨機的過程中,你也可以選取自己喜歡的,將它們保留下來。

        CSS-doodle 支持多種方式的引入,在一頁中展示多個圖形,不在話下,像是這樣:

        CodePen Demo -- CSS-doodle Pure CSS Pattern

        規律總結

        小小總結一下,想要生成不同的圖案,其實只需要找到能夠生成不同線條,或者造型圖案圖形,將它們按照不同的大小,不同的旋轉角度,不同顏色及透明度疊加在一起即可。

        這樣的話,一些可能的 idea:

        • 只利用單向的 border 會是怎么樣的呢?
        • 出現的 border 都是 solid,如果換成是虛線 dashed 呢?或許可以再加上 border-radius
        • text-decoration 也支持一些各式的下劃線,我們也可以利用它們試試

        OK,將上述想法付諸實踐,我們就可以得到利用各式線條繪制出來的各式圖形。它們可能是這樣:

        當然,每次的效果都可以做到隨機,只要我們合理利用好隨機的參數即可,你可以戳進下面的 Demo 感受一下:

        CodePen Demo -- CSS-doodle Pure CSS Pattern

        Clip-pathdrop-shadow

        嘿,說到創造不同的線條與圖案,就不得不提 CSS 里另外兩個有意思是屬性。Clip-pathfitler: drop-shadow()。

        嗯哼?什么意思呢。我們來個簡單的 Demo,利用 Clip-path ,我們可以裁剪出不同的元素造型。譬如實現一個簡單的多邊形:

        div {
            width: 300px;
            height: 300px;
            clip-path: polygon(50% 0%, 90% 20%, 100% 60%, 75% 100%, 25% 100%, 0% 60%, 10% 20%);
            background: #333;
        }

        效果如下:

        那么利用這個思路,我們就可以嘗試利用 clip-path 裁剪出各種不同的造型進行疊加。

        CSS-doodle Shapes 中,內置了非常多的 clip-path 圖形供我們選擇:

        我們隨機選取一個:

        套用上述的規則,嘗試著實現一個圖形:

        <css-doodle>
            :doodle {
                @grid: 1x100 / 100vmin;
            }
            @place-cell: center;
            @size: calc(100% - @calc(@index() - 1) * 1%);
            background: hsla(
                calc(calc(100 - @index()) * 2.55), 
                calc(@index() * 1%), 
                65%,
                calc(@index() / 100)
            );
            clip-path: @shape(
                fill-rule: evenodd;
                split: 200;
                scale: .45;
                x: cos(2t) + cos(π - 5t);
                y: sin(2t) + sin(π - 5t);
            );
        </css-doodle>

        這次沒有旋轉不同的角度,只是給每一層賦予不同的背景底色,能夠得到這樣的效果:

        CodePen Demo -- CSS Doodle - CSS Magic Pattern

        Clip-pathdrop-shadow 創造不同線條

        OK,上述是利用 Clip-path 創造了不同的圖案,那不同的線條怎么得來呢?

        別急。這就需要請出我們另外一個屬性 drop-shadow,利用 drop-shadow,可以給 Clip-path 裁剪出來的圖形創造不同的陰影,當然有一些結構上的限制,大概的偽代碼如下:

        div {
            position: relative;
            width: 300px;
            height: 300px;
            filter: drop-shadow(0px 0px 1px black);
        
            &::after {
                content: "";
                position: absolute;
                width: 100%;
                height: 100%;
                left: 0;
                right: 0;
                background: #fff;
                clip-path: polygon(50% 0%, 90% 20%, 100% 60%, 75% 100%, 25% 100%, 0% 60%, 10% 20%);
            }
        }

        我們需要將 filter: drop-shadow(0px 0px 2px black) 作用在利用了 clip-path 的元素的父元素之上,并且,利用了 clip-path: 的元素必須帶有 background,才能給裁剪元素附上陰影效果。

        上述的代碼如下:

        OK,完美,這樣一來,我們就極大極大的豐富了我們的線條庫,再運用會上述的線條規則,一大波新的圖案應運而生。

        CodePen Demo -- CSS-doodle Pure CSS Pattern - clip-path - drop-shadow

        OK,限于篇幅,就不一一展開了,感興趣可以點進上述 Demo Fork 一份自己嘗試。還有非常多有意思的圖案等待挖掘生成。

        最后,再來欣賞一下 CSS-doodle 作者,袁川袁老師利用上述技巧的作品:

        CodePen Demo -- css doodle art

        最后

        本文到此結束,希望對你有幫助 :),想 Get 到最有意思的 CSS 資訊,千萬不要錯過我的公眾號 -- iCSS前端趣聞 ??

        更多精彩 CSS 技術文章匯總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。

        如果還有什么疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

        查看原文

        贊 13 收藏 7 評論 1

        chokcoco 發布了文章 · 1月25日

        生僻標簽 fieldset 與 legend 的妙用

        談到 <fieldset><legend>,大部分人肯定會比較陌生,在 HTML 標簽中,屬于比較少用的那一批。

        我最早知道這兩個標簽,是在早年學習 reset.css 或者 normalize.css 時,在這些重置統一代碼默認樣式的 CSS 中看到的。最近因為研究邊框,遇到了這兩個標簽,發現它們還是很有意思的,遂起一篇,將整理的一些知識點分享給大家。

        了解 <fieldset><legend>

        通常而言,<fieldset><legend> 比較常用在表單當中。

        • <fieldset>:HTML <fieldset> 元素用于對表單中的控制元素進行分組
        • <legend>:在 <fieldset> 中內置了一個 <legend> 元素作為 fieldset 的標題

        簡單而言,fieldset 可以單獨使用,對表單的元素進行分組,而 legend 則需要和 fieldset 配套使用,成對出現,作為分組的標題。

        看個簡單的例子,簡單的 HTML 及結構如下:

        <fieldset>
            <legend>Form</legend>
            <div>
                <label>Name :</label><input type="text" placeholder="input Name" />
            </div>
            <div>
                <label>Password :</label><input type="text" placeholder="input Name" />
            </div>
        </fieldset>
        fieldset {
            border: 1px solid#ddd;
            padding: 12px;
        }
        
        legend {
            font-size: 18px;
            padding: 0 10px;
        }

        效果如下:

        CodePen Demo -- fieldset & legend Demo

        比較有意思的點在于,如果給 fieldset 設置了 border 邊框,則 legend 元素內的內容則會作為分組的標題,嵌入 border 內。

        控制 legend 的位置及樣式

        對于 legend 的位置和樣式,是可以進行控制的。

        對于位置,我們可以通過 margin 和 父元素的 padding 進行控制,如果父元素 fieldset 不設置 padding ,legend 不設置 margin ,則 legend 默認定位在最左側。

        fieldset {
            border: 1px solid#ddd;
            // padding: 12px;
        }
        
        legend {
            font-size: 18px;
        }

        效果圖:

        通過改變 legendmargin 或者父元素的 padding-left ,可以控制標題的初始位置:

        fieldset {
            border: 1px groove #ddd;
        }
        
        legend {
            animation: marginMove 10s infinite alternate;
        }
        
        @keyframes marginMove {
            100% {
                margin-left: 100px;
            }
        }

        效果圖:

        通過控制 legendpadding,可以增加周圍元素的區域,讓留白更多一點。

        應用場景 -- 標題兩側橫線

        了解了上述基本知識后,我們就可以稍微開始深入,想一想,上述 <fieldset><legend> 的一些有意思的應用場景。

        最適合的場景我覺得應該就是標題兩側帶橫線的布局了。類似這樣:

        當然,這個布局的解決方式有很多,通常會使用偽元素來生成左右兩側的橫線,或者是通過絕對定位局部進行覆蓋疊加。

        這里,使用 <fieldset><legend> 非??焖俚耐瓿桑?/p>

        <div class="g-container">
            <fieldset><legend>排行榜</legend></fieldset>
        </div>
        fieldset {
            width: 300px; 
            height: 24px; 
            border: 1px solid transparent; 
            border-top-color: #000; 
        }
        
        legend{
            margin: auto; 
            padding: 0 10px; 
        } 

        fieldset 只設置上邊框,通過 margin: auto 將標題定位到中間, 通過 padding 控制兩側的留白。非常的完美。

        CodePen Demo -- fieldset & legend Demo 2

        邊框嵌套文字

        在這篇文章中 -- How to Add Text in Borders Using Basic HTML Elements,還介紹了一種非常有意思的使用場景,在邊框中嵌套文字。

        想象一下,一個 <fieldset> 元素配合一個 <legend> 元素,可以生成一個邊框嵌文字的效果,那么,將多組組合,再進行定位排布,就可以生成多邊邊框嵌套文字的效果了。

        偽代碼如下:

        <div class="g-container">
            <fieldset><legend>CSS fieldset</legend></fieldset>
            <fieldset><legend>HTML element</legend></fieldset>
            <fieldset><legend>JavaScript</legend></fieldset>
            <fieldset><legend>TypeScript</legend></fieldset>
        </div>
        .g-container {
            position: relative;
            width: 300px; 
            height: 300px; 
        }
        fieldset{
            position: absolute;
            width: 300px; 
            height: 300px; 
            border: 10px solid transparent; 
            border-top-color: #000; 
        }
        legend{
            padding: 0 10px; 
        } 
        
        fieldset:nth-of-type(2){ transform: rotate(90deg); }
        fieldset:nth-of-type(3){ transform: rotate(180deg); }
        fieldset:nth-of-type(3)>legend{ transform: rotate(180deg); } 
        fieldset:nth-of-type(4){ transform: rotate(-90deg); }

        效果圖如下:

        通過多個 <fieldset><legend> 的組合,我們可以拼出一個容器的 4 個邊,組成一個非常好看的邊框嵌文字效果。

        通過給 legend 加上 animation,讓文字動起來

        legend{
            animation: move 3s infinite linear alternate;
        } 
        @keyframes move {
            100% {
                margin-left: 70px;
            }
        }

        CodePen Demo -- Border Text Design using HTML fieldset and legend

        好,基于這個,我們就可以去生成各種 N 邊形邊框嵌文字的邊框了。下面是簡單的嘗試 幾種多邊形邊框。

        CodePen Demo -- fieldset and legend generate polygon

        最后

        本文到此結束,希望對你有幫助 :),想 Get 到最有意思的 CSS 資訊,千萬不要錯過我的公眾號 -- iCSS前端趣聞 ??

        更多精彩 CSS 技術文章匯總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。

        如果還有什么疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

        查看原文

        贊 12 收藏 7 評論 0

        chokcoco 發布了文章 · 1月18日

        CSS 奇思妙想邊框動畫

        今天逛博客網站 -- shoptalkshow,看到這樣一個界面,非常有意思:

        覺得它的風格很獨特,尤其是其中一些邊框。

        嘿嘿,所以來一篇邊框特輯,看看運用 CSS,可以在邊框上整些什么花樣。

        border 屬性

        談到邊框,首先會想到 border,我們最常用的莫過于 solid,dashed,上圖中便出現了 dashed。

        除了最常見的 solid,dashed,CSS border 還支持 none,hidden, dotted, double, groove, ridge, inset, outset 等樣式。除去 none,hidden,看看所有原生支持的 border 的樣式:

        基礎的就這些,如果希望實現一個其他樣式的邊框,或者給邊框加上動畫,那就需要配合一些其他屬性,或是腦洞大開。OK,一起來看看一些額外的有意思的邊框。

        邊框長度變化

        先來個比較簡單的,實現一個類似這樣的邊框效果:

        這里其實是借用了元素的兩個偽元素。兩個偽元素分別只設置上、左邊框,下、右邊框,通過 hover 時改變兩個偽元素的高寬即可。非常好理解。

        div {
            position: relative;
            border: 1px solid #03A9F3;
            
            &::before,
            &::after {
                content: "";
                position: absolute;
                width: 20px;
                height: 20px;
            }
            
            &::before {
                top: -5px;
                left: -5px;
                border-top: 1px solid var(--borderColor);
                border-left: 1px solid var(--borderColor);
            }
            
            &::after {
                right: -5px;
                bottom: -5px;
                border-bottom: 1px solid var(--borderColor);
                border-right: 1px solid var(--borderColor);
            }
            
            &:hover::before,
            &:hover::after {
                width: calc(100% + 9px);
                height: calc(100% + 9px);
            }
        }

        CodePen Demo -- width border animation

        接下來,會開始加深一些難度。

        虛線邊框動畫

        使用 dashed 關鍵字,可以方便的創建虛線邊框。

        
        div {
            border: 1px dashed #333;
        }

        當然,我們的目的是讓邊框能夠動起來。使用 dashed 關鍵字是沒有辦法的。但是實現虛線的方式在 CSS 中有很多種,譬如漸變就是一種很好的方式:

        div {
            background: linear-gradient(90deg, #333 50%, transparent 0) repeat-x;
            background-size: 4px 1px;
            background-position: 0 0;
        }

        看看,使用漸變模擬的虛線如下:

        好,漸變支持多重漸變,我們把容器的 4 個邊都用漸變表示即可:

        div {
            background: 
                linear-gradient(90deg, #333 50%, transparent 0) repeat-x,
                linear-gradient(90deg, #333 50%, transparent 0) repeat-x,
                linear-gradient(0deg, #333 50%, transparent 0) repeat-y,
                linear-gradient(0deg, #333 50%, transparent 0) repeat-y;
            background-size: 4px 1px, 4px 1px, 1px 4px, 1px 4px;
            background-position: 0 0, 0 100%, 0 0, 100% 0;
        }

        效果如下:

        OK,至此,我們的虛線邊框動畫其實算是完成了一大半了。雖然 border-style: dashed 不支持動畫,但是漸變支持呀。我們給上述 div 再加上一個 hover 效果,hover 的時候新增一個 animation 動畫,改變元素的 background-position 即可。

        div:hover {
            animation: linearGradientMove .3s infinite linear;
        }
        
        @keyframes linearGradientMove {
            100% {
                background-position: 4px 0, -4px 100%, 0 -4px, 100% 4px;
            }
        }

        OK,看看效果,hover 上去的時候,邊框就能動起來,因為整個動畫是首尾相連的,無限循環的動畫看起來就像是虛線邊框在一直運動,這算是一個小小的障眼法或者小技巧:

        這里還有另外一個小技巧,如果我們希望虛線邊框動畫是從其他邊框,過渡到虛線邊框,再行進動畫。完全由漸變來模擬也是可以的,如果想節省一些代碼,使用 border 會更快一些,譬如這樣:

        div {
            border: 1px solid #333;
            
            &:hover {
                border: none;
                background: 
                    linear-gradient(90deg, #333 50%, transparent 0) repeat-x,
                    linear-gradient(90deg, #333 50%, transparent 0) repeat-x,
                    linear-gradient(0deg, #333 50%, transparent 0) repeat-y,
                    linear-gradient(0deg, #333 50%, transparent 0) repeat-y;
                background-size: 4px 1px, 4px 1px, 1px 4px, 1px 4px;
                background-position: 0 0, 0 100%, 0 0, 100% 0;
            }
        }

        由于 border 和 background 在盒子模型上位置的差異,視覺上會有一個很明顯的錯位的感覺:

        要想解決這個問題,我們可以把 border 替換成 outline,因為 outline 可以設置 outline-offset。便能完美解決這個問題:

        div {
            outline: 1px solid #333;
            outline-offset: -1px;
            
            &:hover {
                outline: none;
            }
        }
        

        最后看看運用到實際按鈕上的效果:

        上述 Demo 完整代碼如下:

        CodePen Demo -- dashed border animation

        其實由于背景和邊框的特殊關系,使用 border 的時候,通過修改 background-position 也是可以解決的,就是比較繞。關于背景和邊框的填充關系,可以看這篇文章:條紋邊框的多種實現方式

        漸變的其他妙用

        利用漸變,不僅僅只是能完成上述的效果。

        我們繼續深挖漸變,利用漸變實現這樣一個背景:

        div {
            position: relative;
        
            &::after {
                content: '';
                position: absolute;
                left: -50%;
                top: -50%;
                width: 200%;
                height: 200%;
                background-repeat: no-repeat;
                background-size: 50% 50%, 50% 50%;
                background-position: 0 0, 100% 0, 100% 100%, 0 100%;
                background-image: linear-gradient(#399953, #399953), linear-gradient(#fbb300, #fbb300), linear-gradient(#d53e33, #d53e33), linear-gradient(#377af5, #377af5);
            }
        }

        注意,這里運用了元素的偽元素生成的這個圖形,并且,寬高都是父元素的 200%,超出則 overflow: hidden。

        接下來,給它加上旋轉:

        div {
            animation: rotate 4s linear infinite;
        }
        
        @keyframes rotate {
            100% {
                transform: rotate(1turn);
            }
        }

        看看效果:

        最后,再利用一個偽元素,將中間遮罩起來,一個 Nice 的邊框動畫就出來了 (動畫會出現半透明元素,方便示意看懂原理):

        上述 Demo 完整代碼如下,這個效果我最早見于這位作者 -- Jesse B

        CodePen Demo -- gradient border animation

        改變漸變的顏色

        掌握了上述的基本技巧之后,我們可以再對漸變的顏色做一些調整,我們將 4 種顏色變成 1 種顏色:

        div::after {
            content: '';
            position: absolute;
            left: -50%;
            top: -50%;
            width: 200%;
            height: 200%;
            background-color: #fff;
            background-repeat: no-repeat;
            background-size: 50% 50%;
            background-position: 0 0;
            background-image: linear-gradient(#399953, #399953);
        }

        得到這樣一個圖形:

        同樣的,讓它旋轉一起,一個單色追逐的邊框動畫就出來了:

        CodePen Demo -- gradient border animation 2

        Wow,很不錯的樣子。不過如果是單線條,有個很明顯的缺陷,就是邊框的末尾是一個小三角而不是垂直的,可能有些場景不適用或者 PM 接受不了。

        那有沒有什么辦法能夠消除掉這些小三角呢?有的,在下文中我們再介紹一種方法,利用 clip-path ,消除掉這些小三角。

        conic-gradient 的妙用

        再介紹 clip-path 之前,先講講角向漸變。

        上述主要用到了的是線性漸變 linear-gradient 。我們使用角向漸變 conic-gradient 其實完全也可以實現一模一樣的效果。

        我們試著使用 conic-gradient 也實現一次,這次換一種暗黑風格。核心代碼如下:

        .conic {
            position: relative;
            
            &::before {
                content: '';
                position: absolute;
                left: -50%;
                top: -50%;
                width: 200%;
                height: 200%;
                background: conic-gradient(transparent, rgba(168, 239, 255, 1), transparent 30%);
                animation: rotate 4s linear infinite;
            }
        }
        @keyframes rotate {
            100% {
                transform: rotate(1turn);
            }
        }

        效果圖和示意圖如下,旋轉一個部分角向漸變的圖形,中間的部分使用另外一個偽元素進行遮罩,只漏出線條部分即可:

        CodePen Demo -- Rotating border 3

        clip-path 的妙用

        又是老朋友 clip-path,有意思的事情它總不會缺席。

        clip-path 本身是可以進行坐標點的動畫的,從一個裁剪形狀變換到另外一個裁剪形狀。

        利用這個特點,我們可以巧妙的實現這樣一種 border 跟隨效果。偽代碼如下:

        div {
            position: relative;
        
            &::before {
                content: "";
                position: absolute;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                border: 2px solid gold;
                animation: clippath 3s infinite linear;
            }
        }
        
        @keyframes clippath {
            0%,
            100% {
                clip-path: inset(0 0 95% 0);
            }
            25% {
                clip-path: inset(0 95% 0 0);
            }
            50% {
                clip-path: inset(95% 0 0 0);
            }
            75% {
                clip-path: inset(0 0 0 95%);
            }
        }

        效果圖與示意圖一起:

        CodePen - clip-path border animation

        這里,因為會裁剪元素,借用偽元素作為背景進行裁剪并動畫即可,使用 clip-path 的優點了,切割出來的邊框不會產生小三角。同時,這種方法,也是支持圓角 border-radius 的。

        如果我們把另外一個偽元素也用上,實際實現一個按鈕樣式,可以得到這樣的效果:

        CodePen - clip-path border animation 2

        overflow 的妙用

        下面這個技巧利用 overflow 實現。實現這樣一個邊框動畫:

        為何說是利用 overflow 實現?

        貼個示意圖:

        CodePen Demo -- 巧用overflow及transform實現線條hover效果

        兩個核心點:

        1. 我們利用 overflow: hidden,把原本在容器外的一整個元素隱藏了起來
        2. 利用了 transform-origin,控制了元素的旋轉中心

        發現沒,其實幾乎大部分的有意思的 CSS 效果,都是運用了類似技巧:

        簡單的說就是,我們看到的動畫只是原本現象的一小部分,通過特定的裁剪、透明度的變化、遮罩等,讓我們最后只看到了原本現象的一部分。

        border-image 的妙用

        利用 border-image,我們也可以實現一些有意思的邊框動畫。關于 border-image,有一篇非常好的講解文章 -- border-image 的正確用法,本文不對基本定義做過多的講解。

        如果我們有這樣一張圖:

        便可以利用 border-image-sliceborder-image-repeat 的特性,得到類似的邊框圖案:

        div {
          width: 200px;
          height: 120px;
          border: 24px solid;
          border-image: url(image-url);
          border-image-slice: 32;
          border-image-repeat: round;
        }

        在這個基礎上,可以隨便改變元素的高寬,如此便能擴展到任意大小的容器邊框中:

        CodePen Demo -- border-image Demo

        接著,在這篇文章 -- How to Animate a SVG with border-image 中,還講解了一種利用 border-image 的邊框動畫,非常的酷炫。

        與上面例子不一樣的是,我們只需要讓我們的圖案,動起來,就是我們需要這樣一個背景圖(掘金不支持 SVG 動圖,原圖地址):

        那么,我們也就能得到運動的邊框圖,代碼完全一樣,但是,邊框是運動的:

        CodePen Demo -- Dancing Skull Border

        border-image 使用漸變

        border-image 除了貼圖引用 url 之外,也是可以直接填充顏色或者是漸變的。

        之前也有一篇關于 border-image 的文章 -- 巧妙實現帶圓角的漸變邊框

        我們可以利用 border-image + filter + clip-path 實現漸變變換的圓角邊框:

        .border-image-clip-path {
            width: 200px;
            height: 100px;
            border: 10px solid;
            border-image: linear-gradient(45deg, gold, deeppink) 1;
            clip-path: inset(0px round 10px);
            animation: huerotate 6s infinite linear;
            filter: hue-rotate(360deg);
        }
        
        @keyframes huerotate {
            0% {
                filter: hue-rotate(0deg);
            }
            100% {
                filter: hue-rotate(360deg);
            }
        }

        CodePen Demo -- clip-path、border-image 加 filter 實現圓角漸變邊框

        最后

        本文介紹了一些我認為比較有意思的邊框動畫小技巧,當然 CSS 產生還有非常多有意思的效果,限于篇幅,不一一展開。

        本文到此結束,希望對你有幫助 :),想 Get 到最有意思的 CSS 資訊,千萬不要錯過我的 iCSS 公眾號 -- iCSS前端趣聞 ??

        更多精彩 CSS 技術文章匯總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。

        如果還有什么疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

        查看原文

        贊 28 收藏 24 評論 0

        chokcoco 發布了文章 · 1月13日

        如何不使用 overflow: hidden 實現 overflow: hidden?

        一個很有意思的題目。如何不使用 overflow: hidden 實現 overflow: hidden?

        CSS 中 overflow 定義當一個元素的內容太大而無法適應塊級格式化上下文時候該做什么。而 overflow: hidden 則會將超出容器范圍內的內容剪裁。

        控制 overflow: hidden 的方向

        這源自一個實際的需求,在某個需求當中,要求容器內的內容,在豎直方向上超出容器會被裁剪,而在水平方向上超出容器,則不會被裁剪。類似這樣:

        1 上午10.26.03.gif

        有意思,第一個想到的解法當然是在上述黃色背景元素本身之外再套用一層父元素,然后父元素才是實際設置 overflow: hidden 的元素,父元素的范圍就是實際才是控制是否裁剪的范圍。類似這樣:

        2 上午10.26.03.gif

        實際的父元素才是設置了 overflow: hidden 的元素。

        當然,如果實際情況就是這么簡單,也沒什么問題。

        但是如果元素處于一個復雜的布局流內,那么可能就沒有那么多的空間,讓我們再去包裹一層父容器了:

        3 上午10.26.03.png

        類似上圖的情況,還是中間黃色元素,要求只有豎直方向超出裁剪。此時,包裹父元素不再那么容易施展。所以,我們需要另辟蹊徑。

        利用 clip-path 進行裁剪

        好的,這會可以進入正文了。CSS 中,除了 overflow: hidden,還是有其它屬性也可以實現超出容器區域進行裁剪的。clip-path 便是其中翹楚。

        使用 clip-path,我們可以方便的控制任意方向上的裁剪。上述的需求則可以這樣解決:

        <div class="g-container">
            <div class="sub"></div>
        </div>

        關鍵的 CSS 代碼如下:

        .g-container {
            width: 200px;
            height: 200px;
            clip-path: polygon(-1000% 0, 1000% 0, 1000% 100%, -1000% 100%);
        }

        這里利用了 clip-path: polygon() 來裁剪一個矩形區域,而利用了 clip-path 支持負坐標的特點,將裁剪的起點定到遠離坐標能畫成一個大矩形的形狀。示意圖:

        4 上午10.26.03.png

        這樣,我們能夠在正常布局流中,當前容器大小范圍內,畫出任意希望 overflow: hidden 的范圍。

        你可以點進 Demo 里面嘗試下:

        CodePen -- Clip-path overflow

        再舉兩個例子:

        {
            // 裁剪出左右兩邊都 overflow:hidden,上下不 overflow:hidden 的區域
            clip-path: polygon(0 -1000% ,100% -1000%, 100% 1100%,0 1100%);
        
           // 裁剪出左邊、上邊、右邊都 overflow:hidden,下邊不 overflow: hidden 的區域
            clip-path: polygon(100% 0,100% 1000%, 0 1000%, 0 0);
        }

        當然,上述代碼中的 1000% 是非常靈活的,自己控制,夠用就行。

        非 overflow、clip-path 的裁剪方式

        那么。通過上面的一個小例子,我們知道了 overflow,clip-path 可以裁剪區域。那么除了這兩個,CSS 中還有沒有可以進行區域裁剪的元素呢?

        有,還有一個有意思的元素,就是 -- contain 。

        contain 屬性允許我們指定特定的 DOM 元素和它的子元素,讓它們能夠獨立于整個 DOM 樹結構之外。目的是能夠讓瀏覽器有能力只對部分元素進行重繪、重排,而不必每次都針對整個頁面。

        這里不具體去介紹它的每個屬性,感興趣的可以翻看下這篇文章 -- CSS新特性contain,控制頁面的重繪與重排

        contain: paint 進行內容裁剪

        詳細說說 contain: paint,設定了 contain: paint 的元素即是開啟了布局限制,也就是說,此元素的子元素不會在此元素的邊界之外被展示。

        contain: paint 屬性產生的目的,即是為加快頁面的渲染,在非必要區域,不渲染元素。因此,如果元素不在屏幕上或以其他方式設定為不可見,則其后代不可見不被渲染。
        .g-container {
            contain: paint;
        }

        看看示例:

        5 上午10.26.03.gif

        CodePen Demo -- contain: paint Demo

        contain: paint 的副作用

        contain: paint 的本意是用于提升頁面的渲染,裁剪到容器之外的元素不進行渲染。但是使用它會產生一些副作用:

        1. 它會生成一個自己的新的堆疊上下文(It becomes a stacking context),也就是說,它會改變它的子元素的 absolute 定位和 fixed 定位的基準;
        2. 它會成為新的格式化上下文(It becomes a new formatting context),也就是說,這意味著元素外部的布局不會再影響它的子元素;

        更具體的,可以看看這篇文章 -- CSS Containment in Chrome 52

        我們解釋下第一點,非常的有意思,它會生成一個自己的新的堆疊上下文,也就是說,它將改變 position: fixed 元素的基準,它會使得設置了 position: fixed 的元素不再相對于視口進行定位,而是相對于該元素進行定位。也就是退化成了 position: absolute。

        當然,這個不是本文的重點,我提供了一個 Demo -- contain: paint create a stacking context,這里就不繼續展開。

        總結一下

        到此,本文提供了 3 種可以實現超出容器范圍裁剪的方法:

        • overflow: hidden
        • clip-path 繪制裁切區域
        • contain: paint 不繪制元素范圍外的內容

        這里再提供下 3 個示例的 Demo:CodePen Demo -- Overflow Hidden In CSS

        當然,它們之間還是有一些差異:

        1. overflow: hiddencontain: paint 會創建一個 BFC,而clip-path不會,它只是單純的裁剪
        2. 兼容性間的差異

        所以也就是說,CSS 不僅僅只有 overflow: hidden 實現 overflow: hidden,很多情況,可以靈活使用。

        牛刀小試

        再來個有意思的環節,在 一行 CSS 代碼的魅力 中,提到了 CSS Battle 。

        這個網站是核心玩法就是:官方給出一張圖形,在給定的 400 x 300 的畫布上,能夠用越短的代碼實現它,分數就越高。

        上次講了一題通過一行 CSS 代碼實現,今天,我們再來看看第二題

        6 上午10.26.03.png

        怎么用最短的代碼實現它呢?想想今天說的 clip-path。

        首先,我們利用這一一段代碼,生成這樣一個圖形:

        <style>
        body {
            margin: 0 50px;
            background: #62374e;
            border: 50px dashed #fdc57b;
        }

        7 上午10.26.03.png

        然后,利用 clip-path,把上下兩部分裁掉即可。

        <style>
        body {
            margin: 0 50px;
            background: #62374e;
            border: 50px dashed #fdc57b;
          + clip-path: polygon(0 50px, 100% 50px, 100% 250px, 0 250px);
        }

        6 上午10.26.03.png

        這樣就完美實現啦。當然,現在字符數有點多,有 158 個字符這么多。其實對于裁剪矩形區域,clip-path 有更便捷的語法,上述 clip-path:polygon(0 50px, 100% 50px, 100% 250px, 0 250px) 可以替換成 clip-path:inset(50px 0),減少了 20 個字符。

        當然,再暴力一點,我們也可以一行實現:

        <body bgcolor=62374e style=margin:0+50;border:dashed+50px#fdc57b;clip-path:inset(50px+0>
        當然,這里可能用了一些這個網站才允許的語法,不過核心實現還是在于用 clip-path 切割掉多余部分

        最后

        好了,本文到此結束,希望對你有幫助 :),想 Get 到最有意思的 CSS 資訊,千萬不要錯過我的公眾號 -- iCSS前端趣聞 ??

        qrcode.png

        更多精彩 CSS 技術文章匯總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。

        如果還有什么疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

        查看原文

        贊 7 收藏 5 評論 1

        chokcoco 發布了文章 · 1月8日

        水平垂直居中深入挖掘

        在上篇文章 -- 一行 CSS 代碼的魅力 的最后,提到了兩種快速實現水平垂直居中的方式。

        當然,CSS 中實現水平垂直居中的方式很多。別看到水平垂直居中就準備右上角 x 掉,本文的重點不是羅列有多少種方式實現水平垂直居中方式,而是探討一下常見的幾種水平垂直居中的方式的利弊。

        嗯哼?也就是:

        • 那么多種水平垂直居中的方式,如果真的在業務中需要使用了,你腦海里第一時間會想到哪個?
        • 不同的水平垂直居中方式,它們肯定存在差異,那么最顯著的不同是什么?
        • 有沒有所謂的最完美的水平垂直居中?

        本文將討論 4 種水平垂直居中的方式,分別是,并且每個起個名字方面下面看圖:

        1. absolute: position: absolute 配合 top:50%;left:50%;transform:translate(-50%, -50%)
        2. autobot: display:flex 配合 margin:auto
        3. flex: display:flex 配合 align-items:center、justify-content:center
        4. grid: display:grid 配合 place-content:center;

        居中單個元素

        對于如下簡單的結構:

        <div class="g-container">
            <div class="sub"></div>
        </div>

        居中單個元素而言,上述 4 種方法都很好,沒有問題。

        1.png

        CodePen Demo -- Centering in CSS

        居中多個元素

        對于如下稍微復雜點的結構:

        <div class="g-container">
            <div class="sub">1</div>
            <div class="sub">123</div>
            <div class="sub">123456</div>
        </div>

        那么如果是居中多個子元素,上述 4 種方法,就能體現出明顯的不一樣。稍微也修改一下子元素,不給它設定寬度,通過 padding 撐開即可:

        .sub {
            border: 1px solid deeppink;
            padding: 10px;
            border-radius: 5px;
        }

        看看結果如何:

        2.png

        CodePen Demo -- Centering in CSS 2

        簡單分析分析:

        1. absolute 的方法明顯有問題,由于用的絕對定位,其實 3 個子元素都疊在了一起
        2. flexgrid 的方法,如果不手動添加邊距(margin 或者 gap),會貼在一起
        3. 不限制方向的話,flex 默認是水平排列,grid 是豎直排列
        4. 非常重要的一點,grid 布局下的子元素的寬度,所以子元素的寬度會被強行拉伸至最寬的一個子元素的內容的寬度

        對于多個子元素,absolute 方法明顯不適用, 接下來主要看剩余 3 個方法在一些細節上的差異。

        控制間距

        如果我們希望控制每個元素之間的間距呢?我們給 autobot、flex、grid 的容器各自加上 gap: 5px,再看看:

        .g-container{
            gap: 5px;
        }

        3.png

        CodePen Demo -- Centering in CSS 3

        1. margin: auto 由于需要均分剩余空間,所以表現并不好,無法按照我們設想的 5px 寬度進行間隔

        讓元素多到溢出

        OK,接下來,我們讓內容再多一點,多到溢出整個容器,看看有什么不一樣。

        4.png

        再來一張豎直方向排列的:

        5.png

        CodePen Demo -- Centering in CSS 4

        可以看到:

        1. 非常重要的一點,由于沒有了剩余空間,margin: auto 已經無法做到均勻分配,水平垂直居中了,而是一邊貼著容器邊,另外一邊溢出
        2. flexgrid 都做到了即便超出容器空間,依然是水平垂直居中的

        總結一下

        經由上述幾個 DEMO 可以看出來,在目前比較常用的水平垂直居中方案當中。flex 方案應該是目前而言最優的選擇,它能夠在各種環境下都保持內部元素的水平垂直居中并且不改變子元素的某些特征:

        1. 便捷的水平垂直居中單個元素
        2. 便捷的水平垂直居中多個元素,無論是橫向、豎向,亦或內容超出
        3. 非常方便控制子元素之間的間距
        4. 不會改變子元素的寬度

        當然,美中不足的是,可能相對而言,要敲多幾個字符。

        margin: autogrid 則或多或少有一些小問題。absolute 無法應付多個元素。

        最后

        本文知識點比較細,也表明 CSS 雖然簡單,但是不代表它容易。我們日常工作中用到的很多屬性其實還有很多細節可以挖掘深入。

        譬如,可以再比較下在書寫方式 writing-mode 不同的場景下,上述水平垂直居中的方式的異同,等等。

        好了,本文到此結束,希望對你有幫助 :),想 Get 到最有意思的 CSS 資訊,千萬不要錯過我的 iCSS 公眾號 ??

        QQ20210108-0.png

        更多精彩 CSS 技術文章匯總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。

        如果還有什么疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

        查看原文

        贊 8 收藏 5 評論 0

        chokcoco 發布了文章 · 1月7日

        一行 CSS 代碼的魅力

        之前在知乎看到一個很有意思的討論 一行代碼可以做什么?

        那么,一行 CSS 代碼又能不能搞點事情呢?

        CSS Battle

        首先,這讓我想到了,年初的時候沉迷的一個網站 CSS Battle 。這個網站是核心玩法就是:

        官方給出一張圖形,在給定的 400 x 300 的畫布上,能夠用越短的代碼實現它,分數就越高。

        注意是,完全一模一樣還原。

        其中,第一題就非常有趣,看看題目:

        1.png

        嗯,想一想,如果給定這樣一張圖形,告訴你 HTML 的大小是 400px x 300px,圖片中使用到的顏色是 #5D3A3A, #B5E0BA,你會怎么用 CSS 去實現它呢?

        因為要求,字符數越少,分數越高,當然是選字符少的標簽,是不是這樣:

        <p>
        <style>
        body{background:#5D3A3A;}
        p {width: 200px;height:200px;background:#B5E0BA;}
        </style>
        這個網站的設定,HTML 和 CSS 可以按照上述格式寫在一起,<style> 標簽內的即為 CSS,之外的是 HTML ,標簽 <p></p> 可以簡寫為 <p>

        額,這樣當然可以,但是看看文章的標題,一行 CSS ?這明顯不是啊,并且這里有 100+ 個字符。我們得把字符數壓一壓。

        嗯嗯嗯,想到了陰影,嘗試下使用 box-shadow

        <a>
        <style>
        a {
            box-shadow:0 0 0 200px #b5e0ba,0 0 0 400px #5d3a3a;
        }
        </style>

        利用兩層陰影,完美實現圖例圖形,并且,字符數壓縮到了 82 個。當然,這還不是極致,我們完全可以內聯 CSS,再減少字符數:

        <a style="box-shadow:0 0 0 200px #b5e0ba,0 0 0 400px #5d3a3a">

        只有 62 個字符。當然,從一行代碼的角度,這個問題完美的解決了,如果追求極致的字符數,上述的代碼還可以再簡化一下:

        <a style=box-shadow:0+0+0+200px#b5e0ba,0+0+0+5in#5d3a3a>
        這里有一些小知識點,HTML5 支持屬性后面不用引號包住,又譬如 box-shadow: 0 0 0 400px #5d3a3a 是可以壓縮到 box-shadow:0+0+0+5in#5d3a3a,CSS 中 1in=96px,但是畫布只有 400px,5in 大于 400px,也沒有問題,能夠充滿畫布,但是 400px 相對 5in 字符多了 2 個。

        瀏覽器里面看一下,這個是完全正確的寫法:

        1.png

        OK,最終只有 56 個字符,完美。當然,CSS Battle 里面還有更多更復雜的挑戰,也有很多能夠通過一行代碼實現的,感興趣的嘗試下。

        一行背景代碼

        要說到 CSS 最有意思的屬性,我覺得背景(background)肯定能夠獲得很多選票。背景分為:

        • 純色
        • 線性漸變(linear-gradient)
        • 徑向漸變(radial-gradient)
        • 角向漸變(conic-gradient)
        • 多重線性漸變(repeating-linear-gradient)
        • 多重徑向漸變(repeating-radial-gradient)
        • 多重角向漸變(repeating-conic-gradient)

        突出一個字,離譜。并且它們還可以互相混合、疊加添加濾鏡、配合各種背景相關屬性等等等。

        不過今天,來看看一行 CSS Background 代碼能玩出什么花來。嗯?這里的主角是多重角向漸變(repeating-conic-gradient),只用一行 CSS 代碼:

        {
            background: repeating-conic-gradient(#fff, #000, #fff 0.1deg);
        }

        這什么玩意?腦補一下,這行代碼繪制出來的圖形會是什么樣子?看看:

        2.png

        Wow,不可思議。這里 0.1deg 非常關鍵,這里的角度越?。ㄐ∮?1deg 為佳),圖形越酷炫。

        我們把 0.1deg 替換成 30deg 看看:

        {
            background: repeating-conic-gradient(#fff, #000, #fff 30deg);
        }

        3.png

        當然也非常好看,只不過沒有上面那個那么驚艷。

        CodePen -- One Line CSS Pattern

        我們可以再利用 CSS - Doodle,隨機產生這份美:

        4.gif

        CSS - Doodle 它是一個基于 Web-Component 的庫。允許我們快速的創建基于 CSS Grid 布局的頁面,并且提供各種便捷的指令及函數(隨機、循環等等),讓我們能通過一套規則,得到不同 CSS 效果。

        沒錯,它的本質其實就是上述的那一行核心 CSS 代碼。

        CSS Doodle - CSS Magic Conic-gradient

        margin: autoplace-items: center

        這個也非常有意思,當然,它不算嚴格意義上的一行 CSS,因為需要搭配其他屬性一起使用。

        最快水平垂直居中一個元素的方法是什么?

        水平垂直居中也算是 CSS 領域最為常見的一個問題了,不同場景下的方法也各不相同,各有優劣。嗯,下面這兩種方法應該算是最便捷的了:

        方法一:flex 布局下的 margin: auto

        <div class="g-container">
            <div class="g-box"></div>
        </div>
        .g-container {
            display: flex;
        }
        
        .g-box {
            margin: auto;
        }
        上面的 display: flex 替換成 display: inline-flex | grid | inline-grid 也是可以的。

        display: flex 布局下, margin auto 的生效不僅是水平方向,垂直方向也會自動去分配這個剩余空間。

        CodePen Demo -- 使用 margin auto 水平垂直居中元素

        如果你對非常有用的 margin: auto 還不是很了解,可以看看:探秘 flex 上下文中神奇的自動 margin

        方法二:grid 布局下的 place-items: center

        直接上代碼:

        .g-container {
            display: grid;
            place-items: center
        }

        上述兩份代碼效果都是一樣的:

        5.gif

        CodePen Demo -- 水平垂直居中元素 grid+ place-items: center

        最后

        一行 CSS 能干什么?肯定不止這些,當然,這不就是 CSS 的樂趣么。想 Get 到最有意思的 CSS 資訊,千萬不要錯過我的 iCSS 公眾號 ??

        6.png

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

        更多精彩 CSS 技術文章匯總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。

        如果還有什么疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

        查看原文

        贊 11 收藏 4 評論 1

        chokcoco 發布了文章 · 2020-02-12

        CSS 故障藝術

        v2-c7a2db16babbdefec756d3eed6bc0fa4_hd

        本文的主題是 Glitch Art,故障藝術。

        什么是故障藝術?我們熟知的抖音的 LOGO 正是故障藝術其中一種表現形式。它有一種魔幻的感覺,看起來具有閃爍、震動的效果,很吸引人眼球。

        故障藝術它模擬了畫面信號出現故障導致成像錯誤的感覺。青色色塊與紅色色塊無法重合就是這種故障的體現。從膠片時代開始到今天的數碼時代,這種故障一直是觀眾非常熟悉的現象。即使抖音的 LOGO 只是靜態的,大腦也會自己補完整個效果,甚至還會自己腦補信號干擾的噪音。

        當然,廣義的故障藝術不僅僅指這種效果,我覺得是很寬泛的,本文將介紹一些 CSS 能夠模擬完成的故障藝術效果。

        使用混合模式實現抖音 LOGO

        首先從靜態的開始,抖音的 LOGO 就是很好的一個例子。

        image

        它看著像是 3 個 J 形重疊在一起。而實際上,是兩個 J 形重疊在一起,重疊部分表現為白色,這個特性,使用 CSS 的混合模式 mix-blend-mode 非常好實現,而單個 J 形示意圖如下:

        image

        圖片來源于知乎:為什么抖音的標志,看起來具有“電”“閃爍”“震動”的感覺?

        單個 J 形其實是由 3/4圓 + 豎線 + 1/4圓組成,使用一個標簽即可完成(加上兩個偽元素)。

        關鍵點

        • 主要借助偽元素實現了整體 J 結構,借助了 mix-blend-mode 實現融合效果
        • 利用 mix-blend-mode: lighten 混合模式實現兩個 J 形結構重疊部分為白色

        所以整個效果只需要兩個標簽:

        <div class="j"></div>
        <div class="j"></div>

        簡易 SASS 代碼:

        // 實現第一個 J
        .j {
            position: absolute;
        
            &::before {
                content: "";
                ...
            }
        
            &::after {
                content: "";
                ...
            }
        }
        
        // 實現第二個 J,加上混合模式
        .j:last-child {
            position: absolute;
            mix-blend-mode: lighten;
        
            &::before {
                content: "";
                ...
            }
            &::after {
                content: "";
                ...
            }
        }

        示意圖如下(為了更好理解,加上了動畫):

        tiktok

        完整的 DEMO:

        使用 mix-blend-mode 實現抖音 LOGO

        圖片的 Glitch Art 風

        當然,上面實現的是我們實現的 J 形的疊加,理解了這種技巧之后,我們可以把它運用到圖片之上。

        這里我們會運用到 background-blend-modemix-blend-mode 。

        假設,我們有這樣一張圖:

        image

        只需要一個標簽即可

        <div class="mix"></div>

        給兩張同樣的圖片,疊加上 青色#0ff 和 紅色#f00,并且錯開一定的距離,兩張圖都要加上 background-blend-mode: lighten,其中一張再加上 mix-blend-mode: darken

        .mix {
            width: 400px;
            height: 400px;
            background: url($img), #0ff;
            background-blend-mode: lighten;
        
          &::after {
            content: '';
            position: absolute;
            margin-left: 10px;
            width: 400px;
            height: 400px;
            background: url($img), #f00;
            background-blend-mode: lighten;
            mix-blend-mode: darken;
          }
        }

        得到如下效果:

        image

        這里與上述抖音 LOGO 的處理是有點不一樣的,使用的混合模式也不止一種,簡單解釋下。

        1. 因為圖片本身不是紅色和青色的,所以需要通過 background-image 疊加上這兩種顏色,并通過 background-blend-mode: lighten 讓其表現出來
        2. 為了保持中間疊加部分的原色,需要再疊加一個 mix-blend-mode: darken 反向處理一下。(不理解的同學可以打開調試,手動關掉幾個混合模式,自己感受感受即可)

        完整的 DEMO:

        圖片的類抖音 LOGO Glitch 效果

        動態類抖音風格 Glitch 效果

        OK,有了上面的鋪墊,我們接下來可以給這種效果加上動畫。

        關鍵點

        • 利用 mix-blend-mode: lighten 混合模式實現兩段文字結構重疊部分為白色
        • 利用元素位移完成錯位移動動畫,形成視覺上的沖擊效果

        看看效果:

        textglitch

        本文篇幅有點長,代碼就不上了,完整 DEMO 在這里:

        類抖音 LOGO 文字故障效果

        當然,我們也不是一定要使用混合模式去使得融合部分為白色,可以僅僅是使用這個配色效果,基于上面效果的另外一個版本,沒有使用混合模式。

        關鍵點

        • 利用了偽元素生成了文字的兩個副本
        • 視覺效果由位移、遮罩、混合模式完成
        • 配色借鑒了抖音 LOGO 的風格

        textglitch2

        完整 DEMO 在這里:

        CSS文字故障效果

        僅僅使用配色沒有使用混合模式的好處在于,對于每一個文字的副本,有了更大的移動距離和可以處理的空間。

        Glitch Art 風格的 404 效果

        稍微替換一下文本文案為 404,再添加上一些濾鏡效果(hue-rotate()、blur())嘿嘿,找到了一個可能實際可用的場景:

        效果一:

        404

        效果二:

        404

        兩個 404 效果的 Demo 如下:

        其他配色效果

        當然,不僅僅只有這一種紅 + 青的配色效果。還有一些其他的配色及混合模式的搭配,如 黃 + 粉紅 + 藍配合 mix-blend-mode: multiply。

        然后,有的時候,效果不希望和背景混合在一起,可以使用 isolation: isolate 進行隔離。


        好,上述效果可以歸類為一個分類。接下來開啟下一個分類

        clip-path 登場

        下半篇幅的主角主要是 clip-path 。

        clip-path 一個非常有意思的 CSS 屬性。

        clip-path CSS 屬性可以創建一個只有元素的部分區域可以顯示的剪切區域。區域內的部分顯示,區域外的隱藏。剪切區域是被引用內嵌的URL定義的路徑或者外部 SVG 的路徑。

        也就是說,使用 clip-path 可以將一個容器切成我們想要的樣子。

        例如這樣:

        <div>TXET</div>
        div  {
            margin: auto;
            padding: 10px;
            line-height: 1.2;
            font-size: 60px;
            background: #ddd;
        }

        正常是這樣的:

        image

        使用 clip-path 剪裁為一個平行四邊形:

        div  {
            margin: auto;
            padding: 10px;
            line-height: 1.2;
            font-size: 60px;
            background: #ddd;
        +   clip-path: polygon(35% 0, 85% 0, 75% 100%, 25% 100%);
        }

        結果如下:

        image

        那么,思路就有了,我們可以將一個文字復制幾個副本,重疊在一起,再分別裁剪這幾個副本進行位移動畫即可。

        使用 clip-path 實現文字斷裂動畫

        我們還是使用元素的 ::before、::after 兩個偽元素復制兩份副本,再分別使用 clip-path 進行剪裁,再使用 transform 進行控制。

        核心代碼:

        <div data-text="Text Crack">
            <span>Text Crack</span>
        </div>
        div {
            position: relative;
            animation: shake 2.5s linear forwards;
        }
        
        div span {
            clip-path: polygon(10% 0%, 44% 0%, 70% 100%, 55% 100%);
        }
        
        div::before,
        div::after {
            content: attr(data-text);
            position: absolute;
            top: 0;
            left: 0;
        }
        
        div::before {
            animation: crack1 2.5s linear forwards;
            clip-path: polygon(0% 0%, 10% 0%, 55% 100%, 0% 100%);
        }
        
        div::after {
            animation: crack2 2.5s linear forwards;
            clip-path: polygon(44% 0%, 100% 0%, 100% 100%, 70% 100%);
        }
        
        // 元素晃動,斷裂前搖
        @keyframes shake {    
            ...
        }
        
        @keyframes crack1 {
            0%,
            95% {
                transform: translate(-50%, -50%);
            }
        
            100% {
                transform: translate(-55%, -45%);
            }
        }
        
        @keyframes crack2 {
            0%,
            95% {
                transform: translate(-50%, -50%);
            }
        
            100% {
                transform: translate(-45%, -55%);
            }
        }

        可以得到這樣的效果:

        textcrack

        完整的 Demo:

        clip-path 實現文字斷裂效果

        這個效果,最早的版本見于這位作者:George W. Park

        clip-path 的 Glitch Art

        OK,繼續,有了上面的鋪墊之后,接下來,我們把這個效果作用于圖片之上,并且再添加上動畫。

        隨便選一張圖片:

        image

        哇哦,非常的賽博朋克。

        實現動畫的關鍵在于:

        • 使用元素的兩個偽元素,生成圖片的兩個副本
        • 使用 clip-path 對兩個副本圖片元素進行裁剪,然后進行位移、transform變換、添加濾鏡等一系列操作。

        簡單貼一下偽代碼:

        <div></div>
        $img: "https://mzz-files.oss-cn-shenzhen.aliyuncs.com///uploads/U1002433/0cb5e044a1f0f7fc15f61264ee97ac1f.png";
        
        div {
            position: relative;
            width: 658px;
            height: 370px;
            background: url($img) no-repeat;
            animation: main-img-hide 16s infinite step-end;
        }
        
        div::before,
        div::after {
            position: absolute;
            width: 658px;
            height: 370px;
            top: 0;
            left: 0;
            background: inherit;
        }
        
        div::after {
            content: "";
            animation: glitch-one 16s infinite step-end;
        }
        
        div::before {
            content: "";
            animation: glitch-two 16s infinite 1s step-end;
        }
        
        @keyframes glitch-one {
            @for $i from 20 to 30 {
                #{$i / 2}% {
                    left: #{randomNum(200, -100)}px;
                    clip-path: inset(#{randomNum(150, 30)}px 0 #{randomNum(150, 30)}px);
                }
            }
        
            15.5% {
                clip-path: inset(10px 0 320px);
                left: -20px;
            }
            16% {
                clip-path: inset(10px 0 320px);
                left: -10px;
                opacity: 0;
            }
            ....
        }
        
        @keyframes glitch-two {
            @for $i from 40 to 50 {
                #{$i / 2}% {
                    left: #{randomNum(200, -100)}px;
                    clip-path: inset(#{randomNum(180)}px 0 #{randomNum(180)}px);
                }
            }
        
            25.5% {
                clip-path: inset(10px 0 320px);
                left: -20px;
            }
            26% {
                clip-path: inset(10px 0 320px);
                left: -10px;
                opacity: 0;
            }
           ...
        }
        
        @keyframes main-img-hide {
            5% {
                filter: invert(1);
            }
            ...
        }
        

        由于動畫部分代碼量太多,所以使用了 SASS 循環函數隨機生成了部分。如果手動控制,效果其實還會更好,當然,調試動畫消耗的時間會更多。

        看看效果,雖然 CSS 能力有限,但實際的效果也不是說那么的差:

        imgglitch

        GIF 圖太大,掉幀太多,效果大打折扣。完整的 Demo 及效果,你可以戳這里:

        clip-path 實現圖片的故障藝術風格動畫

        總結

        本文重點介紹了純 CSS 下使用混合模式和 clip-path 實現的一些故障藝術(Glitch Art),當然,上述的幾個效果都不僅僅是靠這兩個屬性單打獨斗就能完成的。

        在其中,transform、filter 也在其中發揮了很重要的作用。當然僅僅使用 transform、filter 也能夠實現一些基礎的故障藝術效果,這個讀者們感興趣的可以自己多加嘗試。如果想使用于生產環境,需要考慮 mix-blend-modeclip-path 的兼容性問題。

        我自己對 Glitch Art 的理解其實也比較淺顯,只是覺得使用 CSS 去實現這樣一些類似的效果比較有意思,就動手嘗試實踐了一番,相關術語或者名詞理解錯誤煩請各位諒解指出。

        最后

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

        更多精彩 CSS 技術文章匯總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。

        更多精彩有趣的 CSS 效果,歡迎來這里看看 CSS 靈感 -- 在這里找到寫 CSS 的靈感。

        如果還有什么疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

        查看原文

        贊 57 收藏 36 評論 5

        chokcoco 贊了文章 · 2019-08-21

        紅黑樹,超強動靜圖詳解,簡單易懂

        寫在前面

        紅黑樹,對很多童鞋來說,是既熟悉又陌生。學校中學過,只了解大概;工作中不怎么使用,但面試又是重點。每次需要查看紅黑樹內容時都很難以更生動形象的方式來理解其內容。沒錯,本文內容就是要解決這個問題,用簡單的語言,搭配靜圖和動圖(利用大腦圖形記憶方式),讓你對紅黑樹有更深入的了解和更清晰的記憶,希望小伙伴們再次遇到紅黑樹的問題不至于頭大,建議讀該文章姿勢: 打開兩個頁面,一個頁面看圖片和內容,一個頁面看公式,像玩魔方一樣,多玩幾次就明白了

        通過工具 (公眾號回復「工具」—>那些可以提高效率的工具—>紅黑樹) 動態感受紅黑樹的轉換過程

        俺家司令買完東西后,我倆經常會發生這樣的一段對話:

        司令:你猜我買的這個多少錢?
        我: 1000
        司令: 高了
        我: 500
        司令: 低了:
        我: 750
        ...... 直到最后猜中

        這樣說大家應該已經猜到了是「二分查找法」,通過這個例子我想要引出的是 ,來看圖片


        程序中的樹其實是我們日??吹降臉涞牡褂?,或者發揮一下想象,倒影也可以是樹根

        二叉查找樹

        二叉查找樹,Binary Search Tree 「BST」,要想了解二叉查找樹,我們首先看下二叉查找樹有哪些特性呢?

        1. 某節點的左子樹節點值僅包含小于該節點值
        2. 某節點的右子樹節點值僅包含大于該節點值
        3. 左右子樹每個也必須是二叉查找樹

        看個圖就輕松理解上面三句話的意思了:

        上圖,結合二叉查找樹的三條約束來看,非常好,沒有什么問題。再來看一個圖,依舊符合上面三條約束,感覺有問題嗎?

        1. 這是一個走路一米六,一米八的樹
        2. 這是一個畸形的樹,大風一掛很可能被折斷的樹

        從程序的角度來說這個樹不夠平衡,查找次數或時間復雜度 O(h)可能會隨著一條腿長無限增長

        理科生在高中學習生物時學過一個關鍵字「去除頂端優勢」,通過去除植物頂端優勢,側芽會迅速生長,慢慢變得強壯和平衡, 紅黑樹其實就是去除二叉查找樹頂端優勢的解決方案,從而達到樹的平衡

        紅黑樹

        紅黑樹,Red-Black Tree 「RBT」是一個自平衡(不是絕對的平衡)的二叉查找樹(BST),樹上的每個節點都遵循下面的規則:

        1. 每個節點都有紅色或黑色
        2. 樹的根始終是黑色的 (黑土地孕育黑樹根,??)
        3. 沒有兩個相鄰的紅色節點(紅色節點不能有紅色父節點或紅色子節點,并沒有說不能出現連續的黑色節點
        4. 從節點(包括根)到其任何后代NULL節點(葉子結點下方掛的兩個空節點,并且認為他們是黑色的)的每條路徑都具有相同數量的黑色節點

        瞬間懵逼?了解一下印象就行,開始玩魔方都是要照著魔方公式一點點玩的,多玩幾次就熟悉了。紅黑樹也一樣,紅黑樹有兩大操作:

        1. recolor (重新標記黑色或紅色)
        2. rotation (旋轉,這是樹達到平衡的關鍵)

        我們會先嘗試 recolor,如果 recolor 不能達到紅黑樹的 4 點要求,然后我們嘗試 rotation,其實紅黑樹的關鍵玩法就是弄清楚 recolor 和 rotation 的規則,接下來看看詳細的算法公式吧 千萬別著急記憶公式,有圖示會逐步說明,就像魔方一樣,多玩幾次就懂了:
        假設我們插入的新節點為 X

        1. 將新插入的節點標記為紅色
        2. 如果 X 是根結點(root),則標記為黑色
        3. 如果 X 的 parent 不是黑色,同時 X 也不是 root:

          • 3.1 如果 X 的 uncle (叔叔) 是紅色

            • 3.1.1 將 parent 和 uncle 標記為黑色
            • 3.1.2 將 grand parent (祖父) 標記為紅色
            • 3.1.3 讓 X 節點的顏色與 X 祖父的顏色相同,然后重復步驟 2、3

        話不多說,看下圖

        跟著上面的公式走:

        1. 將新插入的 X 節點標記為紅色
        2. 發現 X 的 parent (P) 同樣為紅色,這違反了紅黑樹的第三條規則「不能有兩個連續相鄰的紅色節點」
        3. 發現 X 的 uncle (U) 同樣為紅色
        4. 將 P 和 U 標記為黑色
        5. 將 X 和 X 的 grand parent (G) 標記為相同的顏色,即紅色,繼續重復公式 2、3
        6. 發現 G 是根結點,標記為黑色
        7. 結束

        剛剛說了 X 的 uncle 是紅色的情況,接下來要說是黑色的情況

        1. 如果 X 的 parent 不是黑色,同時 X 也不是 root:

          • 3.2 如果 X 的 uncle (叔叔) 是黑色,我們要分四種情況處理

            • 3.2.1 左左 (P 是 G 的左孩子,并且 X 是 P 的左孩子)
            • 3.2.2 左右 (P 是 G 的左孩子,并且 X 是 P 的右孩子)
            • 3.2.3 右右 (和 3.2.1 鏡像過來,恰好相反)
            • 3.2.4 右左 (和 3.2.2 鏡像過來,恰好相反)

        當出現 uncle 是黑色的時候我們第一步要考慮的是 旋轉 ,這里先請小伙伴不要關注紅黑樹的第 4 條規則,主要是為了演示如何旋轉的,來一點點看,不要看圖就慌,有解釋的??:

        左左情況

        這種情況很簡單,想象這是一根繩子,手提起 P 節點,然后變色即可

        左右

        左旋: 使 X 的父節點 P 被 X 取代,同時父節點 P 成為 X 的左孩子,然后再應用 左左情況

        右右

        與左左情況一樣,想象成一根繩子

        右左

        右旋: 使 X 的父節點 P 被 X 取代,同時父節點 P 成為 X 的右孩子,然后再應用 右右情況

        你說的動圖在哪里,你個大騙子,別著急,現在就為小伙伴們奉上動圖演示,來說明公式的使用:

        案例一

        插入 10,20,30,15 到一個空樹中
        1. 向空樹中第一次插入數字 10,肯定是 root 節點
        2. root 節點標記成黑色

        1. 向樹中插入新節點 20,標記為紅色
        2. 20 > 10,并發現 10 沒有葉子節點,將新節點 20 作為 10 的右孩子

        1. 向樹中插入新節點 30,標記為紅色
        2. 30 > 10,查找 10 的右子樹,找到 20
        3. 30 > 20,繼續查找 20 的右子樹,發現 20 沒有葉子節點,將值插在此處
        4. 30 和 20 節點都為紅色,30 為右孩子,20 也為右孩子,觸發了 右右情況
        5. 通過一次旋轉,提起 20 節點
        6. 20 節點是根結點,標記為黑色

        1. 向樹中插入新節點 15,標記為紅色
        2. 通過比對大小和判斷是否有葉子節點,最終插值為 10 節點的右孩子
        3. 15 和 10 節點都為紅色,15 的 uncle 節點 30 也為紅色
        4. 按照公式,將 15 的 parent 10 和 uncle 30 更改為黑色
        5. 讓 15 節點 grand parent 20 的顏色與 15 節點的顏色一樣,變為紅色
        6. 20 為根結點,將其改為黑色

        繼續插入其他節點只不過反復應用上面的公式,上面應用到的紅黑樹工具,可以暫停動畫效果,一幀一幀的看紅黑樹的轉換過程,這樣通過練習,查看公式,觀察變化三管齊下,紅黑樹的入門理解應該完全不再是問題了

        靈魂追問

        1. jdk 1.8 HashMap 中有使用到紅黑樹,你知道觸發條件是什么嗎?有讀過源碼是如何 put 和 remove 的嗎?
        2. 這里講的是紅黑樹的 insert,delete 又是什么規則呢?
        3. 哪些場景可以應用紅黑樹?
        4. 你了解各種樹的時間復雜度嗎?
        5. 留個小作業,應用工具將 [10 70 32 34 13 56 32 56 21 3 62 4 ] 逐個插入到樹中,理解紅黑樹 recolor 和 rotation 的轉換規則

        提高效率工具

        [center]


        推薦閱讀


        歡迎持續關注公眾號:「日拱一兵」

        • 前沿 Java 技術干貨分享
        • 高效工具匯總 | 回復「工具」
        • 面試問題分析與解答
        • 技術資料領取 | 回復「資料」
        以讀偵探小說思維輕松趣味學習 Java 技術棧相關知識,本著將復雜問題簡單化,抽象問題具體化和圖形化原則逐步分解技術問題,技術持續更新,請持續關注......
        查看原文

        贊 100 收藏 69 評論 6

        一本到在线是免费观看_亚洲2020天天堂在线观看_国产欧美亚洲精品第一页_最好看的2018中文字幕