【コピペOK!】マテリアルデザインのボタンを作る方法

今回はマテリアルデザインのWebサイトで使われるようなボタンの作り方を紹介します。

マテリアルデザイン(material design)とはGoogleが提唱しているデザイン手法のガイドラインです。

マテリアルという言葉には「物質的」という意味がありますが、画面を構成するUIパーツに影などの効果を使って奥行きだったり重なりだったりを現実世界のように表現することでユーザーにとっつきやすい画面を作るための表現方法の1つと思ってもらってよいかと思います。

マテリアルデザインではいろいろなパーツを作るにあたってそれぞれルールを定めているのですが、今回は紹介するのはボタンの作り方で、クリックしたら波紋のエフェクトが表れる「これ」です。

Googleはこちらにサンプルを掲載しています。

では早速ソースコードですが、以下のような感じになります。

お急ぎの方はとりあえずこちらをコピーして動きを見てみてください。

HTML


<html>
<head>
    <style>
        .md-button {
            /* width、heightにpaddingやborderの幅を含めます。無くてもよいですがこうしておくとレイアウトがしやすいです。 */
            box-sizing: border-box;
            /* 境界線は無し */
            border: none;
            /* 輪郭線は無し */
            outline: none;
            /* ボタンにカーソルが当たった時に人差し指マークを表示する */
            cursor: pointer;
            /* 文字色は白 */
            color: white;
            /* 背景色は紫 */
            background-color: #6200ee;
            /* パッディング(ボタン内側の余白)の設定 上下10px、左右16px */
            padding: 10px 16px;
            /* ボタンの角を少し丸くする */
            border-radius: 4px;
            /* ボタンに影をつける */
            box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.2),0px 2px 2px 0px rgba(0, 0, 0, 0.14),0px 1px 5px 0px rgba(0,0,0,.12);
            /* 影をふわっと表示させる */
            transition: box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);
            /* 波紋用spanタグをボタン内に配置するための設定 */
            position: relative;
            /* 波紋がボタンからはみ出ないようにする */
            overflow: hidden;
        }
        /* ボタンにマウスが当たった or タップした時 */
        .md-button:hover, .md-button:focus {
            /* 背景色を少し明るくする */
            background-color: #6d11ec;
            /* 影を少し伸ばす */
            box-shadow: 0px 2px 4px -1px rgba(0, 0, 0, 0.2),0px 4px 5px 0px rgba(0, 0, 0, 0.14),0px 1px 10px 0px rgba(0,0,0,.12);
        }
        /* ボタンが押された時 */
        .md-button:active {
            /* 背景色をさらに明るくする */
            background-color: #8d4beb;
            /* 影をさらに伸ばす */
            box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.2),0px 8px 10px 1px rgba(0, 0, 0, 0.14),0px 3px 14px 2px rgba(0,0,0,.12);
        }
        /* 波紋 */
        span.ripple {
            /* 波紋を表現するために配置を絶対位置にする */
            position: absolute;
            /* 角を丸めて円にする */
            border-radius: 50%;
            /* 拡大設定(最初は見えなくしたいので0) */
            transform: scale(0);
            /* 波紋の広がりアニメーションを設定 */
            animation: ripple 600ms linear;
            /* 波紋の色 */
            background-color: rgba(255, 255, 255, 0.7);
        }
        /* 波紋の広がりアニメーション */
        @keyframes ripple {
            to {
                /* 波紋の円を縦横4倍に広げる */
                transform: scale(4);
                /* 不透明度を設定。0にして透明にする */
                opacity: 0;
            }
        }
        body {
            background-color: #FFEE58;
        }
        div {
            width: 100vw;
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
        }
    </style>
    <script>
        window.onload = function() {
            // ボタン要素を取得
            const button = document.getElementById('mybutton')
            // ボタンクリック時イベントを設定
            button.addEventListener('click', createRipple)
        }
        /**
         * ボタンに波紋をつける
         */
        function createRipple(event) {
            // クリックしたボタン要素の取得
            const button = event.currentTarget
            // 波紋の直径(ボタンの縦横長い方のサイズ)
            const diameter = Math.max(button.clientWidth, button.clientHeight)
            // 波紋の半径
            const radius = diameter / 2

            // 波紋用spanタグを作る
            const circle = document.createElement('span')
            // 波紋の横幅、高さを設定
            circle.style.width = `${diameter}px`
            circle.style.height = `${diameter}px`
            // 波紋の表示位置をクリックしたポイントに設定
            circle.style.left = `${event.clientX - button.offsetLeft - radius}px`
            circle.style.top = `${event.clientY - button.offsetTop - radius}px`
            // rippleクラスを設定
            circle.classList.add('ripple')

            // 波紋用のspanタグをbuttonタグに追加する(クリック2回目以降はすでに追加されているので削除してから追加)
            const ripple = button.getElementsByClassName('ripple')[0]
            if (ripple) ripple.remove()
            button.appendChild(circle)
        }
    </script>
