簡単にできる3Dアニメーション【CSS&javascript】

 プログラムが自分の思う通りに動作した時、バグ(不具合)の原因が分かった時は嬉しいものです。まさにプログラマ冥利に、尽きました。その思いが強くて会社を辞めた後もプログラムを作りたいという気持ちに駆られました。
 丁度その頃(会社を辞める前か辞めた頃)HTML,CSS,javascript を使用したホームページを作ることが主流になってきました。その後、十年以上HTML,CSS,javascriptの勉強をしています。
 2022冬季北京オリンピックの感動も冷めやらぬ中、CSSjavascript を使用して3Dアニメーションを作ってみました。 設定をご自由に変更して「RUN」ボタンをクリックして試みてください。面白いですよ!

スノーボード

http://hilbert314159.org/3Danimation/

hilbert314159.org

 このアプリを作成するにあたってのポイント
  ・ボタンをクリックされたらアニメーションを開始するには、どうするの?
  ・えっ!本当。CSSの中で変数を使用できるの?
  ・javascriptを使用してキーフレーム(keyframe)の引数を変更するには、どうするの?
などの技術的な内容を記述します。

1. brackets から VScode への移行

 今まで HTML,CSS,javascript を使用してWEBアプリを作る時には、コードエディター「brackets」を使っていたのですが、サポートが終了したとのことでしたので、「Visual Studio Code」へ移行したいと思っていました。簡易的なローカルサーバ「Live Server」をインストールして試してみましたがエディターで変更した内容がbracketsの様に、すぐにはブラウザに反映されないので未だ拡張機能プラグイン)が足りないのかなと思っていました。そんな時ファイル・メニューの中に「自動保存」の項目があることに気づき「これかも知れない」と思い、「自動保存」にチェックをしたところ、エディターで変更した内容が即時にブラウザに反映される様になりました。自動保存の設定項目は「off」「afterDelay」「onFocusChane」「onWindowChange」があります。

2. アニメーションの作り方とアニメーションを開始する方法

2.1 アニメーションの作り方

 アニメーションの始め方の前に、アニメーションの作り方を簡単に説明します。
 どの様なアニメーションを作るのか、アニメーションのスムーズなトランジション(移り変わり、変遷)の開始点と終了点を 定義する描画をキーフレーム(keyframe)を作ります。例えば、画像を右に移動してから90度時計回りに回転させたい場合には

@keyframes mov_rot {                  

    0% { transform: translateX(0px) rotate(0deg);}
    50% { transform: translateX(100px) rotate(0deg);} 
    100% { transform: translateX(100px) rotate(90deg);}
}

と記述します。
「transformプロパティ」を使えば、要素に動きをつけることができます。移動、回転、伸縮、傾斜の4つの効果が用意されてあり、二次元的な動きだけではなく、三次元的な動きまでも可能にしているのが特徴です。
transformプロパティは,トランスフォーム関数を指定して対象要素を変形させます。
【平面空間】

関数 効果
none 要素を変形しません。
matrix() 行列式によって要素を変形します。
translate() 要素のxy座標を移動します。
translateX() 要素のx座標を移動します。
translateY() 要素のy座標を移動します。
scale() 要素をx軸,y軸方向に拡大・縮小します。
scaleX() 要素をx軸方向に拡大・縮小します。
scaleY() 要素をy軸方向に拡大・縮小します。
rotate() 要素を回転します。
skew() 要素の形状をx軸,y軸方向に傾斜します。
skewX() 要素の形状をx軸方向に傾斜します。
skewY() 要素の形状をy軸方向に傾斜します。

【3D空間】

関数 効果
none 要素を変形しません。
matrix3d() 行列式によって要素を変形します。
translate3d() 要素のxyz座標を移動します。
translateZ() 要素のz座標を移動します。
scale()3d() 要素をx軸,y軸,z軸方向に拡大・縮小します。
scaleZ() 要素をz軸方向に拡大・縮小します。
rotateX() 要素をx軸を中心に回転します。
rotateY() 要素をy軸を中心に回転します。
rotateZ() 要素をz軸を中心に回転します。
perspective() 画面から視点の距離を指定して,z軸方向に変形した要素の奥行を表します。

 アニメーションを開始する方法として、よく使用する疑似クラスに

疑似クラス イベント
active ユーザーの操作により、要素がアクティブになった時にアニメーションを開始する。
hover ユーザーの操作により、要素にカーソルなどが乗った時にアニメーションを開始する。

等があります。
 画像がホーバー(画像にカーソルなどが乗った)時にアニメーションを開始するには

.rect_part3:hover {

    animation-name:             mov_rot;
    animation-duration:         1s; 
    animation-fill-mode:        forwards;
    animation-timing-function:  ease;
    animation-iteration-count:  1;
}

ここで各プロパティの役割を説明します。

プロパティ 内容
animation-name アニメーションを適用する要素を指定する。
animation-duration アニメーションが完了するまでの時間を指定する。
animation-fill-mode アニメーションの再生中・再生後のスタイルを指定する。
animation-timing-function アニメーションの進行度を指定する。
animation-iteration-count アニメーションの実行回数を指定する。

さらに animation-timing-function, animation-fill-mode プロパティの詳細について述べると

animation-timing-function プロパティは、アニメーションが変化する速度を指定します。

進行度
ease アニメーションの開始・終了付近の動きを滑らかにします。
linear 一定の割合で直線的に再生します。
ease-in アニメーションの開始付近の動きを穏やかにします。
ease-out アニメーションの終了付近の動きを穏やかにします。
ease-in-out アニメーションの開始・終了付近の動きを穏やかにします。
cubic-bezier() 三次ベジェ曲線の軌跡によってアニメーションの進行度を指定します。
step-start アニメーションの開始時点で終了状態となります。
step-end 開始時点では変化せず、終了時にアニメーションが完了した状態になります。
steps() 指定したステップ数で等分に区切ります。

