弊社にて現在開発中のWordPressテーマに搭載する機能の候補として、各セクションの境界の形状に、波形の波打つアニメーション要素を表示する仕組みを簡単に作ってみました。
想定は、例えば弊社で提供中のDigiPressテーマに搭載の「パララックススクロール」ウィジェットで表示できるコンテンツの境界デザインとして表示することを考慮し、アニメーションさせる canvas要素は1ページ内に決められた数ではなく複数存在することを前提とします。
セクションの境界は、HTML5 canvas に対して、JavaScript にて sin関数で波形を描画し、setTimeout
関数で位置をずらしながらキャンバスを移動させます。
境界アニメーション要素の条件
- 波形を描画するcanvas要素の数は不定のため、すべてのcanvas要素について波形の描画とアニメーションを実行する。
- canvas の描画は高精細ディスプレイに対応させる。
- 波形は1つの境界要素(canvas)に最大3つまで重ね合わせることができ、それぞれ波形の色を指定できる(アルファ値有効)。
- 汎用性の観点から、波形の色はdata属性に指定し、JavaScript側でそれを取得して動的に塗りつぶす。
- 1つの境界要素(canvas)に波形が1つだけの場合は、波形の色のアルファ値は無効にする。
完成イメージがこちら。
See the Pen Wave animation with canvas (Retina ready) by digistate (@digistate) on CodePen.
INDEX
HTMLの構成
波形の描画だけなら canvas
タグのみでできますが、テキストや画像があるコンテンツ要素の境界として扱うため、ラッパー要素(.canvas-wrapper
)でセクション全体を括り、その中に canvas(.wave-bar
)とメインのコンテンツ(.inner
)を配置します。
波形のみを表示する場合
<div class="canvas-wrapper"> <canvas class="wave-bar" data-color1="#C8D32F"></canvas> </div>
上部境界に波形を表示し、メインコンテンツの背景と同化させる場合
<div class="canvas-wrapper"> <canvas class="wave-bar" data-color1="#43c0e4" data-color2="#43c0e4" data-color3="#43c0e4"></canvas> <div class="inner" data-bgcolor="#7DD1EF" data-color="#fff">メインコンテンツ</div> </div>
波形は3つまで表示可能で、波形の色は、canvas要素の data-color1
, data-color2
, data-color3
属性で指定します。
この data-color[n] の数が波形の数に一致します。
境界の波形(canvas
)とメインコンテンツ(.inner
)が同化して見えるようにするために、メインコンテンツ要素には data-bgcolor
で背景カラー、data-color
でフォントカラーを指定できるようにしておきます。
これらのカラーコードを、Javascript側で取得してメインコンテンツのスタイルに反映させます。
上下の境界に波形アニメーションを表示する場合
<div class="canvas-wrapper"> <canvas class="wave-bar" data-color1="#96D601" data-color2="#00BA89"></canvas> <canvas class="wave-bar flipped" data-color1="#96D601" data-color2="#00BA89"></canvas> </div>
上下の境界に波形を表示する場合は、canvas要素を2つ指定し、2つ目のセレクタに .flipped
を加えます。
.flipped
セレクタがある場合は、単純にCSSで要素を反転させて表示するようにしています。
JavaScriptによるcanvasの描画
canvasの波形の描画処理については、今回参考にしたドキュメントに詳しく解説されているため割愛します。
ここでは、今回のサンプルで独自に追加した必要な処理について記載しておきます。
波形用canvas要素の取得、初期化
// .wave-bar セレクタを持つ要素をすべて取得 canvasList = document.querySelectorAll('.wave-bar'); // 各.wave-bar要素について順番に初期化 Array.prototype.forEach.call(canvasList, function(canvas, index){ // 初期化の処理 } );
document.querySelectorAll
でセレクタを指定して、波形アニメーションを表示する対象要素をすべて取得し、Array.prototype.forEach.call
という書式で、取得した canvasList
オブジェクトをforeachのように順番に処理します。
canvasの描画を高精細ディスプレイに対応させる
canvasに幅と高さを表示したいサイズの数値でそのまま指定すると、Retinaディスプレイのような高精細ディスプレイでは、2倍以上の解像度が必要となるため、画像(img)と同じように描画が粗くなります。
これを解決するためには、まずは通常通りcanvas
に表示したいサイズをセットします。
そのサイズに devicePixelRatio
という、表示されるディスプレイのピクセル比を表すプロパティの数値を掛け、適切な解像度にリサイズ(拡大)します。
最後に、必要な解像度までリサイズ(拡大)したcanvas要素を、devicePixelRatio
分縮小します。
// 幅はブラウザの表示幅と同じにする canvas.width = document.documentElement.clientWidth; // devicePixelRatio分、拡大する canvas.width *= devicePixelRatio; // 高さはこのサンプルでは幅の4% canvas.height = canvas.width * .04; // devicePixelRatio分、拡大する canvas.height *= devicePixelRatio; // canvasのstyle属性にdevicePixelRatio分縮小した値をセット canvas.style.height = String(canvas.height / devicePixelRatio) + "px";
ここでポイントとなるのが、レスポンシブ表示となるよう、canvasの幅は最後に devicePixelRatio分縮小せず、CSSで常に表示幅が100%でリサイズされるようにしておきます。
ラッパー要素に背景色を指定する
ラッパー要素(.canvas-wrapper
)同士が切れ目なく波形の境界で繋がっているように見せれるデザインが構成できるよう、ラッパー要素にも背景カラーを指定できるようにします。
ラッパー要素の背景カラーは、canvas
要素の data-bgcolor
属性に指定します。
// canvas要素のdata属性を取得 var canvasDataSet = canvas.dataset; // data-bgcolorがある場合 if (canvasDataSet.bgcolor) { // 親要素である.canvas-wrapper のスタイルに background-colorをセット canvas.parentNode.style.backgroundColor = canvasDataSet.bgcolor; }
メインコンテンツ部分の配色
メインコンテンツ(.inner
)がある場合、境界の波形(canvas
)の色と同化させるため、.inner
要素にもdata-bgcolor
とdata-color
属性を用意してメインコンテンツの背景カラーとフォントカラーを指定できるようにします。
// メインコンテンツ(.inner)要素を取得 var inner = canvas.nextElementSibling; // メインコンテンツが存在する場合 if (inner){ // .inner要素のdata属性を取得 innerDataSet = inner.dataset; // data-color がある場合 if (innerDataSet.color){ // .inner要素のフォントカラーをセット inner.style.color = innerDataSet.color; } // data-bgcolor がある場合 if (innerDataSet.bgcolor){ // .inner要素の背景カラーをセット inner.style.backgroundColor = innerDataSet.bgcolor; } }
アニメーションの速さや波形の数、形は作り方次第で様々な組み合わせが考えられるので、canvasだけでもかなりWebデザインの表現の幅が広がるのではないでしょうか。
[参考] Canvasで波のアニメーションを描画するhttps://jsfiddle.net/39we73t1/
HTML5 CanvasとWebGLの高解像度対応はどこまで行うべきか