こちらは友利奈緒 Advent Calendar 2016 - Adventar 17日目の記事です。
TomoriNao
昨年のこの時期、ふとした思いつきからTeam TomoriNaoというホワイトハッカー(死語?)チームが生まれました。 皆さんもご覧に入れたことがあると思いますが、トップの画像のようなチームロゴを掲げています。 このトップの画像はロゴとして用意した公式のものではなく、Team TomoriNaoのWebサイトのスクリーンキャプチャです。
Team TomoriNaoのWebサイトは、JavaScript を使用せずにいろいろなアニメーションやアクションを実装しているのをご存知ですか? このちょっとした仕掛けを今回は紹介していきたいと思います。
ロゴのアニメーション
文字列の縁取り
まずサイトを開くと現れるロゴのアニメーション。 彗星の輝きが表現されていた今までに加え、1周年を記念してロゴの描画アニメーションが増えています。
ロゴの描画アニメーションはもちろんJavaScriptを用いず、CSS3の keyframe によって実現されています。
CSS3のSVGアニメーションで制御できるものは限られているものの1、Pathのライン取りのアニメーションは容易に実現できます。
ロゴの縁取りを行うアニメーションは、stroke-dasharray
とstroke-dashoffset
属性によって実現できます。
stroke-dasharray
はパスのストロークを破線として表示するときの間隔を指定し、storke-dashoffset
は破線の開始位置を指定します。
ちょうどパスストロークの長さ分をstroke-dasharray
として破線の間隔を指定し、
その破線の開始位置をstorke-dashoffset
で指定することで、縁取りのされていない透明な文字が表現できます。
そこから keyframe によるアニメーションでstorke-dashoffset
を0に近づけていくと、文字列の縁取りが実現できます。
TomoriNao という文字列はそれぞれ独立したパスでできており、パスの大きさが違うが故にパスストロークの長さも違います。 一文字ずつパスの長さを測り、以下のように指定しました。
svg #TomoriNao path {
fill: none;
stroke-linecap: round;
animation: dash 1.6s ease-in 0.4s forwards/*, color 1s ease-in 2.2s forwards*/;
}
svg #TomoriNao path[name=T] {
stroke-dasharray: 290;
stroke-dashoffset: 290;
}
svg #TomoriNao path[name=o] {
stroke-dasharray: 216;
stroke-dashoffset: 216;
}
/* more */
@keyframes dash {
100% { stroke-dashoffset: 0; }
}
@keyframes color {
0% { fill: rgba(0, 0, 0, 0); }
100% { fill: rgba(0, 0, 0, 1); }
}
これを適用すると、ページがロードされてから0.4秒後に1.6秒間かけてすべての文字が書き上がる表現ができます。 以下はデモのためにループやタイミング関数が調整されていますが、だいたい上記のコードでこのような表示になります。
ちなみにTomoriNaoの文字は、Orbitronをベースに変更を加えたものです。
彗星の輝き
こちらはSVGアニメーションのSMILで書かれています。
SVG animation with SMIL - SVG | MDN
輝きを表現する中心が輝いた円形グラデーションを塗りつぶしとして用意し、それを移動させたりグラデーション半径を拡大縮小して彗星が輝いているように見せています。
まず、SVGの<radialGradient>
要素で円形グラデーションを定義し、中心を輝きとして#DDDDEEの色で、外側に向かってチームカラーの#14A6C8を指定します。
そして、<radialGradient>
要素内で、<animate>
要素によってグラデーションの中心位置とグラデーション半径を、アニメーションしています。
<radialGradient id="comet" cx="0" cy="0" r="0.1">
<stop offset="0%" stop-color="#DDDDEE"/>
<stop offset="100%" stop-color="#14A6C8"/>
<animate attributeName="cx" values="0; 0; 0.02; 0.28; 0.6; 0.70; 0.88; 0.92; 0.88; 0.82; 0.62; 0.50; 0.34" dur="5s" restart="always" repeatCount="indefinite" calcMode="linear" keyTimes="0;0.9;0.9;0.926;0.933;0.942;0.956;0.965;0.970;0.975;0.98;0.99;1"/>
<animate attributeName="cy" values="0; 0; 0.20; 0.13; 0.1; 0.11; 0.20; 0.30; 0.40; 0.50; 0.70; 0.80; 0.92" dur="5s" restart="always" repeatCount="indefinite" calcMode="linear" keyTimes="0;0.9;0.9;0.926;0.933;0.943;0.956;0.965;0.970;0.975;0.98;0.99;1"/>
<animate attributeName="r" values="0.1; 0.1; 0.20; 0.30; 0.40;" dur="5" restart="always" repeatCount="indefinite" calcMode="linear" keyTimes="0;0.9;0.9;0.99;1"/>
</radialGradient>
ロゴでは見えない部分をわかりやすく表したデモが以下のようになっています。
なぜCSS3ではなくSMILを使ったかというと、このグラデーションのパラメータがCSS3では変更できなかったからという理由があります。
サイトのフェードイン
これもCSS3 keyframeアニメーションで実現されています。 ロゴが表示される最初のセクション意外に対して、最初は透明にしておき、ロゴ表示のタイミングで不透明度を変更するという形でフェードインをしています。
body > section:not(:first-of-type) {
opacity: 0;
animation: visible 1s ease-in 2s forwards;
}
@keyframes visible {
100% { opacity: 1; }
}
More… ボタン
NEWSセクションにMoreボタンがありますが、このボタンによって追加の項目を表示する部分も、JavaScriptなしで実現しています。
More…ボタンの正体は、実は チェックボックス で、チェックされた状態を示すCSS3の擬似セレクタである:checked
で表示を切り替えています。
<input type="checkbox" name="show_more">
として用意したチェックボックスを非表示にし、その隣に<label>
要素をfor属性で関連付けして表示してあります。
<label>
は、クリックが可能であることを示すよう、cursor: pointer;
が指定されています。
チェックされいないチェックボックスの後に続く要素を非表示にするため、:not
擬似セレクタと後続を示すセレクタ ~
を組み合わせています。
その<label>
をクリックすると、チェックボックスがチェックされ、非表示が解除されると同時に、CSS3 transitionとtransformの組み合わせによって、
横から要素がスライドして表示されるようになっています。
[name=show_more] {
visibility: hidden;
}
[for=show_more] {
cursor: pointer;
}
[name=show_more]:checked, [name=show_more]:checked ~ label {
display: none;
}
[name=show_more]:not(:checked) ~ div {
visibility: hidden;
height: 0;
width: 0;
}
[name=show_more]:checked ~ div {
visibility: visible;
height: auto;
width: auto;
overflow: hidden;
}
[name=show_more]:not(:checked) ~ div * {
transform: translateX(300px);
opacity: 0;
}
[name=show_more]:checked ~ div * {
transform: translateX(0px);
opacity: 1;
transition-property: transform, opacity;
transition-duration: 0.5s;
transition-timing-function: ease;
}
まとめ
悪意のある広告にJavaScriptが利用されていたり、JavaScriptが複雑化しページの表示が重くなる一方の2016年。 2017年はNo-JSが叫ばれ、JavaScriptなしで如何にきれいにサイトを表現するかを考えさせられるかもしれません。 また、きれいな表示にかかせないアニメーションにJavaScriptを用いる時代ではなくなっています。 みなさんの管理するWebサイトでまさかjQueryをつかったアニメーションなどは利用していないと思いますが、 一度、CSS3やSMILでNo-JSなアニメーションを真剣に考えてみてはいかがでしょうか。