CSSだけで簡単にグリッチエフェクト風アニメーションを実装する

# 目標: “なんちゃって”グリッチエフェクトを楽に実装する

グリッチエフェクトとは、ノイズや歪みなどの"画面の乱れ"の効果のことです。
壊れかけのブラウン管テレビのような、ちょっと不気味な雰囲気を醸し出したいときに用いられます。
動画においては、Adobe PremiereやAfter Effectsで割と手軽にエフェクトをつけられるので、最近はYouTube等でもよく見かけます。

このエフェクトをWebページでもやりたかったのですが、本当に使うか分からずとりあえずで入れてみたかったという経緯から、簡単になんちゃってグリッチエフェクトを実装してみました。

「なんちゃって」の意味合いとしては、本当ならRGBを分けたレイヤー要素を重ねたり、ノイズをのせたりするのが本来のグリッチなのでしょうが、純粋に大変なので**それっぽく見せることを目標**としている、と理解いただければ幸いです。

# 実装

それっぽくでいいので、ノイズをのせたり要素を重ねたりなどは（面倒なので）せず、簡単なCSSのプロパティのみで実現していきます。
また、テキスト・画像、いずれにも簡単にエフェクトを適用できるように作ります。

なんちゃってグリッチのために使うプロパティと用途を以下の通り考えてみました。

- ```opacity```でチカチカさせる
- ```transform: skew()```で歪んでいるかのように見せる
- ```transform: translate()```でブレ感を出す
- ```filter: invert(100%)```でネガポジ反転させる（IE非対応）

これらを、いい感じにキーフレームを打って配置していきます。
歪み具合や移動具合は好みに応じて微調整してください。

以下は、10秒でループさせる想定のキーフレームです。つまり、0.1% = 0.1秒になります。
動画制作時は、秒ではなくフレームを用いることが多いので少し違和感を感じる方もいらっしゃるかもしれませんが、とりあえず分かりやすいのでこれでいきます。
（秒ではなく30fpsとかでフレーム割った方が実はキレイだったりするのでしょうか。未検証なので今後調査・確認してみたいと思います）

```css
@keyframes glitch_effect {
  0% {
    opacity: 1;
    transform: none;
  }
  33% {
    transform: none;
    filter: invert(0%);
  }
  33.3% {
    opacity: 1;
    transform: skewY(20deg) translate(8px, 6px);
    filter: invert(100%);
  }
  33.6% {
    opacity: 0;
    transform: skewY(-10deg) translate(-10px, -10px);
  }
  33.9% {
    opacity: 1;
    transform: none;
    filter: invert(0%);
  }
  41% {
    opacity: 1;
  }
  41.4% {
    opacity: 0;
  }
  41.8% {
    opacity: 1;
  }
  42.2% {
    opacity: 0;
  }
  42.6% {
    opacity: 1;
  }
  66% {
    transform: none;
    filter: invert(0%);
  }
  66.3% {
    opacity: 1;
    transform: skewX(5deg) translate(-4px, 10px);
    filter: invert(100%);
  }
  66.6% {
    opacity: 0;
    transform: skewX(-7deg) translate(4px, -6px);
  }
  66.9% {
    opacity: 1;
    transform: none;
  }
  77% {
    transform: none;
  }
  77.3% {
    transform: skewY(10deg) translate(10px, -6px);
  }
  77.6% {
    transform: skewY(-22deg) translate(-5px, 8px);
    filter: invert(100%);
  }
  77.9% {
    transform: none;
    filter: invert(0%);
  }
  100% {
    opacity: 1;
    transform: none;
    filter: invert(0%);
  }
}
```

これでキーフレームは完成したので、これを適用させます。

要素に`glitch`というクラスを付与すれば、グリッチエフェクト風がかかるようにします。

```html
<div class="img_container glitch">
  <img src="sample.jpg">
</div>

<div class="txt_container glitch">
  <span>Sample</span>
  <span>サンプル</span>
</div>
```

```css
.glitch {
  animation: glitch_effect 10s infinite;
}

.txt_container > span {
  background: #fff;
}
```

テキストに関しては、`span`で囲み白い座布団を敷いています。
これは、文字色が**黒**でページ全体の背景が**白**だった場合、文字色のみネガポジ反転するため、文字は**白**、ページの背景は**白**のままとなってしまい、文字の可読性どころか**可視性さえも失われてしまう**ことを防ぐためです。デザイン的に座布団を良しとするかは状況に応じて使い分けてください。

実際に実装した結果はこちらです。

<p class="codepen" data-height="300" data-theme-id="dark" data-default-tab="result" data-slug-hash="wveXYBd" data-user="heeroo-ymsw" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/heeroo-ymsw/pen/wveXYBd">
  Glitch Effect</a> by heeroo-ymsw (<a href="https://codepen.io/heeroo-ymsw">@heeroo-ymsw</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

チカチカが目に悪い気もしますが、それっぽい感じにはなったと思います。

# まとめ

今回は、馴染みのあるCSSプロパティを用いて、簡単にグリッチエフェクト風のアニメーションを実装してみました。

この度制作していたページに関しては、ネガポジ反転がうまく映えるサイトでしたので非常に有効な手段だったのではないかと自負しています。

~~こんな偽物じゃなくて、~~もっと本格的なグリッチエフェクトを再現したい方は、ググっていただくと、疑似要素等を用いてレイヤーを重ねて再現されているものなどが見つかると思いますので、ぜひそちらを参考になさってください。

# 余談: `top`, `right`, `bottom`, `left`ではなく`translate()`を用いた理由

全くもって余談ではありますが、ざっくり備忘録的に残します。

今回、アニメーションでは`transform: translate()`を用いました。
しかし、要素を動かすのであれば、`position: absolute`で`top`や`left`で位置を指定する方法もあります。

やっていることは同じですが、両者の違いはその処理方法にあります。

`top`や`left`はCPUで処理され、要素の位置が変更される度、**ペイント処理**が行われるのに対し、
`translate()`はGPUで処理され、要素の**レイヤーを移動し合成**することでレンダリングされます。

要素の描画にはいくつか手順を踏みますが、positionやmargin、width、heightに変更がかかると、その描画手順の中でもカロリーの高い処理が再度走ることになります。(リフロー・リペイント)
そこで`transform`を用いることで、このカロリー高めな処理をすっ飛ばして、レイヤー合成処理のみで描画を完了できるので、パフォーマンスが向上する、ということになります。

その他パフォーマンス関連だと、`will-change`や`transform: translateZ(0)`などもありますが、またそれは別のお話し。