キャンバスとJavaScriptを使用した描画と編集


この先週、私はcanvas apiと粘り込んでいました.私はいくつかの視覚化を一緒に入れて、私の古いコンテンツを介して行った(私は流れのフィールドとノイズアルゴリズムの長さにどこに行く:それをチェックアウト、私は本当にそれを楽しんだ).
私が遊んでいる間、私はツールをグラフ化することについていくつかの考えをまとめていて、グラフ・ツールでユーザーが必要とする最も基本的なものの1つがテキスト入力でタイプする能力であると決めました.キャンバス描画面(またはd3.jsを使用する)の上にHTMLをオーバーレイすることを含む、これをする方法がいくつかあります.代わりに、既存のキャンバスAPIを使用する単純なスクリプトを書くことを選びました.すべてのもののように、目に会うよりも、それに多くがありますが、物事を開始しようとしている場合-まあ、ここに行く.

プロジェクトの設定


始めるには、HTMLとCSSのサンプルを設定する必要があります.それはあまりでなく、明らかに、それは出発点です.
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Map</title>
    <link rel="stylesheet" href="index.css">
    <script type="text/javascript" src="load.js"></script>
</head>
<body>
    <canvas></canvas>
</body>
</html>
CSS用の別のファイルでは、いくつかの基本的なリセット変数といくつかのルートスタイリングを設定しました.それは本当に完全に必要ではありません、しかし、私が始めるとき、私はこれらのものを持つのが好きです.
/** index.css */
:root {
    --root-font-size: 12px;
    --bg: #fafafa;
    --text-color: #333333;
}

/** Reset */
html, body, nav, ul, h1, h2, h3, h4, a, canvas {
    margin: 0px;
    padding: 0px;
    color: var(--text-color);
}
html, body {
    font-family: Roboto, -apple-system, BlinkMacSystemFont, 'Segoe UI', Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
    font-size: var(--root-font-size);
    background: var(--bg);
    height: 100%;
    width: 100%;
    overflow: hidden;
}
*, body, button, input, select, textarea, canvas {
    text-rendering: optimizeLegibility;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    outline: 0;
}
私が本当に最新のCSSについて好きであることの1つは、それのためにどんなツールも本当に必要としないということです.あなたはちょうどroot variablesであなたのwebappから最大限に得ることができます.多くの場合、これらのような小さなプロジェクトでは、私はそれよりずっと遠くに行かない.
実際にこれらの変数を使用してCSSで完全なチューリングロジックを行う方法についての大きなポストがあります.それをチェックアウト、著者は実際に完全な掃海ゲームを使用して.

キャンバスAPI


The canvas element creates a fixed-size drawing surface that exposes one or more rendering contexts, which are used to create and manipulate the content shown. In this tutorial, we focus on the 2D rendering context. Other contexts may provide different types of rendering; for example, WebGL uses a 3D context based on OpenGL ES.


以下を伴うファイルload.jsをつくってください
/** load.js */
var canvas, context;
var text = [''];

function setup() {
    canvas = document.querySelector('canvas');
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;

    context = canvas.getContext('2d');
    context.font = '18px Roboto';
}

function draw() {
    /* draw code */
}

window.onresize = function () {
    if (canvas) {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
    }
}

window.onkeypress = function (e) {
}

window.onkeydown = function (e) {
}

window.onload = function () {
    setup();
}
ここで起こっているカップルもの.まず、onload経由でウィンドウがロードされるまで待機しています.
セットアップが呼び出されると、キャンバスを取得し、ウィンドウの高さ/幅に設定します.ウィンドウがonresize eventを通じて変更されたとき、幅/高さも設定されます.

キープレス/キーダウン


これは編集者なので、キーを押すと何かを書くと思います.OnKeyPressとOnKeyDownコードを次のように更新します.
window.onkeypress = function (e) {
    if (e.key === 'Enter') {
        text.push('');
    } else {
        text[text.length - 1] += e.key;
    }
    draw();
}

