🧿

D3.jsでこっくりさんを実装する

2022/03/24に公開

概要

D3.jsを使ってコックリさんを実装する方法を紹介します。

完成イメージはこちらのデモをご覧ください

コイン(に見立てた黄色い円)をドラッグしようとすると、ドラッグの方向とは関係なくコインがどこかの文字に向かって動き出します。
ドラッグをやめたり、ドラッグの座標(ユーザが実際に押さえている座標)がコインから離れるとコインの移動が止まります。
するとどうでしょう。不思議とコインの動きに沿ってドラッグしてしまい「やだ!コインを押さえると自分の意思とは無関係に手が動くわ!」を再現できるのです。

コードの解説

まずはAからZまでのアルファベットとコインを画面上に配置します。

let svg = d3.select('#container').select('svg')
let svg_chars = svg.append('svg:g')
let chars = []
for(let i = 65; i<91; i++){
    let str = String.fromCharCode(i)
    let x = ~~(window.innerWidth * Math.random())
    let y = ~~(window.innerHeight * Math.random())
    chars.push({
        str : str,
        x : x,
        y : y
    })
    svg_chars.append('svg:text')
        .attr('x', x)
        .attr('y', y)
        .attr('font-size', 30)
        .text(str)
}
let pos_coin = [~~(window.innerWidth / 2 ), ~~(window.innerHeight / 2 )]
let svg_coin = svg.append('svg:circle')
    .attr('r', 40)
    .attr('fill', 'rgba(255,255,0,0.7)')
    .attr("transform", function(){
        return "translate(" + pos_coin + ")";
    });

移動先のアルファベットとコインの移動速度を決める関数を用意します。

let next
const step = 20
let set_next =()=>{
    next = {
        char : chars[~~(chars.length * Math.random())]
    }
    next.dx = ~~((next.char.x - pos_coin[0]) / step)
    next.dy = ~~((next.char.y - pos_coin[1]) / step)
}
set_next()

ドラッグイベントと、定期実行するアニメーション関数を用意します。

let pos_drag = [0,0]
let flg_drag = false;
let coin_drag = d3.drag(event)
    .on('start', function(d,i){
        pos_drag[0] = event.x
        pos_drag[1] = event.y
        flg_drag = true;
    })
    .on('drag', function(d,i){
        pos_drag[0] = event.x
        pos_drag[1] = event.y
    })
    .on('end', function(d,i){
        flg_drag = false;
    });
svg_coin.call(coin_drag);

animate();
function animate(){
    let duration = 200
    if(flg_drag){
        if(~~Math.sqrt(Math.pow(pos_coin[0] - pos_drag[0], 2) + Math.pow(pos_coin[1] - pos_drag[1] , 2)) > 70) flg_drag = false
        pos_coin[0] += next.dx
        pos_coin[1] += next.dy
        svg_coin
        .transition()
        .duration(duration)
        .ease(d3.easeLinear)
        .attr("transform", function(){
            return "translate(" + pos_coin + ")";
        });
        if(~~Math.sqrt(Math.pow(pos_coin[0] - next.char.x, 2) + Math.pow(pos_coin[1] - next.char.y , 2)) < 20) {
            flg_drag = false
            set_next()
        }
    }
    setTimeout(animate, duration);
}

ポイントは下記の4つです。
・コインのドラッグイベント開始によりフラグが立つ
・フラグが立っている間は指示されたアルファベットに向かってコインが動く
・ドラッグの座標(ユーザが実際に押さえている座標)がコインの座標から離れたらフラグを解除する
・指示されたアルファベットにコインが到達したら、次のアルファベットを指示してフラグを解除する

※サイズや時間などの値は適宜変更してください。

補足

今回は説明の簡素化のためアルファベットを配置するだけにしておりますが、
原作(?)に忠実にひらがなにしたり、鳥居のマークを入れても良いと思います。

これを応用して、DTKでは英文で簡単な占いをしてくれるステージを実装しました。

こっくりさんステージはこちらから

サイケデリックなこっくりさん

Discussion