【CSS/Sassで作るミニゲーム】CSS変数(カスタムプロパティ)とシンプルなJSコードでHTML要素をコントロール


※ 当ページには【広告/PR】を含む場合があります。
2023/12/15
【Sassで作るCssミニゲーム】Cssミニゲームで使えるアニメーションテクニックの基礎
蛸壺の技術ブログ|CSS変数(カスタムプロパティ)とシンプルなJSコードでCSSゲームをコントロール

CSSの
「カスタムプロパティ」はcssコード内で変数として扱えるもので、Sassでいうところの『$...』と似た文法です。

カスタムプロパティ (--*): CSS 変数 - mdn web doc

とはいえ、CSS標準のカスタムプロパティ(変数)とSassでの変数は全く似て非なるものです。

もっとも大きな違いは、プリプロセッサであるSass変数はJavascriptコード側からは操作できないのに対して、CSSのカスタムプロパティはJSスクリプト側から直接操作が可能です。

このCSSカスタムプロパティは色々と活用の機会も多く、例えばJS系のフレームワークを使うほどの規模ではない、シンプルなHTMLミニゲームの実装などでも利用できるでしょう。

これまで当ブログでは幾つかSassの変数を使ったテクニックを紹介してきましたが、それとは全く別の話として今回はCSSの変数を使う上で欠かせない基礎テクニックとちょっとした応用を取り上げていきます。


合同会社タコスキングダム|蛸壺の技術ブログ【効果的学習法レポート】CSS/Sassをこれから学びたい人のためのオススメ書籍&教材特集

CSS変数(カスタムプロパティ)の基本的な使い方

まずは少しだけカスタムプロパティの使い方を復習しておきます。

シンプルにカスタムプロパティから文字の色を指定してみましょう。

            
            <body id="main">
    <p>ようこそ、カスタムプロパティ!</p>
</body>
        
ここに以下のCSSスタイルを適用します。

            
            #main {
    --color: red;
}
p {
    color: var(--color);
}
        
これを試すと以下のような結果で表示されるでしょう。

CSSの文法として、カスタムプロパティとは
「--(ハイフン2回)」から始まるプロパティのことであり、ここだと--color: redにあたります。

このカスタムプロパティは組込関数の
varを使って、カスタムプロパティの値を任意のプロパティで展開することが可能です。

つまり、この例でいうと、
var(--color) --> redとして解釈されます。

これがカスタムプロパティが、CSS変数と呼ばれる所以になります。

ただの変数として利用するだけだと、利便上、Sass変数と何も大差ないのですが、先述の通り、JSスクリプト側からの動的な操作が可能であるところが異なります。

では、カスタムプロパティとJS側の連携をどうするのか、簡単な例で見ていきます。

getComputedStyle/getPropertyValueから変数を読み込む

まずはカスタムプロパティの値の読み取りに使う
「getComputedStyle/getPropertyValue」関数を理解しましょう。

HTML部は以下のようにしてみます。

            
            <div id="main">
    <p>この文字は何色ですか?</p>
</div>
        
CSSは先ほどとほぼ同じです。

            
            #main {
    --color: green;
}
p {
    color: var(--color);
}
        
また今回は以下のようなJSスクリプトをHTMLの<script>タグに仕込みます。

            
            const isSupported = window.CSS && window.CSS.supports && window.CSS.supports('--a', 0);
if (isSupported) {
    const div = document.getElementById("main");
    const styles = getComputedStyle(div);
    const value = styles.getPropertyValue('--color');
    console.log(`文字は ${value} に設定されています`);
}
else {
    console.log('お使いのブラウザはカスタムプロパティ非対応です');
}
        
CSSカスタムプロパティは現在、ほとんどのブラウザでサポートしているため、必ずしも要らないのですが、

            
            //カスタムプロパティがサポートされている場合、isSupportedはtrueが返る
const isSupported = window.CSS && window.CSS.supports && window.CSS.supports('--a', 0);
        
とすることで、ユーザーが利用中のブラウザに適切な処理の切り替え判定が可能です。

該当のDOM要素に割り当てられたCSSスタイルを取得するのに、
getComputedStyleを利用します。

参考|Window.getComputedStyle()メソッド

