Diary

Diary

日々学んだことをアウトプットする場として初めてみました

JavaScript で文字をぶっ飛ばす方法

目標

fontBombのようなものを、JavaScript のみを用いて実装したい

実装方法

とりあえず、1つのタグの中の文章を飛ばすことを考える

下の説明で出てくる関数名は、コードの例中での関数名のこと

  1. 1つのタグのままでは文字ごとに別々の動きを出すことが不可能なため、1文字1文字を div タグに入れる(本家では particle タグというものに入っていた)。そのdiv タグには id を振っておく
  2. クリックした位置の座標を取得(爆弾の中心)
  3. クリックした位置に爆弾を置く(putBomb 関数)
  4. 半径 xx 以内にある div タグの id 一覧と、爆弾の中心との相対位置を取得(checkPosition 関数)
  5. 相対位置に応じて、近いほど派手に飛ばす。その際、ランダムに角度を回転させる(scatterTag 関数)

改善ポイント

方法 5 の部分において、css の transform を用いて div タグの移動を実装したが、それだと div タグのメインの位置は(おそらく)変わってないことになってるので、2回目以降爆発させると少し動きおかしい

transform で移動させる際に、「中心に近いほど遠くに飛ばす」ことを実装するために

sqr = dx**2 + dy**2 (dx,dy は中心からの距離)

translateX = POWER * dx / sqr
translateY = POWER * dy / sqr

のようにしたが、もう少し綺麗に見える方法があるかも

コードの例

  • HTML
<div class="conteiner" id="cotainer"></div>

transition の時間を変えると、文字の飛んでく速度変わります

div {
    display: inline-block;
    transition: 0.5s ease;
}

.bomb {
    width: 40px;
    height: 40px;
    line-height: 40px;
    border-radius: 50%;
    border: 2px solid #555;
    background-color: #eee;
    text-align: center;
}
const TEXTCONTENT = `
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ad aliquid iste cumque totam repellat velit quo doloribus quaerat voluptates veritatis praesentium culpa tempore, eum adipisci tempora suscipit perferendis labore obcaecati.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ad aliquid iste cumque totam repellat velit quo doloribus quaerat voluptates veritatis praesentium culpa tempore, eum adipisci tempora suscipit perferendis labore obcaecati.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ad aliquid iste cumque totam repellat velit quo doloribus quaerat voluptates veritatis praesentium culpa tempore, eum adipisci tempora suscipit perferendis labore obcaecati.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ad aliquid iste cumque totam repellat velit quo doloribus quaerat voluptates veritatis praesentium culpa tempore, eum adipisci tempora suscipit perferendis labore obcaecati.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ad aliquid iste cumque totam repellat velit quo doloribus quaerat voluptates veritatis praesentium culpa tempore, eum adipisci tempora suscipit perferendis labore obcaecati.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ad aliquid iste cumque totam repellat velit quo doloribus quaerat voluptates veritatis praesentium culpa tempore, eum adipisci tempora suscipit perferendis labore obcaecati.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ad aliquid iste cumque totam repellat velit quo doloribus quaerat voluptates veritatis praesentium culpa tempore, eum adipisci tempora suscipit perferendis labore obcaecati.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ad aliquid iste cumque totam repellat velit quo doloribus quaerat voluptates veritatis praesentium culpa tempore, eum adipisci tempora suscipit perferendis labore obcaecati.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ad aliquid iste cumque totam repellat velit quo doloribus quaerat voluptates veritatis praesentium culpa tempore, eum adipisci tempora suscipit perferendis labore obcaecati.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ad aliquid iste cumque totam repellat velit quo doloribus quaerat voluptates veritatis praesentium culpa tempore, eum adipisci tempora suscipit perferendis labore obcaecati.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ad aliquid iste cumque totam repellat velit quo doloribus quaerat voluptates veritatis praesentium culpa tempore, eum adipisci tempora suscipit perferendis labore obcaecati.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ad aliquid iste cumque totam repellat velit quo doloribus quaerat voluptates veritatis praesentium culpa tempore, eum adipisci tempora suscipit perferendis labore obcaecati.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ad aliquid iste cumque totam repellat velit quo doloribus quaerat voluptates veritatis praesentium culpa tempore, eum adipisci tempora suscipit perferendis labore obcaecati.
`

