css 的 filter屬性竟然如此好玩

lulu_up發表於2022-03-15

css 的 filter屬性竟然如此好玩

背景

     在此之前我對css裡面的filter屬性不是很瞭解, 只知道使用這個屬性來改變svg圖片的顏色, 最近恰好查了很多相關文件並做了大量實驗, 並有了一些啟發與想法, 索性就在這裡分享出來。

一、filter 濾鏡

     "濾鏡"這個名字很貼切了, 可以理解成為元素新增各種顯示效果, 先不用記各種名詞我們們直接看效果, 使用方法 & 效果圖:

<style>
  #lulu {
     filter: grayscale(1);
  }
</style>

<body>
  <img id="lulu" src="./img/頭像.jpeg" />
</body>

image.png

     看上圖裡的這些效果, 比如第一排第一個, 我們會想到在某些特定的紀念日網站整體會變成灰色的樣式, 應該就是用的這個屬性, 第二排的第一張就可以用與某些事物被"雷擊"?

二、做一個'抖動'特效

image.png
     看到這張圖我第一個想法就是做個抖動的特效, 就是那種很動感的效果:

     當然配合上一旋轉效果也不錯:

     原理就是兩個圖片層疊在一起, 上面的圖片進行放大與旋轉動畫:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
  <style>
    .box {
      position: relative;
      border: 1px solid gray;
      display: flex;
      overflow: hidden;
      width: 110px;
      padding: 0px;
      margin-top: 100px;
      margin-left: 300px;
    }

    .box>img {
      width: 100px;
      height: 100px;
      margin-left: 6px;
      filter: invert(1);
    }

    .mk {
      position: absolute;
      top: 0;
      left: 0;
      opacity: 0.5;
      animation: cc 0.5s linear infinite;
    }

    @keyframes cc {
      from {
        transform: scale(1.2);
      }

      to {
        transform: scale(1);
      }
    }

    .mk2 {
      position: absolute;
      top: 0;
      left: 0;
      opacity: 0.5;
      animation: cc2 0.5s linear infinite alternate;
      border-radius: 50%;
      overflow: hidden;
    }

    @keyframes cc2 {
      from {
        transform: scale(2.2) rotate(30deg);
      }

      to {
        transform: scale(1) rotate(0deg);
      }
    }
  </style>
</head>

<body>
  <div class="box">
    <img src="./img/頭像.jpeg" />
    <img class="mk" src="./img/頭像.jpeg" />
  </div>

  <div class="box">
    <img src="./img/頭像.jpeg" />
    <img class="mk2" src="./img/頭像.jpeg" />
  </div>
</body>

</html>

三、drop-shadow 陰影

     filter屬性通過設定drop-shadow為元素新增陰影, 可是早就有box-shadow屬性了呀, 那這兩個屬性有什麼區別了?

image.png

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
  <style>
    #w1 {
      width: 50px;
      height: 50px;
      font-size: 36px;
      font-weight: 900;
      box-shadow: 0 0 2px red;
    }

    #w2 {
      width: 50px;
      height: 50px;
      font-size: 36px;
      font-weight: 900;
      filter: drop-shadow(0px 0px 2px red);
    }
  </style>
</head>

<body>
  <p id="w1">九</p>
  <p id="w2">九</p>
</body>

</html>

     上圖可知, box-shadow是針對整個dom元素進行陰影的產生, 但是drop-shadow會忽略掉"透明"的部分。

四、drop-shadow 複製 (做一個看圖猜人物遊戲)

     注意: 我這裡使用的都是svg圖片。

     既然與box-shadow都有為元素設定陰影的能力, 那麼box-shadow有複製自身樣的能力drop-shadow是都也有?

     所謂box-shadow的複製自身樣式如圖所示, box-shadow可以製作n個與元素本身形狀相同或不同的樣式, 下圖右側紅色的方塊就是左圖的陰影:

image.png

     再看一下drop-shadow的表現:

image.png

     看到上面的圖我第一反應就是"猜人物"小遊戲, 我們把人物的輪廓也就是右圖顯示出來, 然後在公佈答案的時候展示左側的原圖即可。

賦值gif圖有bug

     賦值gif圖會有bug, 效果如下:

