LoginSignup
1
2

More than 1 year has passed since last update.

ボタンタップで一時的に要素を量産する

Posted at

作ったもの

emoteAnim.gif

インスタライブのような同期チャットにエモート機能を載せるということで
JSとCSSアニメーションでひな形の挙動を作ってみました。

コード

HTML
html
<link rel="stylesheet" href="style.css">

<div class="contentsAll">
    <!-- なにもないエリア(表示用) -->
    <div class="none-area"></div>
    <!-- エモートリスト  -->
    <ul class="emotes">
        <li class="emotes__box left">
            <button class="emotes__box-select"></button>
            <div class="floatBox">
                <span class="floatBox__item"></span>
            </div>
        </li>
        <li class="emotes__box upper">
            <button class="emotes__box-select"></button>
            <div class="floatBox">
                <span class="floatBox__item"></span>
            </div>
        </li>
        <li class="emotes__box right">
            <button class="emotes__box-select"></button>
            <div class="floatBox">
                <span class="floatBox__item"></span>
            </div>
        </li>
        <li class="emotes__box under">
            <button class="emotes__box-select"></button>
            <div class="floatBox">
                <span class="floatBox__item"></span>
            </div>
        </li>
    </ul>
</div>

<script type="text/javascript" src="index.js"></script>
CSS
css
ul li {
    list-style: none;
}
button{
    background-color: transparent;
    border: none;
    cursor: pointer;
    width: 100%;
    height:100%;
}


.none-area{
    height:70%;
    background-color: lightgray;
}


.emotes{
    display: flex;
    justify-content: space-around;
    align-items: center;
    padding:5px 10px;
    background-color: #FFF;
}
.emotes__box , .floatBox {
    height:30px;
    width:30px;
}
.emotes__box{
    position:relative;
}
.floatBox{
    position: absolute;
    bottom:70px;
    z-index:3;
}


.floatBox__item{
    display: none;
    width:100%;
    height:100%;
}
.floatBox__item:nth-of-type(n+2){
    display: block;
    position:absolute;
}
.floatBox__item:nth-of-type(odd){
    animation:oddemote 1.5s linear forwards;
}
.floatBox__item:nth-of-type(even){
    animation:evenemote 1.5s linear forwards;
}

.left button , .left .floatBox__item{
    background: url('img/left-arrow.png');
    background-size: contain;
    background-repeat: no-repeat;
}
.upper button , .upper .floatBox__item{
    background: url('img/upper-arrow.png');
    background-size: contain;
    background-repeat: no-repeat;
}
.right button , .right .floatBox__item{
    background: url('img/right-arrow.png');
    background-size: contain;
    background-repeat: no-repeat;
}
.under button , .under .floatBox__item{
    background: url('img/under-arrow.png');
    background-size: contain;
    background-repeat: no-repeat;
}


