ゲームなどでよく見る「長押しのボタン」のUIをWEBで表現してみた

# はじめに

スマホゲームやSwitchなどのゲームのUIでよく見る、「**長押しボタン**」をWEB画面で実装します。

使う技術としては、**HTML**、**CSS**、**JavaScript**だけで、JavaScriptのライブラリや画像やSVGは使いません。

長押しボタンと言ってもイメージが付かないと思いますので、完成系のGifを用意しました。（なぜか、WEBではあまり見かけないUIですよね...）

![Animation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/473097/220370b8-de93-7db0-9db2-3d7edfe2c098.gif)

# 実装

## 1. メータ部分の作成

まずは、メータ部分を作成するのですが、`divタグ`を使って円を作成します。
`background-image`に`conic-gradient`を指定して、円グラフのような見た目ができるスタイルを使っています。


```html:html
<div id="outer_circle"></div>
```

```css:css
#outer_circle {
    position: relative;
    margin-right: auto;
    margin-left: auto;
    width: 100px;
    height: 100px;
    border-radius: 50%;
    background-image: conic-gradient(#d5525f 0% 0%, #d9d9d9 0% 100%);
}
```

`conic-gradient`の第二引数または第三引数の割合を増やしていくと、以下のような動きになります。（今回はJavaScript側で動きを制御したいので、CSSのアニメーションは使いません）

![Animation4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/473097/a18a76a3-66ff-2a3e-620d-37472f2a4ae4.gif)



## 2. ボタン部分の見た目を作成

次は、ボタン部分の見た目を完成させます。
先ほど作成した、`outer_circle`の`divタグ`で`inner_circle`を作成し、中に少し小さい円を作成します。

```html:html
<div id="outer_circle">
    <div id="inner_circle"></div>
</div>
```

```css:css
#inner_circle {
    text-align: center;
    background-color: #676767;
    width: 80px;
    height: 80px;
    border-radius: 50%;
    position: relative;
    top: 10px;
    left: 10px;
}
```

見た目はこれで完成になります。まだ、JavaScript部分を作っていないのでクリックしても動作しませんが、疑似的に動かすと以下のような感じになります。

![Animation4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/473097/34753311-fcfc-0936-5c92-be40df429acd.gif)


## 3. 長押しの処理を実装

次にボタンの長押し時の処理をJavascriptで実装します。
`longPress`のオブジェクトを作成し状態を管理しており、長押し中に`conic-gradient`の割合を計算し画面に随時反映しています。
（下記処理は、画面読み込み後に実行されるようにする必要があります）


```js:javascript
const longPress = {
    // プロパティ
    innerEl: document.getElementById("inner_circle"), //内側の円
    outerEl: document.getElementById("outer_circle"), //外側の円
    second: 3, //長押しの時間
    count: 0,
    timerId: 0,
    interval: 10,

    // 初期化処理
    init: function(param){
       // イベントリスナー
        this.innerEl.addEventListener('pointerdown', ()=>{this.start()}, false);
        this.innerEl.addEventListener('pointerup', ()=>{this.end()}, false);
    },

    // 長押し中の処理
    start: function(){
        this.timerId = setInterval(()=>{
            this.count++;
            this.outerEl.style.backgroundImage = 'conic-gradient(#d5525f 0% '+ this.count/this.second +'%, #d9d9d9 0% 100%)';

            if (this.count / 100 === this.second) {
                alert('ボタンを' + this.second + '秒長押ししました！');
                this.end();
            }
        }, this.interval);
    },

    // 長押し中断の処理
    end: function(){
        clearInterval(this.timerId);
        this.count = 0;
        this.outerEl.style.backgroundImage = 'conic-gradient(#ff0019 0% 0%, #d9d9d9 0% 100%)';
    }
};

//longPressの初期化
longPress.init();
```

実際に長押し操作を行うと以下のような動作になります。

![Animation4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/473097/c4a2197b-fb75-71ca-a46c-cc3259a6a910.gif)

## 4. バグについて

実は、上記コードだと考慮していない操作があり、**長押しメーターが止まらない**というバグがあります。バグが発生する操作としては以下の通りです。
- 長押し中に右クリック
- 長押し中にマウスのポインタが外れる

`init`メソッド内に下記の処理を追記することで解消されます。

```js:javascript
this.innerEl.addEventListener('pointerout', ()=>{ this.end() }, false);
this.innerEl.oncontextmenu = () => {return false;}
```

# おわりに

長押しのボタンをHTML・CSS・JavaScriptで実装してみました。

今回は、円グラフのようなUIで作成しましたが、カスタマイズすることで棒グラフが伸びていくUIや水が溜まっていくようなUIにすることも可能かと思います。

また、longPressのオブジェクトを使って**長押しメーターの色**や**タイマーの時間**や長押しし終わったときに**コールバック関数を使って特定の処理を実行する**ことも可能です。下記リンクが、パラメータを色々とカスタマイズできるようにしたサンプルです。（気になった方は、ぜひ触ってみてください）

https://ishi720.github.io/LongPressButton/

ここまで見ていただきありがとうございました。