五、drop-shadow 批量複製

     box-shadow屬性是可以寫多個屬性值的, 我一般會利用這個屬性進行一個單一樣式的dom元素的複製 效果如下圖:

image.png

     drop-shadow有點'狠', 他的每一次複製都是基於上次的整體效果進行的陰影投射:

image.png

     上圖可以看出, 第一個複製後是出現了橫排的2個, 第二次投射是產生了下方的兩個, 並且每次投射都是疊加的, 下面我們看一組更誇張的:

image.png

     可想而知這種增長方式有多可怕, 稍微寫幾遍就可以覆蓋滿螢幕了。

"找不同"小遊戲

     我們可以做一片陰影, 但是其中某個我們單獨做一個樣式進行覆蓋, 考考大家的眼力, 就如圖例所示:

image.png

     這裡就是利用drop-shadow產生陰影, 然後再進行一點修改, 正確答案在這裡:

image.png

     所以只要再寫兩段程式碼, 就可以讓這個8x8 變成16x16那麼多, 應該還挺好玩的。

image.png

六、drop-shadow 與 box-shadow的聯合

     drop-shadowbox-shadow 都有投射的能力, 那麼他兩個屬性共同作用於一個元素會是怎樣的:
image.png

     box-shadow會基於drop-shadow屬性產生的全部投影進行透射陰影, 第一排是drop-shadow的投影, 第二排是box-shadow的投影, 具體怎麼玩我還沒想到太適合的。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
        #wrap {
            position: relative;
            height: 350px;
            width: 500px;
            margin: 50px auto;
            overflow: hidden;
        }

        #n {
            border: 1px solid gray;
            width: 50px;
            box-shadow: 0 200px;
            transform: rotate(10deg);
            filter: drop-shadow(70px 0) drop-shadow(140px 0px);
        }
    </style>
</head>

<body>
    <div id="wrap">
        <img id="n" src="./svg/人.svg" /
    </div>
</body>

</html>

七、drop-shadow 複製後的'運動'

     既然可以投射出那麼多投影, 那麼如果我元素進行旋轉的話, 投影是否也會進行旋轉? 並且它是以什麼規律運動的那?

     下面演示的是, 物體投影 + 物體本身旋轉:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
        #wrap {
            position: relative;
            height: 350px;
            width: 400px;
            margin: 250px auto;
        }

        #n {
            width: 20px;
            filter: drop-shadow(25px 0) drop-shadow(50px 0px) drop-shadow(100px 0);
            animation: rr 2s linear infinite;
        }

        @keyframes rr {
            0% {
                transform: rotate(0);
            }

            100% {
                transform: rotate(360deg);
            }
        }
    </style>
</head>

<body>
    <div id="wrap">
        <img id="n" src="./svg/人.svg" />
    </div>
</body>

</html>

     上面是整體以'元素'本身為旋轉點進行旋轉, 那要如何讓'元素'的每個投影都以自身為原點旋轉那?

     這裡的思路就是, 在img外包裹一層div, 我們對外層div進行投影, 內部的img負責旋轉:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
        #wrap {
            position: relative;
            height: 350px;
            width: 400px;
            margin: 250px auto;
        }

        #box {
            width: 20px;
            height: 20px;
            filter: drop-shadow(25px 0) drop-shadow(50px 0px) drop-shadow(100px 0);
        }

        #n {
            width: 20px;
            animation: rr 2s linear infinite;
        }

        @keyframes rr {
            0% {
                transform: rotate(0);
            }

            100% {
                transform: rotate(360deg);
            }
        }
    </style>
</head>

<body>
    <div id="wrap">
        <div id="box">
            <img id="n" src="./svg/人.svg" />
        </div>
    </div>
</body>

</html>

八、filter屬性著色(svg + png)圖片

     改變svg顏色最直接的方法就是改其本身的fill屬性, 這裡不做探討, 這裡要研究的是到底為什麼filter可以改變圖片的顏色, 是什麼原理? 這裡我們就一起探究一下(這裡只討論純色圖片)。

輪廓的形成

     並不是所有的圖片被賦予drop-shadow屬性後都會呈現出物體的輪廓, 投影會忽略透明背景的地方, 所以png這種可以定義透明背景的圖片才可以被投射出相應的輪廓而不是矩形輪廓, svg同理。

     比如jpg圖片無法設定透明的背景, 所以其投影效果就與box-shadow相同了。