</head>
<body>
    <div>
        <button id="mybutton" class="md-button">BUTTON</button>
    </div>
</body>
</html>

解説用のコメントを付け足しているのでちょっと長めになっております。

では以下、解説していきます。

HTML

HTML


<button id="mybutton" class="md-button">BUTTON</button>

HTMLはbuttonタグを用意するだけですね。

ボタンのスタイリングはCSSで、クリックした時の波紋はJavaScriptで制御しているのでそちらがメインになります。

CSS

ボタンに「md-button」という名前でクラスを付与しています。

「md-button」クラスでボタンのデザインをしています。

CSS


.md-button {
   /* width、heightにpaddingやborderの幅を含めます。無くてもよいですがこうしておくとレイアウトがしやすいです。 */
   box-sizing: border-box;
   /* 境界線は無し */
   border: none;
   /* 輪郭線は無し */
   outline: none;
   /* ボタンにカーソルが当たった時に人差し指マークを表示する */
   cursor: pointer;
   /* 文字色は白 */
   color: white;
   /* 背景色は紫 */
   background-color: #6200ee;
   /* パッディング(ボタン内側の余白)の設定 上下10px、左右16px */
   padding: 10px 16px;
   /* ボタンの角を少し丸くする */
   border-radius: 4px;
   /* ボタンに影をつける */
   box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.2),0px 2px 2px 0px rgba(0, 0, 0, 0.14),0px 1px 5px 0px rgba(0,0,0,.12);
   /* 影をふわっと表示させる */
   transition: box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);
   /* 波紋用spanタグをボタン内に配置するための設定 */
   position: relative;
   /* 波紋がボタンからはみ出ないようにする */
   overflow: hidden;
}

box-sizingでborder-boxを設定しています。こうすることでボタンの境界線やパッディングの余白などボタン要素の内側にあるものはボタンのwidth、heightに含めることができます。この設定はマテリアルデザインには関係ないですが、widthやheightでサイズを指定したのに実際はそれより大きくなることもありレイアウトで苦労するかもしれないので、基本的には全要素でこれを指定してもいいと思います。bodyタグなどで一括で設定していたらここで設定する必要はありません。というかマテリアルデザインには関係ないので今回は無くても大丈夫です。

border、outlineはnoneにすることでボタンのまわりに余計な線が表示されないようにしています。

cursorでpointerを設定しているのはこれがないとボタンにカーソルを当てても矢印マークのまま変わらないので、「押せるよ!」というのを明確にするために人差し指マークが出るようにしています。

paddingでボタン名の上下の余白を10px、左右の余白を16pxで設定しています。今回はこのpaddingによってボタンのサイズを決めています。代わりにwidthやheightでボタンのサイズを指定しても構いません。

border-radiusでボタンの角を丸くしています。数値が大きくなればなるほど角が丸くなります。後ほど波紋のエフェクトでも出てきますが、50%を設定すると正円が描けます。

box-shadowでうっすらボタンに影をつけています。「0px 3px 1px -2px rgba(0, 0, 0, 0.2)」の影と「0px 2px 2px 0px rgba(0, 0, 0, 0.14)」の影と「0px 1px 5px 0px rgba(0,0,0,.12)」の影をかけ合わせています。(この設定はこちらのページでボタンのCSSを開発者ツールで確認して得たものです。)

ここまででおおまかなボタンのスタイルが出来上がりました。

以降はボタンに動きがあった時の制御用なのですが、

transitionについては後述するボタンにカーソルを当てた時の影のスタイルがふわっと表示されるよう速度調整をしています。

positionにrelativeを設定しているのはボタンをクリックされた時にbuttonタグの中にspanタグを作ります。このspanタグが波紋になります。そしてこのspanタグがposition: absolute;で絶対位置になるため、親であるbuttonタグからはみ出てしまいます。buttonタグにrelativeを設定することでspanタグはbuttonタグの中で絶対配置になるようにしています。

