作ったもの
インスタライブのような同期チャットにエモート機能を載せるということで
JSとCSSアニメーションでひな形の挙動を作ってみました。
コード
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
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
// エモート表示
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.表示が終わったらエモート(エレメントそのもの)を消す
大まかな仕様解説
- エモート表示用のボタンを基準値にして、エモート本体の座標を決める
- デフォルトはdisplay:none;としつつ、2つ目以降をdisplay:block;とする。
○○:nth-of-type(n+2){display: block;}
- ボタンを押下する度にエモート本体(span)要素を量産
-
setTimeOut
で時間差を作り、表示が終わったエモートを順次削除
コアな挙動
JSの部分ですね。
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」が使えないので
const arrayEmote = Array.prototype.slice.call(selectedEmote);
Array.prototype.slice.call
で配列化します。
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要素は表示される指定になっているので
動的なスタイルの変更やクラスの付替は不要です。
表示が終わった後の削除処理は、もうちょっとうまいことやれそうなのでアドバイスください..