const CONTAINER = document.getElementById('cotainer');
const FONT_SIZE = 20;

const RADIUS    = 16000;
const POWER     = 16000;

const textList = TEXTCONTENT.split('');

let bombId = 0;

for ( let idx=0; idx<textList.length; idx++ ){
    let child = document.createElement('div');
    child.textContent   = textList[idx];
    child.id            = idx;
    CONTAINER.appendChild(child);
}

// マウスクリックの座標を渡したら、ある特定の範囲内の要素のIDのリストを返す
function checkPosition( ex, ey, r ){
    let tmp = [];
    for ( let idx=0; idx<textList.length; idx++ ){
        let elm = document.getElementById(`${idx}`);
        if ( elm ){
            let rect = elm.getBoundingClientRect();  // 画面左上を基準とする位置
    
            // document(ページ左上)からの絶対座標
            let y1 = rect.top + window.pageYOffset;
            let x1 = rect.left + window.pageXOffset;
            if ( Math.pow(x1 - ex, 2) + Math.pow(y1 - ey, 2) < r ){
                tmp.push([idx, x1 - ex, y1 - ey]);
            }
        }
    }
    return tmp
}

function scatterTag( array ){
    for ( const idx of array ){
        // console.log(idx)
        let elm = document.getElementById(`${idx[0]}`);

        let sqr = sqrt(idx[1], idx[2]);
        // transform = "translate(" + scatDist(idx[1]) + "px, " + scatDist(idx[2]) + "px) rotate(" + randomAngle() + "deg)";
        transform = "translate(" + POWER*(idx[1])/sqr + "px, " + POWER*(idx[2])/sqr + "px) rotate(" + randomAngle() + "deg)";
        elm.style['transform'] = transform;
    }   
}

function sqrt(x,y ){
    return x**2 + y**2
}

function scatDist( dist ){
    let revDis = 200 / dist;
    return revDis * ( 1 + Math.random() )
}

// 0-360 deg をランダムに返す
function randomAngle() { 
    return 360 * Math.random()
}

function putBomb( x, y ){
    createMark( x,y );
    // console.log(mark.id);
    startTimer( bombId );
    bombId += 1;
}
// id のものを x, y だけ移動する
function createMark( x,y ){
    let mark = document.createElement('div');

    mark.id = 'b' + bombId;

    mark.className = 'bomb';
    mark.style.position = 'fixed';
    mark.style.top = y - FONT_SIZE + 'px';
    mark.style.left = x - FONT_SIZE + 'px';

    mark.textContent = 3;
    CONTAINER.appendChild(mark);
}

let nextRemoveBomb = 0;
function startTimer( bombId ){
    const bombEach = document.getElementById('b' + bombId);

    // HACK
    setTimeout(function() {bombEach.textContent = 2}, 1000);
    setTimeout(function() {bombEach.textContent = 1}, 2000);
    setTimeout(function() {bombEach.textContent = 0}, 3000);
    setTimeout(function() {bombEach.remove}, 3100);
    setTimeout(function() {bombEach.animate({
        opacity: [0, 1]
        }, {
        delay: 500,
        direction: 'reverse',
        duration: 500,
        easing: 'ease-in-out',
        fill: 'forwards'
    })}, 3100);
}

let isDragging = false
let startPosition = null
let lastPosition = null
let requestId = null

// 指定した id を持つタグを消去する(removeChild)
function clearTags( array ){
    for ( tagId of array ){
        // tagId = [id, x, y] にしたんだった
        let elm = document.getElementById( tagId[0] );
        elm.animate({
            opacity: [0, 1]
            }, {
            delay: 500,
            direction: 'reverse',
            duration: 500,
            easing: 'ease-in-out',
            fill: 'forwards'
        })
        // elm.remove();
    }
}

function onClick(e) {

    putBomb( e.clientX, e.clientY );
    
    const withInIndex = checkPosition(e.clientX, e.clientY, RADIUS);
    window.setTimeout( scatterTag, 3000, withInIndex );
    // scatterTag(withInIndex);
    window.setTimeout( clearTags, 3100, withInIndex );
}
document.addEventListener('click', onClick, false);