JSで花火を打ち上げてみた

はじめに


海外のYoutubeで紹介されていた、クリックすると花火が打ち上がる仕様を実装してみました。まだまだJSを理解しきれていない私にちょうど良い内容でした!学びが多かったので日本語でちゃんと整理してもっとJSと仲良くなっていこうと思います。

見た目もおしゃれで実装していて楽しいです♪


全体のコード


JS

let container = document.querySelector('body');

container.addEventListener('click', function(event) {

    let spark = document.createElement('div');
    spark.classList.add('spark');
    spark.style.top = (event.clientY - container.offsetTop) + 'px';
    spark.style.left = (event.clientX - container.offsetLeft) + 'px';
    spark.style.filter = 'hue-rotate(' + Math.random() * 360 + 'deg)';
    container.appendChild(spark);

    for (var i = 0; i <= 7; i++) {
        let span = document.createElement('span');
        span.style.transform = 'rotate(' + (i * 45) + 'deg)';
        spark.appendChild(span);
    }

    setTimeout(function() {
        spark.remove()
    }, 1500)
})

HTML

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
    <title>Document</title>
</head>
<body>
    
    <script src="main.js"></script>
</body>
</html>

CSS

*{
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    background-color: #222;
    overflow: hidden;
    min-height: 100vh;
}

.spark {
    position: absolute;
    width: 40px;
    height: 40px;
    transform: translateY(-20px);
}

.spark span {
    position: absolute;
    width: 2px;
    height: 20px;
    pointer-events: none;
    transform-origin: bottom;
    filter: drop-shadow(0 0 20px #0f0) drop-shadow(0 0 40px #0f0);
}

.spark span::before {
    content: "";
    position: absolute;
    width: 100%;
    height: 100%;
    background-color: #0f0;
    animation: animate 2s ease-in-out forwards;
}
@keyframes animate {
    0% {
        transform: translateY(100%);
    }
    100% {
        transform: translateY(1500%);
    }
}


JSのコード解説

いくつかポイントをおさえながら紹介できればなと思います。

クリックイベント

container.addEventListener('click', function(event) {

    let spark = document.createElement('div');
    spark.classList.add('spark');

ここでクリックした時にdiv要素を作り、そのdivにsparkというクラスを与えています。

座標を取得しスタイルにあてる

spark.style.top = (event.clientY - container.offsetTop) + 'px';
spark.style.left = (event.clientX - container.offsetLeft) + 'px';

変数sparkにクリックした位置の座標をスタイルとして与えています。
座標の取得はclientY、clientXになります。
offsetTop、offsetLeftはcontainerがあてられているのでここでは画面の一番上と画面の左端になります。

図で説明するとこんな感じ
灰色がcontainer(画面)。
offsetTopとoffsetLeftは画面上端と左端になるのでtop0、left0の状態です。
クリックした箇所がtop100px、left500pxです。

なので数値で表すと
top: 100 - 0 + px
left: 500 - 0 + px
になります。

色相環をランダムで生成

Web系を齧っている人なら誰しもがみたことがあるであろうこれ↓

https://www.quackit.com/css/functions/css_hue-rotate_function.cfm

Quackit.com

この色相環を使って色をランダムに出すことができるらしい。

CSSにも色相環のプロパティhue-rotateというものがあってこれを使っていきます。

spark.style.filter = 'hue-rotate(' + Math.random() * 360 + 'deg)';

Math.random()は0 以上 1 未満の数字を返してくれるらしい
1より小さい小数点を返すから360より大きな数字にはならないってわけですね。
これでランダムでいろんな色を出すエフェクトが出来ました。

放射状に8本の線を出す

 for (var i = 0; i <= 7; i++) {
        let span = document.createElement('span');
        span.style.transform = 'rotate(' + (i * 45) + 'deg)';
        spark.appendChild(span);
    }

まずfor文で8回分の繰り返しを行います

let span = document.createElement('span');
ここでspanタグを生成。spanタグのstyleは後ほどCSSの方で調整しています。

span.style.transform = 'rotate(' + (i * 45) + 'deg)';
ここで45度ずつ角度を変えて放射状にspan
タグをずらしています。

spark.appendChild(span);
.sparkに子要素spanを8つ入れています。

setTimeout(function() {
        spark.remove()
    }, 1500)

最後はsetTimeoutを使って1.5秒後に.sparkが消えるようにします。
JSの解説はここでおしまいです

CSSのコード解説

.sparkのスタイル

JSで生成したdiv.sparkに対するスタイルです。

.spark {
    position: absolute;
    width: 40px;
    height: 40px;
    transform: translateY(-20px);
}


一度JSの記述に戻ります

spark.style.top = (event.clientY - container.offsetTop) + 'px';
spark.style.left = (event.clientX - container.offsetLeft) + 'px';

park.style.top
spark.style.left
を使用する際はposition: absolute;もしくはposition: relative;が必要になります。動画内ではabsoluteで記述をしています。relativeにするとクリック一から少しずれたところで生成されてしまいます。

transform: translateY(-20px);
これはクリック位置を花火の中央にずらすためのスタイルです。
.sparkの高さが40pxなのでtransformプロパティで.spark要素を-20pxずらすとクリック位置がちょうど中央に来ます。

spark spanのスタイル

.spark span {
    position: absolute;
    width: 2px;
    height: 20px;
    pointer-events: none;
    transform-origin: bottom;
    filter: drop-shadow(0 0 20px #0f0) drop-shadow(0 0 40px #0f0);
}

transform-origin: bottom;で放射状に広がっているspanタグを円の下側から配置しています。

filter: drop-shadow(0 0 20px #0f0) drop-shadow(0 0 40px #0f0);
ボヤッとした影を配置することで花火が光っているような演出をします。
ちなみに、box-shadowは背景が不透明な要素に影をつけるときに主に使いますが、影は要素の形状に基づくため、透明部分や複雑な形状には適していません。

.spark span::before

.spark span::before {
    content: "";
    position: absolute;
    width: 100%;
    height: 100%;
    background-color: #0f9;
    animation: animate 2s ease-in-out forwards;
}
@keyframes animate {
    0% {
        transform: translateY(100%);
    }
    100% {
        transform: translateY(1500%);
    }
}

spark spanのスタイルでは要素の大きさと影を表現してspark span::beforeではベースとなる色とアニメーションを設定しています。

animation: animate 2s ease-in-out forwards;
@keyframes animate {
0% {transform: translateY(100%);}
100% {transform: translateY(1500%);}
}

keyframesで要素を1の大きさから15倍まで広げて花火が広がる様子を再現しています。
ease-in-outは最初と最後がゆっくり、中間を早くしています。
forwordsはアニメーションが終了した後に、最後の状態を保持する設定です。これがないと、アニメーションが終わった後に元の状態に戻ります。


以上です。

最後に

私自身この記事を書くために、どのコードがどんな動きをしているのかをコードの数値を変えてみたりプロパティの値を変えてみたり試しながら書きました。
このTipsを紹介してるYoutuberさんもいっきにプログラムを完成させるのではなくまずはクリックしたらdiv要素が生成されるか、クリックした箇所に要素が現れるかなど少しずつプログラムを作っています。プログラムを作る過程が見れるのもすごく面白いのですよ〜!

いいなと思ったら応援しよう!