【SHOWROOM】配信中のギフトをリアルタイムに画面に降らせる方法

# はじめに
この記事は[【SHOWROOM】配信中のコメント・ギフトログをリアルタイムに取得する方法](https://qiita.com/TYoichiro/items/535137094d6ca413b10f)の続きになります

コメントログとギフトログは取得して表示する事ができたので、次はギフトを画面に視覚的に降らせてみようと思います

# アニメーション
よく配信者が使っている[すこアニ](http://sr-com.net/suko_ani/)では見た感じ動き（アニメーション）に[Matter.js](https://brm.io/matter-js/)が使われているっぽいですが、私が使い方を知らないので今回は有名な[GSAP](https://greensock.com/gsap/)を使って再現してみたいと思います

すこアニを使ってギフトを降らせるアニメーションの配信例
![gif3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/310163/8e4d7b0a-9aae-a1ee-f76b-9d8fc019737c.gif)
七瀬ユイ
https://twitter.com/nanase_yui_V
https://www.youtube.com/channel/UCtboPDonPdQTO1vyb_JbXDg
https://www.showroom-live.com/nanase_yui


### GSAP(GreenSock Animation Platform)
アニメーションライブラリで多くのサイトで利用されています

- 使い方  
CDNを使って読み込ませて使うことができます

```html
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.6.0/gsap.min.js"></script>
```

詳しいドキュメントはこちら
https://greensock.com/docs/

今回は単純にギフト画像を上から下に動かすだけになりますので`gsap.to()`を使用して実装していきます

```javascript
// 例　（idと言う名前の要素を3秒かけて横方向に300px分27°回転させながら移動）
gsap.to("#id", {
    rotation: 27, // 27°回転
    x: 300, // 横方向に300px（左から右へ）
    duration: 3 // アニメーションの持続時間（3秒）
})
```

今回は上から下なのでアニメーション方向は`x`ではなく`y`で指定させればOK

# 画面にギフトを降らせてみる
[前回](https://qiita.com/TYoichiro/items/535137094d6ca413b10f)のソースに追加して作っていきます
降らせるメソッドを追加します

```javascript
// （略）
// メッセージの待ち受け
socket.onmessage = (message) => {
    if (message.data === "ACK\tshowroom" || message.data === "Could not decode a text frame as UTF-8.") {
        console.log("疎通確認又はエラー")
    } else {
        // JSONオブジェクトとして取得
        let messageJson = JSON.parse(message.data.replace("MSG\tbXXXXX:XXXXXXXX", ""))
        if (Object.keys(messageJson).length === 9) {
            // コメントログ
            showComment(messageJson)
        } else if (Object.keys(messageJson).length === 12) {
            // ギフトログ
            // showGift(messageJson) 一旦コメントアウト
            // 画面に降らせる
            fallGift(messageJson) // 追加
        } else {
            // テロップなど（今回は無視）
        }
    }
}
```

降らせるメソッドを追加してギフト画像の要素を作成します

```javascript
function fallGift(gift) {
    // 画面幅を取得
    let width = window.innerWidth
    let height = window.innerHeight

    // ギフトの数分ループ
    for (let i = 0; i < gift.n; i++) {
        // 要素のID
        let id = `gift_${gift.u}_${gift.g}_${i}`
        // ギフト画像の要素を作成
        let giftImgElement = document.createElement('img')
        // 画像を設定
        giftImgElement.src = `https://image.showroom-cdn.com/showroom-prod/assets/img/gift/${gift.g}_s.png`
        // IDを設定
        giftImgElement.setAttribute('id', id)
        // 配置位置を設定
        giftImgElement.style.position = 'absolute'
        giftImgElement.style.top = '-25px' // 画面外に配置
        giftImgElement.style.left = getRandomNum(10, width - 70) + 'px' // ランダムに配置
        // ギフト要素を画面に追加
        document.getElementById('fallGiftArea').append(giftImgElement)
    }
}

// 指定した数値間のランダム値を取得
function getRandomNum(min, max) {
    min = Math.ceil(min)
    max = Math.floor(max)
    return Math.floor(Math.random() * (max - min + 1) + min)
}
```

HTML画面の方も少し修正します

```html
<!-- 追加 -->
<div id="fallGiftArea"></div>
<div>
    <div id="comment"></div>
    <div id="gift"></div>
</div>
```

次にGSAPを使って配置した画像を上から下に移動させて降ってくるみたいにします

```javascript
function fallGift(gift) {
    // 画面幅を取得
    let width = window.innerWidth
    let height = window.innerHeight

    // ギフトの数分ループ
    for (let i = 0; i < gift.n; i++) {
        // 要素のID
        let id = `gift_${gift.u}_${gift.g}_${i}`
        // ギフト画像の要素を作成
        let giftImgElement = document.createElement('img')
        // 画像を設定
        giftImgElement.src = `https://image.showroom-cdn.com/showroom-prod/assets/img/gift/${gift.g}_s.png`
        // IDを設定
        giftImgElement.setAttribute('id', id)
        // 配置位置を設定
        giftImgElement.style.position = 'absolute'
        giftImgElement.style.top = '-25px' // 画面外に配置
        giftImgElement.style.left = getRandomNum(10, width - 70) + 'px' // ランダムに配置
        // ギフト要素を画面に追加
        document.getElementById('fallGiftArea').append(giftImgElement)

        // 動きを追加
        // 動かす要素IDを指定
        TweenMax.to(`#${id}`, {
            duration: getRandomNum(2, 5), // 2秒～5秒の間で移動
            rotation: getRandomNum(90, 720), // 回転角度
            y: height - 60, // 落ちる高さ
            onComplete: () => {
                document.getElementById(id).remove() // 終わったら要素を削除
            },
        })
    }
}
```

# DEMO
動かしてみるとこのようになります
現状アニメーションの移動速度が2秒から5秒の4パターンしかないので若干不自然な感じがします
ミリ秒単位でランダムに指定してあげれば多少改善されるのではない気がします
![gif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/310163/cf1c05f2-9c85-51f1-6a07-46cdffebb648.gif)

### Tips

下記スクリプトの部分に自分の好きな画像を設定してあげることでデフォルトの星・種以外も降らせる事ができるのでチャレンジしてみてみるのもいいかと思います

```javascript
// （略）

// 画像を設定
giftImgElement.src = `https://image.showroom-cdn.com/showroom-prod/assets/img/gift/${gift.g}_s.png`

// （略）
```

# 最後に
今のままだと有料ギフトも無料ギフトも同じように降ってくるアニメーションなので、有料・無料で動きを変えたら面白そうですね