overflowにhiddenを設定しているのは拡大する波紋がボタンからはみ出ないようにするためです。

CSS


/* ボタンにマウスが当たった or タップした時 */
.md-button:hover, .md-button:focus {
   /* 背景色を少し明るくする */
   background-color: #6d11ec;
   /* 影を少し伸ばす */
   box-shadow: 0px 2px 4px -1px rgba(0, 0, 0, 0.2),0px 4px 5px 0px rgba(0, 0, 0, 0.14),0px 1px 10px 0px rgba(0,0,0,.12);
}
/* ボタンが押された時 */
.md-button:active {
   /* 背景色をさらに明るくする */
   background-color: #8d4beb;
   /* 影をさらに伸ばす */
   box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.2),0px 8px 10px 1px rgba(0, 0, 0, 0.14),0px 3px 14px 2px rgba(0,0,0,.12);
}

:hover、:focus、:acriveは擬似クラスといってこれら擬似クラスはボタンの状態が変わった時に発動するスタイルになります。

:hoverはマウスがボタンの上に配置された時に、

:focusはタップされた時に、

:activeはボタンが押された時に有効になります。

コメントに書いてあるとおりですが、背景色を少しずつ明るくしているのと、影を少しずつ伸ばしています。

こうすることでボタンにカーソルが当たった、押されたことをユーザーに伝わるよう表現しています。

CSS


/* 波紋 */
span.ripple {
   /* 波紋を表現するために配置を絶対位置にする */
   position: absolute;
   /* 角を丸めて円にする */
   border-radius: 50%;
   /* 拡大設定(最初は見えなくしたいので0) */
   transform: scale(0);
   /* 波紋の広がりアニメーションを設定 */
   animation: ripple 600ms linear;
   /* 波紋の色 */
   background-color: rgba(255, 255, 255, 0.7);
}
/* 波紋の広がりアニメーション */
@keyframes ripple {
   to {
      /* 波紋の円を縦横4倍に広げる */
      transform: scale(4);
      /* 不透明度を設定。0にして透明にする */
      opacity: 0;
   }
}

こちらはボタンをクリックした時に表れる波紋を表現するスタイルになります。

波紋はクリックされた位置から広がっていくため、表示位置がころころ変わります。

なので、position: absoluteで絶対位置に設定しています。

クリックされた位置はCSSではわからないので後述するJavaScriptで算出しています。

border-radius: 50%でspanタグを正円にしています。

transform: scale(0)で拡大率0倍、つまり初期状態は何も見えない状態にしています。

animationにrippleという名前のアニメーションを設定しています。rippleアニメーションの具体的な設定はその下の@keyframesに記載されています。「to」のくくりの中にスタイルが書かれていますね。「to」はアニメーション終了時のスタイルを意味しています。ボタンをクリックしてから600ms(0.6秒)の時間をかけて「to」の状態にしますよ、という意味になります。

最終的には波紋のサイズは縦横4倍まで広がるのと同時に徐々に透明になっていきます。

CSSの設定は以上です。HTML上、初期表示では波紋のspanタグはまだ無いので後半に見たspanタグに対するCSSは機能しません。

spanタグが出現するのはボタンをクリックしてからになります。

JavaScript

ではボタンをクリックした時に挙動を以下でチェックしましょう。

JavaScript


window.onload = function() {
   // ボタン要素を取得
   const button = document.getElementById('mybutton')
   // ボタンクリック時イベントを設定
   button.addEventListener('click', createRipple)
}

まずは画面読み込み時にボタンに対してクリックイベントを設定しています。

mybuttonというID名でボタン要素を取得し、addEventListener関数でクリック時にcreateRipple関数が呼ばれるようにしています。

ではcreateRipple関数の中身を見ていきましょう。

JavaScript


// クリックしたボタン要素の取得
const button = event.currentTarget
// 波紋の直径(ボタンの縦横長い方のサイズ)
const diameter = Math.max(button.clientWidth, button.clientHeight)
// 波紋の半径
const radius = diameter / 2

まずは波紋の表示位置を計算するための材料(ボタン要素、波紋の直径・半径)を集めます。

引数のeventからクリックしたボタン要素を取得しています。

ボタンの横幅、高さどちらか高い方を波紋の直径とします。なのでサイズ的にはボタンからはみ出るのですが、overflow: hiddenをCSSで設定しているのではみ出る分は表示されません。

JavaScript