window.onkeydown = function (e) {
    if (e.key === 'Backspace' && text.length && text[0].length) {
        let txt = text[text.length - 1];
        txt = txt.slice(0, txt.length - 1);
        text[text.length - 1] = txt;
        if (!txt.length && text.length > 1) {
            text = text.slice(0, text.length - 1);
        }
    }
    draw();
}
これらの関数は、効果的にテキスト状態を管理します.それは包括的ではありませんが、現時点では、テキスト・アレイに変更を加えるためにEnter/Backspaceを入力して、打つような基本的なことをすることができます.

図面


ドローコードに行きましょう.我々はキャンバスにいるときはいつでも、追加の描画の変更を行う前に、画面をクリアするのは適切です.視覚化と生成アートでは、いくつかのきちんとした効果を作成するためにすでに何があるかを活用することができます.しかし、我々はすべてのキーのストロークと更新のテキストを描画しているので、我々は画面をクリアし、そのような内容をリフレッシュしたい.
function draw() {
    context.clearRect(0, 0, window.innerWidth, window.innerHeight);

    let offset = 0;
    let totalHeight = 0;
    let height = (18 * 1.5); // font * line height

    let items = text.map(txt => {
        let width = context.measureText(txt).width;
        let item = {
            txt,
            width,
            offset
        };
        offset = offset + height;
        totalHeight += height;
        return item;
    });

    let cY = (window.innerHeight / 2) - (totalHeight / 2);
    items.forEach(item => {
        let x = window.innerWidth / 2 - item.width / 2;
        let y = item.offset + cY;
        context.fillText(item.txt, x, y);
    });
}

上記のコードでは、キャンバスAPIのmeasureTextを使用しています.getBoundingBoxClientRectを使用して別のDOM要素にテキストをオフロードするなど、さらに正確になりたいなら、ここでテキストを測定する別の方法があります.私は我々が追加の測定を行うために下にレンダリングコンテキストを利用して終了するように今のためのキャンバスメソッドを選択しました.
いずれにしても、複数行とバックスペースをサポートする最小限のテキスト入力が必要です.続けましょう!

マークダウン


これは、Markdownエディタであることになっています.スペックとしてのMarkdownはかなり最小です、しかし、我々は1つのポストでそれのすべてに着くつもりでありません.私はあなたにこれを拡大するために残します、しかし、現在、我々は仕様のちょうど見出し部分を実行します.
これを行うには、テキスト行を解析し、適切にコンテキストに呼び出しをスワップする必要があります.
テキスト行を解析するには、次のコードを追加します
function parse(txt) {
    let lineHeight = 1.5;
    let headingSize = 32;
    let baseSize = 16;
    if (txt.trim().startsWith('#')) {
        let level = txt.match(/\s*\#/g).length;
        let size = headingSize - (level * 4);
        return {
            font: `bold ${size}px roboto`,
            height: size * lineHeight,
            txt
        };
    } else {
        return {
            font: `${baseSize}px roboto`,
            height: baseSize * lineHeight,
            txt
        };
    }
}
次に、DrawコードでUPDATE関数を呼び出します.
function draw() {
    context.clearRect(0, 0, window.innerWidth, window.innerHeight);

    let offset = 0;
    let totalHeight = 0;

    let items = text.map(txt => {
        let item = parse(txt);
        item.offset = offset;
        offset = offset + item.height;
        totalHeight += item.height;
        return item;
    });

    let centerY = (window.innerHeight / 2) - (totalHeight / 2);
    items.forEach(item => {
        context.font = item.font;
        let width = context.measureText(item.txt).width;
        let x = window.innerWidth / 2 - width / 2;
        let y = item.offset + centerY;
        context.fillText(item.txt, x, y);
    });
}
注意してください、私たちが実際にそれを描画しようとする前に、我々は測定テキストコードをコードに動かしました.これは、context.font = item.fontで前の行のレンダリングコンテキストを変更したためです.我々は、現在のレンダリングコンテキストに基づいて正しい測定を行うことを確認したい.

結論


あそこにある.それは非常に基本的な最小限ですが、それは良いスタートとして任意です.残りの部分を終えるためにコードの詳細を記入するためにあなたに任せます.
いつものように、この記事を楽しんでいただければ、次のようにお願いします.将来の記事のフィードバックやアイデアを歓迎します.私に同様の更新プログラムのフォローを与えることを確認してください!
乾杯!🍺
シリーズの次の部分をチェックアウトすることを忘れないでください!