CSSCSSのみで行う背景アニメーション
animation
backgrund-image
circle
ellipse
radial-gradient
repeating-radial-gradient
あるときデザイナーから、
「水彩のような背景をアニメーションで動かすことはできますか?」
と聞かれて、
「その表現方法については WebGL じゃないと難しいかも」
と返した後、本当にそれじゃないとできない?と思い立ったのがきっかけで調べ始めました。
今回期待される水彩の背景とは
右記下記のような、淡い感じの画像になります。
このような背景画像がアニメーションで動き続けてほしいとのことで、
WebGLではない代替手段を探します。
目から鱗の背景イメージ用関数
背景画像には、画像だけでなく画像関数というものが使えます。
知ってはいたけど忘れていたのは、画像関数の重ねがけでした。
linear-gradientを襲がけることで、市松模様ができるならば、
水彩に 近い 表現はできるのではないかということで検証していきます。
radial-gradient, repeating-radial-gradient関数
円形または楕円形の決まった形しか選ぶことはできないけれども、
大きさも変更できるし、色の濃度も変更できるこの関数を重ねがけしていきます。
とりあえず下記の3色を重ねがけしてみます。
radial-gradient(ellipse farthest-corner at 20% 20%, rgba(255, 0, 0, .25), transparent 50% 150%)
radial-gradient(ellipse farthest-corner at 70% 50%, rgba(0, 255, 0, .25), transparent 50% 150%)
radial-gradient(ellipse farthest-corner at 40% 75%, rgba(0, 0, 255, .25), transparent 50% 150%)
重ねがけした結果が下記になります。
なんかっぽくなりました。
.c-article__demo._1 {
height: 30.0rem;
background-image: radial-gradient(ellipse farthest-corner at 20% 20%, rgba(255, 0, 0, .25), transparent 50% 100%),
radial-gradient(ellipse farthest-corner at 70% 50%, rgba(0, 255, 0, .25), transparent 50% 100%),
radial-gradient(ellipse farthest-corner at 40% 75%, rgba(0, 0, 255, .25), transparent 50% 100%);
}
アニメーション(色を動かしてみる)
要望としてあった「動いてほしい」という要望を検証していきたいと思います
cssアニメーションなので、
animationプロパティを使い、@keyframeで動かしていきたいと思います。
赤は右下方向に行って帰ってくる
緑は左に行って戻ってくる
青は上に行って戻ってくる
上記の動きを繰り返し動くように設定していきたいと思います。
.c-article__demo._animation1 {
animation: move 2s linear infinite;
}
@keyframes move {
0% {
background-image: radial-gradient(ellipse farthest-corner at 20% 20%, rgba(255, 0, 0, .25), transparent 50% 100%),
radial-gradient(ellipse farthest-corner at 70% 50%, rgba(0, 255, 0, .25), transparent 50% 100%),
radial-gradient(ellipse farthest-corner at 40% 75%, rgba(0, 0, 255, .25), transparent 50% 100%);
}
50% {
background-image: repeating-radial-gradient(ellipse farthest-corner at 75% 75%, rgba(255, 0, 0, .25), transparent 50% 100%),
radial-gradient(ellipse farthest-corner at 15% 50%, rgba(0, 255, 0, .25), transparent 50% 100%),
radial-gradient(ellipse farthest-corner at 40% 20%, rgba(0, 0, 255, .25), transparent 50% 100%);
}
100% {
background-image: radial-gradient(ellipse farthest-corner at 20% 20%, rgba(255, 0, 0, .25), transparent 50% 100%),
radial-gradient(ellipse farthest-corner at 70% 50%, rgba(0, 255, 0, .25), transparent 50% 100%),
radial-gradient(ellipse farthest-corner at 40% 75%, rgba(0, 0, 255, .25), transparent 50% 100%);
}
}
なんか思ったような動きではないですね。
tween(それぞれの色が移動)するのを期待していましたが、
切り替わるように移動するのをみる限りでは、
radial-gradient の () 内に設定する引数はアニメーションの対象数値ではないことがわかります。
そもそも、background-image プロパティがアニメーションに対応できないようです。
アニメーションをどうにかさせる方法
background-image が アニメーションに対応していない(radial-gradient の () 内に設定する引数)ならば、
アニメーションさせられるプロパティを動かす方法に行きたいと思います。
アニメーションさせられるプロパティ
数ある中で今回使用するにあたって下記のプロパティが使用できるかと思います。
- background-position
- transform
- top, left, right, bottom
アニメーションのためにHTMLを変更
上記のプロパティで動かすと全てが同じ動きになってしまうので、
各色ごとにレイヤーを分けて各レイヤーを動かすことにします。
一つの背景として設定しているわけではなく、レイヤー分けしているので、若干ですが、色の混じり合いというものが違う気がします。
<div class="c-article__demo _2" aria-hidden="true">
<div class="c-article__demo-layer _red"></div>
<div class="c-article__demo-layer _green"></div>
<div class="c-article__demo-layer _blue"></div>
</div>
.c-article__demo._2 {
position: relative;
height: 30rem;
}
.c-article__demo-layer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-size: 200% 200%;
background-position: 50% 50%;
}
.c-article__demo-layer._red {
background-image: radial-gradient(ellipse farthest-corner at 35% 35%, rgba(255, 0, 0, .25), transparent 25% 100%);
}
.c-article__demo-layer._green {
background-image: radial-gradient(ellipse farthest-corner at 60% 50%, rgba(0, 255, 0, .25), transparent 25% 100%);
}
.c-article__demo-layer._blue {
background-image: radial-gradient(ellipse farthest-corner at 45% 62.5%, rgba(0, 0, 255, .25), transparent 25% 100%);
}
各レイヤーをアニメーションさせる
上記のもので、レイヤーを描くレイヤーごとも元々想定していた動きを設定してみます。
._animate2 .c-article__demo-layer._red {
animation: moveRed 10s linear infinite;
}
._animate2 .c-article__demo-layer._green {
animation: moveGreen 13s linear 3s infinite;
}
._animate2 .c-article__demo-layer._blue {
animation: moveBlue 7s linear 7s infinite;
}
@keyframes moveRed {
0% {background-position: 50% 50%}
50% {background-position: -20% -20%}
100% {background-position: 50% 50%}
}
@keyframes moveGreen {
0% {background-position: 50% 50%}
50% {background-position: 75% 50%}
100% {background-position: 50% 50%}
}
@keyframes moveBlue {
0% {background-position: 50% 50%}
50% {background-position: 50% 80%}
100% {background-position: 50% 50%}
}
それっぽくなってきました。
これに背景色を加え、赤・緑・青を同系色の色に調整していきます。
色の調整
元々の水彩画像は黄色ベースだったので、これに寄せていきたいと思います。
使用する画像は、黄色、オレンジ、ピンク、黄緑あたりで変更していきます。
.c-article__demo._3 {
position: relative;
height: 30rem;
background-image: repeating-radial-gradient(ellipse farthest-corner at 35% 35%, rgba(255, 255, 0, .05), rgba(255, 255, 0, .01) 25%, rgba(255, 255, 0, .05) 35%),
repeating-radial-gradient(circle farthest-corner at 50% 50%, rgba(255, 255, 0, .01), rgba(255, 255, 0, .005) 25%, rgba(255, 255, 0, .01) 35%),
repeating-radial-gradient(ellipse farthest-corner at 85% 85%, rgba(255, 255, 0, .05), rgba(255, 255, 0, .01) 25%, rgba(255, 255, 0, .05) 35%);
}
._animate3 .c-article__demo-layer._orange {
background-image: radial-gradient(ellipse farthest-corner at 35% 35%, rgba(255, 165, 0, .25), transparent 25% 100%);
animation: moveOrnage 20s linear infinite;
}
._animate3 .c-article__demo-layer._pink {
background-image: radial-gradient(ellipse farthest-corner at 60% 50%, rgba(255, 192, 203, .25), transparent 25% 100%);
animation: movePink 26s linear 6s infinite;
}
._animate3 .c-article__demo-layer._lightgreen {
background-image: radial-gradient(ellipse farthest-corner at 45% 62.5%, rgba(144, 238, 144, .25), transparent 25% 100%);
animation: moveLightgreen 14s linear 14s infinite;
}
@keyframes moveOrnage {
0% {background-position: 50% 50%}
50% {background-position: -20% -20%}
100% {background-position: 50% 50%}
}
@keyframes movePink {
0% {background-position: 50% 50%}
50% {background-position: 75% 50%}
100% {background-position: 50% 50%}
}
@keyframes moveLightgreen {
0% {background-position: 50% 50%}
50% {background-position: 50% 80%}
100% {background-position: 50% 50%}
}
レイヤーの数を増やす
若干隙間が目立つので、レイヤーを増やしていきます。
追加3レイヤーに対するcss
._animate3 .c-article__demo-layer._orange2 {
background-image: radial-gradient(ellipse farthest-corner at 70% 35%, rgba(255, 165, 0, .25), transparent 25% 100%);
animation: moveOrnage2 25s linear infinite;
}
@keyframes moveOrnage2 {
0% {background-position: 50% 50%}
33% {background-position: 0% 30%}
66% {background-position: 80% 0%}
100% {background-position: 50% 50%}
}
._animate3 .c-article__demo-layer._pink2 {
background-image: radial-gradient(ellipse farthest-corner at 60% 80%, rgba(255, 192, 203, .25), transparent 25% 100%);
animation: movePink2 20s linear 6s infinite;
}
@keyframes movePink2 {
0% {background-position: 50% 50%}
33% {background-position: 0% 60%}
66% {background-position: 90% 10%}
100% {background-position: 50% 50%}
}
._animate3 .c-article__demo-layer._lightgreen2 {
background-image: radial-gradient(ellipse farthest-corner at 15% 55%, rgba(144, 238, 144, .25), transparent 25% 100%);
animation: moveLightgreen2 14s linear 5s infinite;
}
@keyframes moveLightgreen2 {
0% {background-position: 50% 50%}
33% {background-position: 70% 40%}
66% {background-position: 0% 20%}
100% {background-position: 50% 50%}
}
まとめ
水彩画のような背景を実装するということでしたが、
トータルのコード(css記述)量を見ると、結構な量が発生していました。
そして、この表現を調整したいとなったときの保守性を考えると、
CSSの部分をJSで管理するか、
全く違う実装であるcanvas要素を使っての実装の方が、
見通しも、保守性も高くなるのではないかと思います。
CSSでやろうとすると、
とにかく色味、アニメーションのランダム性を表現するのに苦労しそうです。
最終的な表現のHTML, CSSを右記下記に記述しておきます。
<div class="c-article__demo _3 _animate3" aria-hidden="true">
<div class="c-article__demo-layer _orange"></div>
<div class="c-article__demo-layer _orange2"></div>
<div class="c-article__demo-layer _pink"></div>
<div class="c-article__demo-layer _pink2"></div>
<div class="c-article__demo-layer _lightgreen"></div>
<div class="c-article__demo-layer _lightgreen2"></div>
</div>
.c-article__demo._3 {
position: relative;
height: 30rem;
background-image: repeating-radial-gradient(ellipse farthest-corner at 35% 35%, rgba(255, 255, 0, .05), rgba(255, 255, 0, .01) 25%, rgba(255, 255, 0, .05) 35%),
repeating-radial-gradient(circle farthest-corner at 50% 50%, rgba(255, 255, 0, .01), rgba(255, 255, 0, .005) 25%, rgba(255, 255, 0, .01) 35%),
repeating-radial-gradient(ellipse farthest-corner at 85% 85%, rgba(255, 255, 0, .05), rgba(255, 255, 0, .01) 25%, rgba(255, 255, 0, .05) 35%);
}
._animate3 .c-article__demo-layer._orange {
background-image: radial-gradient(ellipse farthest-corner at 35% 35%, rgba(255, 165, 0, .25), transparent 25% 100%);
animation: moveOrnage 20s linear infinite;
}
@keyframes moveOrnage {
0% {background-position: 50% 50%}
50% {background-position: -20% -20%}
100% {background-position: 50% 50%}
}
._animate3 .c-article__demo-layer._pink {
background-image: radial-gradient(ellipse farthest-corner at 60% 50%, rgba(255, 192, 203, .25), transparent 25% 100%);
animation: movePink 26s linear 6s infinite;
}
@keyframes movePink {
0% {background-position: 50% 50%}
50% {background-position: 75% 50%}
100% {background-position: 50% 50%}
}
._animate3 .c-article__demo-layer._lightgreen {
background-image: radial-gradient(ellipse farthest-corner at 45% 62.5%, rgba(144, 238, 144, .25), transparent 25% 100%);
animation: moveLightgreen 14s linear 14s infinite;
}
@keyframes moveLightgreen {
0% {background-position: 50% 50%}
50% {background-position: 50% 80%}
100% {background-position: 50% 50%}
}
._animate3 .c-article__demo-layer._orange2 {
background-image: radial-gradient(ellipse farthest-corner at 70% 35%, rgba(255, 165, 0, .25), transparent 25% 100%);
animation: moveOrnage2 25s linear infinite;
}
@keyframes moveOrnage2 {
0% {background-position: 50% 50%}
33% {background-position: 0% 30%}
66% {background-position: 80% 0%}
100% {background-position: 50% 50%}
}
._animate3 .c-article__demo-layer._pink2 {
background-image: radial-gradient(ellipse farthest-corner at 60% 80%, rgba(255, 192, 203, .25), transparent 25% 100%);
animation: movePink2 20s linear 6s infinite;
}
@keyframes movePink2 {
0% {background-position: 50% 50%}
33% {background-position: 0% 60%}
66% {background-position: 90% 10%}
100% {background-position: 50% 50%}
}
._animate3 .c-article__demo-layer._lightgreen2 {
background-image: radial-gradient(ellipse farthest-corner at 15% 55%, rgba(144, 238, 144, .25), transparent 25% 100%);
animation: moveLightgreen2 14s linear 5s infinite;
}
@keyframes moveLightgreen2 {
0% {background-position: 50% 50%}
33% {background-position: 70% 40%}
66% {background-position: 0% 20%}
100% {background-position: 50% 50%}
}