@keyframes oddemote{
    0%{
        transform:rotate(0deg) scale(1.2);
    }
    25%{
        transform: translate(10px, -25px) rotate(-15deg) scale(1.2);
    }
    35%{
        transform: translate(0px, -35px) rotate(-20deg) scale(1);
    }
    55%{
        transform: translate(-10px, -55px) rotate(15deg) scale(1);
        opacity:0.9;
    }
    75%{
        transform: translate(0px , -75px) rotate(-5deg) scale(0.8);
        opacity:0.8;
    }
    95%{
        transform: translate(-10px , -100px) rotate(-15deg) scale(0.7);
        opacity:0.6;
    }
    100%{
        transform: translate(-12px , -105px) rotate(-15deg) scale(0.2);
        opacity:0;
    }
}
@keyframes evenemote{
    0%{
        transform:rotate(0deg) scale(1.2);
    }
    25%{
        transform: translate(-10px, -25px) rotate(15deg) scale(1.2);
    }
    35%{
        transform: translate(0px, -35px) rotate(20deg) scale(1.1);
    }
    55%{
        transform: translate(10px, -55px) rotate(-15deg) scale(1.1);
        opacity:0.9;
    }
    75%{
        transform: translate(0px , -75px) rotate(5deg) scale(0.9);
        opacity:0.8;
    }
    95%{
        transform: translate(10px , -100px) rotate(15deg) scale(0.7);
        opacity:0.6;
    }
    100%{
        transform: translate(11px , -102px) rotate(-15deg) scale(0.2);
        opacity:0;
    }
}
javascript
javascript
// エモート表示
const selectedEmote = document.getElementsByClassName('emotes__box-select');
const floatEmote = document.getElementsByClassName('floatBox')
for(let i = 0; i < selectedEmote.length; i++){
    selectedEmote[i].addEventListener('click' , displayEmote);
    function displayEmote(){
        const arrayEmote = Array.prototype.slice.call(selectedEmote);
        const emoteIndex = arrayEmote.indexOf(this);
        const clone = floatEmote[emoteIndex].firstElementChild.cloneNode(true);
        const clonedEmote = floatEmote[emoteIndex].appendChild(clone);

        //表示が終わったエレメントを消す
        setTimeout(function(){clonedEmote.style.display = 'none'},2000);
        setTimeout(function(){clonedEmote.remove()},6000);

    }
};

概要

大まかな挙動のゴール

1.エモートボタンを連打できる
2.表示が終わったらエモート(エレメントそのもの)を消す

大まかな仕様解説

  1. エモート表示用のボタンを基準値にして、エモート本体の座標を決める
  2. デフォルトはdisplay:none;としつつ、2つ目以降をdisplay:block;とする。
    ○○:nth-of-type(n+2){display: block;}
  3. ボタンを押下する度にエモート本体(span)要素を量産
  4. setTimeOutで時間差を作り、表示が終わったエモートを順次削除

コアな挙動

JSの部分ですね。

javascript
const selectedEmote = document.getElementsByClassName('emotes__box-select');
const floatEmote = document.getElementsByClassName('floatBox')
for(let i = 0; i < selectedEmote.length; i++){
    selectedEmote[i].addEventListener('click' , displayEmote);
    function displayEmote(){
        const arrayEmote = Array.prototype.slice.call(selectedEmote);
        const emoteIndex = arrayEmote.indexOf(this);
        const clone = floatEmote[emoteIndex].firstElementChild.cloneNode(true);
        const clonedEmote = floatEmote[emoteIndex].appendChild(clone);

        //表示が終わったエモートを消す
        setTimeout(function(){clonedEmote.style.display = 'none'},2000);
        setTimeout(function(){clonedEmote.remove()},6000);

    }
};

まずはボタン要素をクラスで取得、for文を使っておなじみの処理。(いずれmap等に置換え)
getElementsByClassNameで取得した要素は配列でなく
HTMLCollectionという配列っぽい配列でない何か になり
このままだと後に使う「indexOf」が使えないので

javascript
        const arrayEmote = Array.prototype.slice.call(selectedEmote);

Array.prototype.slice.callで配列化します。

javascript
        const emoteIndex = arrayEmote.indexOf(this);
        const clone = floatEmote[emoteIndex].firstElementChild.cloneNode(true);
        const clonedEmote = floatEmote[emoteIndex].appendChild(clone);

配列化した後、クリックされたエモートのindex番号を取得。
floatEmoteで取得した表示用エモートの親要素のindex番号に照らし合わせる
そして、その子要素に現存する表示用エモートの雛形(非表示になっている)をクローン。
appendChildで一番最後にクローンした要素をくっつける。

○○:nth-of-type(n+2){display: block;}
2つ目以降のspan要素は表示される指定になっているので
動的なスタイルの変更やクラスの付替は不要です。

表示が終わった後の削除処理は、もうちょっとうまいことやれそうなのでアドバイスください..

1
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2