animation-fill-mode プロパティは、アニメーション再生中・再生後のスタイルを指定します。

スタイル
none スタイルを指定します。アニメーション再生後は、元のスタイルが適用されます。animation-delayプロパティを指定している場合、再生までの時間は元のスタイルが適用されます。
backwards アニメーション再生後は、最初のキーフレーム(0%)のスタイルが適用されます。animation-delayプロパティを指定している場合、再生までの時間は最初のキーフレーム(0%)のスタイルが適用されます。
forwards アニメーション再生後は、最後のキーフレーム(100%)のスタイルが適用されます。 animation-delayプロパティを指定している場合、再生までの時間は元のスタイルが適用されます。
both アニメーション再生後は、最後のキーフレーム(100%)のスタイルが適用されます。 animation-delayプロパティを指定している場合、再生までの時間は最初のキーフレーム(0%)のスタイルが適用されます。

但し、animation-delayプロパティはアニメーションが開始するまでの時間を指定するプロパティです。
 ユーザー操作によりボタンをクリックされた時にアニメーションを開始するためにはjavascriptを使用する必要があります。 ユーザー操作によりボタンをクリックされた時、javascriptの関数を呼び出すためはイベント処理を実行するためのメソッド addEventLListener() を使用します。

let Element = document.getElementById('クラス名など');
Element.addEventListener('click', 関数名);

と記述します。ただし関数名の後に () を付ける必要はありません.
イベントの種類には

【マウス関連】

イベント 発生のタイミング
click ボタンをクリックしたとき
mousemove カーソルがターゲット内に移動したとき
mouseover カーソルがターゲット内に侵入してきたとき
mousedown ボタンを押下したとき
mouseup ボタンを離したとき
mouseout カーソルがターゲットの外に出たとき

【キーボード関連】

イベント 発生のタイミング
keypress キーを押して離したとき
keydown キーを押下したとき
keyup キーを離したとき

【DOM関連】

イベント 発生のタイミング
DOMFocusin ターゲットがフォーカスを受け取ったとき
DOMFocusout ターゲットがフォーカスを失ったとき
DOMActivate ターゲットがアクティブになったとき

【INPUT関連】

イベント 発生のタイミング
select テキストフィールドで文字が選択されたとき
change コントロールの値が変化した後
submit submitボタンが押されたとき
reset resetボタンが押されたとき
focus コントロールがフォーカスを受け取ったとき
blur コントロールがフォーカスを失ったとき

などがあります。特にアニメーションに関連するイベントは

イベント 発生のタイミング
animationstart アニメーションを開始したときに発生
animationiteration アニメーションを反復して実行するときに発生
animationend アニメーションが完了したときに発生
animationcancel アニメーションを途中でキャンセルしたときに発生

があります。

2.2 アニメーションを開始する方法

 メソッド addEventListener により呼び出された関数の中で、アニメーションを開始します。ここで classList について説明します。  classList とは特定の要素のクラス名を追加したり、削除したり、参照したりすることが出来るプロパティです。  classListの後にメソッドを定義することにより、追加削除などの機能を指定することができます。
主な classList のメソッドは

メソッド 機能
add クラスを追加する
remove クラスを削除する
contains クラスが含まれているか確認する
toggle クラスが含まれていれば削除、含まれていなければ追加する。

があります。 そこでホーバー疑似クラスの時に作った様にしてアニメーションのクラスを作成します。

.rect_rotate {

    animation-name:                 mov_rot;
    transform-style:                   preserve-3d;
    animation-duration:            1s;
    animation-fill-mode:            forwards;
    animation-timing-function: ease;
    animation-iteration-count:  1;
}

 このアニメーションのクラスを画像などのクラスに追加することにより、アニメーションを開始することができます。そしてアニメーションが終了したならば、アニメーションが完了したときに発生するイベントによりアニメーションのクラスを忘れずに削除します。

3. CSSの中で変数を使用する手順

 もう一つ特記したい事は、今までは知らなかったのですが CSS の中で変数が使用できるという事です。カスタム・プロパティという名称で呼ばれているそうです。(2015年にW3Cから勧告されたの事です。)これは大変便利な機能です。WEBサイトやWEBアプリの制作過程においてヘッダー、ナビゲーション、セクション、フッターなどの全ての幅(width)を少しだけ広げたいという事はよくありました。この様な場合 CSS の中の最初に変数を定義しておけば、一か所を変更するだけで全ての幅を変更することができます。
 変数名の先頭に二つのハイフンを付けて変数を定義しておき、変数を使用するときにはvar()関数に変数名を代入します。
さらに、:root 疑似クラスを使って

:root {
    --body_width: 720px;
}

と定義すれば、HTML文書の全体にわたって適用することができます。
 このカスタム・プロパティを使用する、もう一つのメリットはキーフレームの中に変数を使えば、javascript により変数を変更することができます。たとえばアニメーションを回転させるキーフレームを

@keyframes Rotate_rectangle {

    from{
        transform: rotateX(0deg) rotateY(0deg) rotateZ(0deg);
    }
    to{
        transform: rotateX(var(--val-xdeg)) rotateY(var(--val-ydeg)) rotateZ(var(--val-zdeg));
    }
}

と定義しておけば、javascript の setProperty() メソッドにより変更することができます。

function setProperty_xdeg(event){

    var xdeg = event.currentTarget.value + "deg";

    document.documentElement.style.setProperty('--val-xdeg', xdeg);
}