見出し画像

漢字がずっと流れるウェブサイトを作る


背景

Word Cascadeというウェブサイトがあります。

すべて手で入力しているみたいです

眺めていると時間が経つのを忘れてしまう。

「ミノカサゴ」って何だ?

https://ja.wikipedia.org/wiki/%E3%83%9F%E3%83%8E%E3%82%AB%E3%82%B5%E3%82%B4#/media/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB:Lionfish.jpg
毒を持った魚でした。

「ハニカム構造」も面白いな〜

…この感じ、なにかに似ている———

———漢字を見つめているときに似ている。

そうだ、漢字で作ってみよう。もっと発見をするイメージで…

作る

※コードの解説もするので興味がない方は「使い方」まで飛ばしても結構です。

ということで作ります。

やることとしては、

  1. ランダムな漢字を出力するアルゴリズムを考える

  2. 漢字を上から下に流す

  3. ぼかしを実装する

の3つですね。

1. ランダムな漢字を出力するアルゴリズムを考える

過去にランダムな漢字を生成するコードを書いたのですが、

これをそのまま組み込むと…

表示できない文字があまりにも多く、見るに耐えないので、

function getRandomNumberFromRanges(ranges) {
    const totalWeight = ranges.reduce((acc, [min, max]) => acc + max - min + 1, 0);
    let randomWeight = Math.random() * totalWeight;

    for (const [min, max] of ranges) {
        const weight = max - min + 1;
        if (randomWeight < weight) {
            return Math.floor(Math.random() * weight) + min;
        }
        randomWeight -= weight;
    }
}

function randomKanji() {
    const ranges = [[19968, 40908], [11904, 12031], [63744, 64217], [131072, 196607]];
    return String.fromCodePoint(getRandomNumberFromRanges(ranges));
}

function getRandomNumberFromRanges(ranges) {
    const totalWeight = ranges.reduce((acc, [min, max]) => acc + max - min + 1, 0);
    let randomWeight = Math.random() * totalWeight;

    for (const [min, max] of ranges) {
        const weight = max - min + 1;
        if (randomWeight < weight) {
            return Math.floor(Math.random() * weight) + min;
        }
        randomWeight -= weight;
    }
}

function randomKanji() {
    // 変更点
    const ranges = [[19968, 40908], [11904, 12031], [63744, 64217]];
    return String.fromCodePoint(getRandomNumberFromRanges(ranges));
}

にしてrangesの範囲を狭くしました。早く[131072, 196607]にも対応したいです。

良くなりましたね。

2. 漢字を上から下に流す

本当はcanvasを使った方が良いのでしょうが、使えない使い勝手が悪いのですべてhtmlで行います。そのせいで少し重いです。

具体的には、

document.querySelectorAll(".kanji").forEach(kanji => {
    const top = parseFloat(kanji.style.top);
    if (top > 100) {
        kanji.remove();
    } else {
        kanji.style.top = `${top + parseFloat(kanji.style.fontSize) / 20}vh`;
        if (isBlur == 1) {
            kanji.style.filter = `blur(${Math.abs(Number(kanji.style.fontSize.replace("vh", "")) - distance) / fStop + Math.max(5 - top, 0) / 2}vh)`;
        }
        kanji.style.opacity = Math.max(top, 1) / 5;
    }
});

こういったことを毎秒数十回して下に動かしています。

let animation = setInterval(draw, 1000 / 30);

どうしても漢字が突然現れてしまうので、誤魔化すために透明度を徐々に下げていって自然に表示しています。後にぼかしも追加しました。

また、漢字をクリックしたときに辞書に飛びたいので

if (frame % 30 == 0) {
    const newKanji = document.createElement('a');
    newKanji.textContent = randomKanji();
    newKanji.classList.add("kanji");
    newKanji.style.fontSize = `${getRandomArbitrary(1, 10)}vh`;
    newKanji.style.top = "0vh";
    newKanji.style.left = `${getRandomArbitrary(0, 100)}vw`;

    // リンクを追加
    newKanji.href = "https://zi.tools/zi/" + newKanji.textContent;
    newKanji.target = "_blank";
    newKanji.rel = "noopener noreferrer";

    newKanji.draggable = false;
    canvas.appendChild(newKanji);
}

漢字をリンクにしました。

いい感じ!

3. ぼかしを実装する

最後に、漢字を発見するイメージを膨らませるためにぼかしを実装します。

土を掘って宝物を発見する、そんなイメージに近いのかもしれません。

これは多分砂

最初はスライダーを画面の下に表示させようかと思いましたが、イメージを崩さないようにマウスのみの操作で行うようにしました。

実装するにあたって、この動画が参考になりました。

あとは微調整して完成です!

使い方

を開くと

こんな画面になっているはずなので、マウスをクリックしたまま動かしてみてください。横に動かすとピントが合う位置が、縦に動かすとぼかしの強さが変わります。

また、ぼかしをオフにしたいのならば右下のチェックを外してください。

さらに、漢字をクリックすると辞書に飛ぶことができます。

思う存分楽しんでください。

最後に面白かった漢字をご紹介します。

https://zi.tools/zi/%E8%8A%88

???

(おしまい)


よろしければサポートお願いします!いただいたサポートを励ましにがんばります!