svg + png 投影變色

     我們可以利用drop-shadow製作一個指定顏色的投影, 然後只要將元素本身隱藏, 只留下投影就ok了。

image.png

image.png

image.png

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
        #box2 {
            display: inline-block;
            overflow: hidden;
        }

        #glasses2 {
            filter: drop-shadow(200px 0 red);
            transform: translateX(-200px);
        }
    </style>
</head>

<body>
    <div>
        <div id="box2">
            <img id="glasses2" src="./img/太陽鏡.png" />
        </div>
    </div>
</body>
</html>
顏色的疊加變色有點強!

     有沒有辦法是直接改變元素本身的顏色? 我嘗試將drop-shadow投射在自身位置, 但是投射的陰影永遠在元素後面, 我嘗試將元素的opacity改小, 陰影也會隨之變小, 如果設定opacity:0則投影也不可見了。

     不管什麼顏色無非是三原色合成的顏色, filter屬性可以定義那麼多種濾鏡, 那是不是代表著某些濾鏡效果的疊加態就是我們想要的目標顏色:

image.png

     手動生成那麼多的屬性不現實, 順著這個思路我找到了一個真的這樣做的網站:

為圖片混合調色官網

image.png

  1. 需要多點幾次Compute Filters按鈕, 直到生成差異度較小的屬性。
  2. 如果我們的元素不是純黑色, 需要先賦予 filter: brightness(0) saturate(100%) 將其變為純黑, 因為不同的底色需要變成目標顏色的filter屬性不同。
  3. 當然啦這裡屬於頭腦一波, 實際專案中不會這樣去做的。

九、區域性清晰

     這裡所謂的區域性清晰可以想象為, 某張圖全部都是模糊的, 但是我們把一個放大鏡放在某處, 此處就會變得清晰, 先看我做的效果:

image.png

     這裡的原理是這樣的, 一共兩層, 下層是模糊濾鏡的圖片,上層是一個圓形的div, 並且這個div的背景圖是圖片的清晰版, 設定background-position, 在拖動div的同時, 實時變換背景的background-position位置, 就實現了圖裡的效果。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
  <style>
    * {
      box-sizing: border-box;
    }

    #wrap {
      position: relative;
    }

    #acc {
      position: absolute;
      left: 0;
      right: 0;
      width: 540px;
      filter: blur(7px);
      pointer-events: none;
    }

    #mk {
      z-index: 2;
      height: 100px;
      width: 100px;
      border-radius: 50%;
      overflow: hidden;
      border: 1px solid blue;
      position: absolute;
      left: 0;
      top: 0;
      background-image: url("./img/利姆露jpeg.jpeg");
      background-size: 540px 562px;
      background-position: 0 0;
      background-repeat: no-repeat;
    }
  </style>
</head>

<body>
  <div id="wrap">
    <div id="mk"></div>
    <img id="acc" src="./img/利姆露jpeg.jpeg" />
  </div>
  <script>
    function drag(elementId) {
      const element = document.getElementById(elementId);
      const position = {
        offsetX: 0,
        offsetY: 0,
        state: 0,
      }
      function getEvent(event) {
        return event || window.event;
      }
      element.addEventListener(
        "mousedown",
        function (event) {
          var e = getEvent(event);
          position.offsetX = e.offsetX;
          position.offsetY = e.offsetY;
          position.state = 1;
        },
        false
      );
      document.addEventListener(
        "mousemove",
        function (event) {
          var e = getEvent(event);
          if (position.state) {
            position.endX = e.clientX;
            position.endY = e.clientY;
            element.style.top = position.endY - position.offsetY + "px";
            element.style.left = position.endX - position.offsetX + "px";
            element.style.backgroundPositionX = "-" + element.style.left;
            element.style.backgroundPositionY = "-" + element.style.top;
          }
        },
        false
      );
      element.addEventListener(
        "mouseup",
        function (event) {
          position.state = 0;
        },
        false
      );
    }
    drag("mk");
  </script>
</body>

</html>

end

     這次就是這樣, 希望與你一起進步。

相關文章