DOM要素からCSSスタイルを取得する方法は、
getComputedStyleメソッドを使うか<要素オブジェクト>.styleプロパティを使うかの2通りが存在します。

ここで重要となるのが、参考先のドキュメントにも言及されているように、

            
            + getComputedStyle:
    返されたオブジェクトは読み取り専用。
    <style>タグや外部スタイルシートで設定されたものも含めて、
    要素に付与されているCSSスタイルを取得する
+ <element>.style:
    JavaScriptの操作やグローバルのstyle属性から直接追加された
    CSSスタイルを取得できる。
    もしくは、その要素(element)に新しいCSSスタイルを設定する
        
という違いがあります。

カスタムプロパティは、通常
<style>タグで定義して利用しますので、前者のgetComputedStyleを使うことが望ましいです。

getComputedStyleで取得したスタイルオブジェクトから、getPropertyValueでDOM要素に割り当てられたカスタムプロパティを指定して、その値を読み出すことができます。

            
            //...
const styles = getComputedStyle(div);
const value = styles.getPropertyValue('--color');
//...
        

ここでの
getPropertyValueメソッドは、指定されたプロパティの値を、文字列として返します。

参考|CSSStyleDeclaration.getPropertyValue()

以上、このHTMLを試すと以下のように動きます。

ブラウザのデバッグコンソールで確認すると、

合同会社タコスキングダム|蛸壺の技術ブログ

というように、カスタムプロパティに設定した値を取得できています。

setPropertyから変数の値を変更する

カスタムプロパティを使う上で、もっとも強力な活用法に、
setPropertyメソッドを利用することで、カスタムプロパティに値を動的に設定することができます。

参考|CSSStyleDeclaration.setProperty()

この
setPropertyメソッドはカスタムプロパティに限らず通常のプロパティにも利用することができます。

簡単な一例でその使い方をやってみましょう。

テストのHTML部は以下のようにしてみます。

            
            <div id="main">
    <p>色を変えてみよう</p>
    <button id="btn">色をチェンジ</button>
</div>
        
CSSは先ほどとほぼ同じで、

            
            #main {
    --color: black;
}
p {
    color: var(--color);
}
        
とします。

ボタンをクリックすると、カスタムプロパティの値が変わるように以下のJSスクリプトを
<script>へ設置します。

            
            const isSupported = window.CSS && window.CSS.supports && window.CSS.supports('--a', 0);
if (isSupported) {
    const btn = document.getElementById("btn");
    btn.addEventListener('click', changeTextColor);
}
else {
    console.log('お使いのブラウザはカスタムプロパティ非対応です');
}
function changeTextColor(e) {
    const div = document.getElementById("main");
    div.style.setProperty('--color', 'red');
}
        
これを実際に試すと、以下のように動くでしょう。

やってみると分かる通り、
div#main要素に設定されている--colorプロパティの値を以下のコードで動的に変更しています。

            
            //...
const div = document.getElementById("main");
div.style.setProperty('--color', 'red');
//...
        
このsetPropertyメソッドを使う真価は、カスタムプロパティを変数として参照しているDOM要素が、どこにあっても、複数存在していても、確実にその値を変更できるということにあります。

かつてかなり悩ましくもあった、「スタイルの値を変更したい要素をgetElementById等で個別に取得する」操作はもう必要ではないため、JSスクリプトの実装はとても見通しがよく楽になります。


合同会社タコスキングダム|蛸壺の技術ブログ【効果的学習法レポート】CSS/Sassをこれから学びたい人のためのオススメ書籍&教材特集

応用編〜画像をカスタムプロパティで動かす

ここからはゲームづくりに活かせるテクニックとして、カスタムプロパティを画像の座標と連動させてみます。

まずはHTMLパートを以下のように仕込んでおきます。

            
            <div id="main">
    <div id="mentaco"></div>
    <input type="range" id="slider1" min="0" max="100" value="0">
    <input type="range" id="slider2" min="0" max="100" value="100" orient="vertical">
</div>
        
数値を連動させる要素は何でもよいですが、ここではinputのrangeタイプをスライダーとして利用します。

参考|<input type="range">

画像は前回同様、
<div>要素に仕込むやり方でそのまま流用します。

合同会社タコスキングダム|蛸壺の技術ブログ
【Sassで作るCssミニゲーム】Cssミニゲームで使えるアニメーションテクニックの基礎