// 波紋用spanタグを作る
const circle = document.createElement('span')
// 波紋の横幅、高さを設定
circle.style.width = `${diameter}px`
circle.style.height = `${diameter}px`
// 波紋の表示位置をクリックしたポイントに設定
circle.style.left = `${event.clientX - button.offsetLeft - radius}px`
circle.style.top = `${event.clientY - button.offsetTop - radius}px`
// rippleクラスを設定
circle.classList.add('ripple')

では波紋のspanタグを作ります。ここがJavaScriptのキモですね。

createElement関数でspanタグを作ります。しかしこの時点ではまだ画面に追加されたわけではありません。

spanタグの横幅、高さに波紋の直径を設定します。

ちなみにちょっと脱線しますが、「`${変数名}px`」という書き方はテンプレートリテラルといって、文字列の一部に変数を埋め込んで文字列を作る手法です。

上記のdiameterが50だとすると「`${変数名}px`」は’50px’ということになります。「diameter + ‘px’」と書いても同じことです。

波紋はposition: absoluteにて絶対位置なので、left、topで左上の位置を設定します。

event.clientX、clientYはクリックしたポイントのX座標・Y座標になります。

button.offsetLeft、offsetTopはボタンの親要素の左上からの距離になります。

radiusは波紋の半径です。

event.clientX、clientYからbutton.offsetLeft、offsetTopを引くと、クリックした座標に波紋の左上がくるようになります。

しかしクリックした座標を波紋の中心にしたいので、半径分をさらに引きます。

こうすることでクリックした座標が波紋の中心になります。

JavaScript


// 波紋用のspanタグをbuttonタグに追加する(クリック2回目以降はすでに追加されているので削除してから追加)
const ripple = button.getElementsByClassName('ripple')[0]
if (ripple) ripple.remove()
button.appendChild(circle)

最後に波紋のspanタグをbuttonタグに追加しておしまいなのですが、1回クリックするとbuttonタグの中にspanタグが残った状態になります。

appendChild関数でspanタグが画面に追加されて初めて上記で用意した波紋のCSSが効きはじめます。

次回のクリックからは追加する前に削除しないとクリックするたびにspanタグが増えていくので、事前に削除する必要があるのが注意事項です。

まとめ

マテリアルデザインでよく見るクリックしたら波紋が広がるボタンの作り方を紹介しました。

ちょっとだけJavaScriptが必要になりますが、基本的にはCSSでほぼ実装できます。

よかったら試してみてください。

ちなみに解説のために1行1行コメントを載せていますが、コメントだらけになるのはあまりよろしくないと思うので、実際に使う時は必要に応じて削除してください。

したっけ!

本格的にプログラミングを学びたいですか?ITのエンジニアになりたいですか?

IT業界は万年人手不足であり、ニーズがあります。
パソコンとインターネットがあれば場所を問わず仕事ができるので、リモートワークが普及しつつある現代にマッチした職種と言えると思いますし、 物理的に必要なものはパソコンぐらいなので初期投資にかかる費用も少なく、人並みに仕事ができればフリーランスになって会社依存を脱却することもできます。

身につけた技術は一生モノです。

もし本腰を入れて勉強したいという方はスクールに入るのも一つの手です。
いくつか紹介しますので、興味があればサイトを覗いてみてください。

DMM WEBCAMP

転職を本気で考えている方向けのプログラミングスクールです。 転職を保証しているため、未経験からIT業界へ転職を求めている方へおすすめです!

DMM WEBCAMPのサイトへ

TechAcademy

最短4週間で未経験からプロを育てるオンライン完結のスクールです。 どこかに通う必要なく、自宅でもプログラミングやアプリ開発を学ぶことができます。

TechAcademyのサイトへ

Want to learn programming in earnest? Want to be an IT engineer?

The IT industry is understaffed for many years and has needs.
If you have a computer and the Internet, you can work anywhere, so I think it can be said that it is a job type that matches the present age when remote work is becoming widespread. The initial investment cost is low, and if you can work like a normal person, you can become freelance and get rid of your dependence on the company.

The skills that you have acquired is something that will last a lifetime.

If you want to study in earnest, you can go to school.
I will introduce some of them, so if you are interested, please take a look at the site.

DMM WEBCAMP

This is a programming school for those who are serious about changing jobs. guarantee a job change, so it is recommended for those who are inexperienced and are looking for a job change in the IT industry!

move to DMM WEBCAMP

TechAcademy

It is an online school that trains professionals from inexperienced in a minimum of 4 weeks. You can learn programming and app development at home without having to go anywhere.

move to TechAcademy