CSSでアニメーション効果をつける「transition」と「animation」の使い方を比較&効果的な利用方法

次にCSSパートを作成します。

            
            #main {
        width: calc(100% - 40px);
        height: 300px;
        margin: 20px;
        background-color: darkgray;
        box-sizing: border-box;
        position:relative;
        --mentaco-x: 0;
        --mentaco-y: 0;
    }
    #mentaco {
        position: absolute;
        display: block;
        padding: 0 0 0 0;
        top: max(var(--mentaco-y) - 80px, 0%);
        left: max(var(--mentaco-x) - 72px, 0%);
        width: 80px;
        height: 72px;
        background: transparent url('https://raw.githubusercontent.com/tacoskingdom/commonBlogMaterial/main/deep-tacopots/blog116/mentaco.png') no-repeat 0 0;
        background-size: 80px 72px;
    }
    #slider1, #slider2{
        position: absolute;
        display: block;
        z-index: 1;
        cursor: pointer;
    }
    #slider1 {
        top: -20px;
        margin: 0 20px;
        width: calc(100% - 40px);
    }
    #slider2 {
        left: -20px;
        margin: 5px auto;
        height: calc(300px - 10px);
    }
        
要素数が増えて、少しCSSのコードのボリュームが多くなりました。

ここでのカスタムプロパティは2つで、画像のx座標を変数化した
--mentaco-xと、y座標の--mentaco-yです。

色々とスタイルがごちゃごちゃしてますが、ここでの注目すべきポイントは一つで、

            
            ...
#mentaco {
    ...
    top: max(var(--mentaco-y) - 80px, 0%);
    left: max(var(--mentaco-x) - 72px, 0%);
    ...
}
...
        
の部分です。

画像の絶対位置(topとleft)は、それぞれカスタムプロパティから算出されるようにしています。

CSS側の事前準備ができましたので、あとは
<script>タグにJSスクリプトパートを実装していきます。

            
            const isSupported = window.CSS && window.CSS.supports && window.CSS.supports('--a', 0);
if (isSupported) {
    const sldr1 = document.getElementById("slider1");
    const sldr2 = document.getElementById("slider2");
    sldr1.addEventListener('input', xSlider);
    sldr2.addEventListener('input', ySlider);
}
else {
    console.log('お使いのブラウザはカスタムプロパティ非対応です');
}
function xSlider(e) {
    const x_pos = e.target.value;
    const div = document.getElementById("main");
    div.style.setProperty('--mentaco-x', x_pos + '%');
}
    function ySlider(e) {
    const y_pos = 100 - e.target.value;
    const div = document.getElementById("main");
    div.style.setProperty('--mentaco-y', y_pos + '%');
}
        
inputのスライダータイプは、inputというイベント名でハンドラをセットすることができます。

あとは、このスライダーを動かしたイベントに対して、スライダーの値(0 ~ 100)から画像の座標を算出するロジックを作り、
setPropertyメソッドでカスタムプロパティを再設定する、というスクリプトになっています。

では、理屈も分かっていただいたとして、最後に実際のHTMLを動かしてみましょう。

スライダーと連動して、メンダコが動いていたら完成です。


合同会社タコスキングダム|蛸壺の技術ブログ【効果的学習法レポート】CSS/Sassをこれから学びたい人のためのオススメ書籍&教材特集

まとめ

CSSカスタムプロパティを変数として利用することで、JSスクリプト側の実装コードの見通しが非常にすっきりとしたものにすることができるようになります。

近年は、JS系のフレームワークもかなり巧妙化・高機能化が進み、SPA(シングルページアプリ)が簡単に作れるようになった反面、開発環境のセットアップに苦労するようになり、JSのコーディング側のお手軽感がなくなりつつあります。

ちょっとしたHTMLアプリなら、今回のように他のライブラリも必要としないでサッと作れる、ピュアJSだけで完結させるテクニックを身に付けておかれると、当ブログのようなフロントエンド技術系ブログの運用に便利になること請け合いです。

記事を書いた人

記事の担当:taconocat

ナンデモ系エンジニア

主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。

合同会社タコスキングダム|蛸壺の技術ブログ【効果的学習法レポート】CSS/Sassをこれから学びたい人のためのオススメ書籍&教材特集