ホームページ  >  記事  >  ウェブフロントエンド  >  Canvas (コード) を使用して滑らかな曲線を描くにはどうすればよいですか?

Canvas (コード) を使用して滑らかな曲線を描くにはどうすればよいですか?

不言
不言転載
2018-10-15 13:55:214316ブラウズ

この記事の内容は、Canvas を使って滑らかな曲線を描く方法に関するものです。必要な方は参考にしていただければ幸いです。

背景の概要

Canvas を学習したり、プロジェクト開発で Canvas を使用したりするときに、誰もがこのようなニーズに遭遇したことがあると思います。それは、書き込み可能な小さな描画ボードを実装することです。道具。

まあ、Canvas に慣れている子供にとっては、ほんの数十行のコードでこれを実行できると思います。次のデモは簡単な例です。

nbsp;html>


    <title>Sketchpad demo</title>
    <style>
        canvas {
            border: 1px blue solid; 
        }
    </style>


    <canvas></canvas>
    <script>
        let isDown = false;
        let beginPoint = null;
        const canvas = document.querySelector(&#39;#canvas&#39;);
        const ctx = canvas.getContext(&#39;2d&#39;);

        // 设置线条颜色
        ctx.strokeStyle = &#39;red&#39;;
        ctx.lineWidth = 1;
        ctx.lineJoin = &#39;round&#39;;
        ctx.lineCap = &#39;round&#39;;

        canvas.addEventListener(&#39;mousedown&#39;, down, false);
        canvas.addEventListener(&#39;mousemove&#39;, move, false);
        canvas.addEventListener(&#39;mouseup&#39;, up, false);
        canvas.addEventListener(&#39;mouseout&#39;, up, false);

        function down(evt) {
            isDown = true;
            beginPoint = getPos(evt);
        }

        function move(evt) {
            if (!isDown) return;
            const endPoint = getPos(evt);
            drawLine(beginPoint, endPoint);
            beginPoint = endPoint;
        }

        function up(evt) {
            if (!isDown) return;
            
            const endPoint = getPos(evt);
            drawLine(beginPoint, endPoint);

            beginPoint = null;
            isDown = false;
        }

        function getPos(evt) {
            return {
                x: evt.clientX,
                y: evt.clientY
            }
        }

        function drawLine(beginPoint, endPoint) {
            ctx.beginPath();
            ctx.moveTo(beginPoint.x, beginPoint.y);
            ctx.lineTo(endPoint.x, endPoint.y);
            ctx.stroke();
            ctx.closePath();
        }
    </script>

その実装。ロジックも非常に単純です。

  1. 主にキャンバス上の 3 つのイベント (mousedown、mouseup、mousemove) をリッスンし、isDown 変数も作成します。

  2. #ユーザーがマウスを押したとき(mousedown)はisDownをtrueに設定し、ユーザーがマウスを置いたとき(mouseup)はfalseに設定するかどうかを判断できるのが利点です。ユーザーは現在描画状態にあります。


  3. isDown が true の場合に限り、マウスが通過する座標点を継続的に収集します。書き込み状態では)、現在のポイントは、canvas の lineTo メソッドを通じて前のポイントと接続されて描画されます。


#上記の手順により、次のことが実現できます。基本的な描画ボードの機能ですが、物事はそれほど単純ではありません。注意してください。子供用の靴には非常に深刻な問題が発生する可能性があります。この方法で描かれた線はギザギザで十分に滑らかではなく、速く描くほど感覚が強くなります。折りたたみの。パフォーマンスは次の図に示すとおりです。

Canvas (コード) を使用して滑らかな曲線を描くにはどうすればよいですか?

# なぜこのようなことが起こるのでしょうか?

問題分析

この現象の主な理由は次のとおりです:

  • キャンバスの lineTo メソッドを使用して、 Points では、隣接する 2 つの点を結ぶのは曲線ではなく直線であるため、この方法で描画されるのはポリラインです。


    Canvas (コード) を使用して滑らかな曲線を描くにはどうすればよいですか?

    ##ブラウザのmousemoveイベントの収集頻度によって制限されますが、mousemove中、ブラウザは短い時間ごとに現在のマウスの座標を収集するため、マウスの移動速度が速くなるほど、距離は遠くなります。隣接する 2 つの点の間を収集すると、「破線感」がより顕著になります。

  • 滑らかな曲線を描くにはどうすればよいですか?

実際には、滑らかな曲線を描画する方法があります。 lineTo が信頼できない場合は、canvas の別の描画 API、quadraticCurveTo を使用して、二次ベジェ曲線を描画することができます。

二次ベジェ曲線

quadraticCurveTo(cp1x, cp1y, x, y)

quadraticCurveTo メソッドを呼び出すには、cp1x と cp1y が制御点 、x、および を記述する必要があります。 y は曲線の終点です:

Canvas (コード) を使用して滑らかな曲線を描くにはどうすればよいですか?詳細については、MDN をご覧ください。

ベジェを使用したいので、曲線、明らかに私たちのデータは十分ではありません。

二次ベジェ曲線を完全に記述するには、開始点、制御点、および終了点が必要です。

、これらのデータはどのようにして得られるのでしょうか?

この情報を取得するのに役立つ非常に賢いアルゴリズムがあります

二次ベジェのキー ポイントを取得するためのアルゴリズム

このアルゴリズムを理解するのは難しくありません。ここで直接説明します。例を挙げてみましょう:

絵画内の合計 6 つのマウス座標、つまり A、B、C、D、E、F を収集するとします。
  1. ##前の 3 つの点 A、B、C を取得し、B と C の間の中点 B1 を計算します。A を開始点、B を制御点、B1 を終了点とします。 quadraticCurveTo を使用して二次シェルサール曲線セグメントを描画します。



  2. Canvas (コード) を使用して滑らかな曲線を描くにはどうすればよいですか?##次に、点の中心を計算します。 C と D 点 C1、B1 を始点、C を制御点、C1 を終点として曲線を描き続けます。

    #同様に描画が続き、最後の点 F に到達すると、D と E の中点である D1 を開始点とし、E をコントロールとしてベジェ曲線が終了します。点、F を終点とします。

  3. Canvas (コード) を使用して滑らかな曲線を描くにはどうすればよいですか?

OK,算法就是这样,那我们基于该算法再对现有代码进行一次升级改造:

let isDown = false;
let points = [];
let beginPoint = null;
const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');

// 设置线条颜色
ctx.strokeStyle = 'red';
ctx.lineWidth = 1;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';

canvas.addEventListener('mousedown', down, false);
canvas.addEventListener('mousemove', move, false);
canvas.addEventListener('mouseup', up, false);
canvas.addEventListener('mouseout', up, false);

function down(evt) {
    isDown = true;
    const { x, y } = getPos(evt);
    points.push({x, y});
    beginPoint = {x, y};
}

function move(evt) {
    if (!isDown) return;

    const { x, y } = getPos(evt);
    points.push({x, y});

    if (points.length > 3) {
        const lastTwoPoints = points.slice(-2);
        const controlPoint = lastTwoPoints[0];
        const endPoint = {
            x: (lastTwoPoints[0].x + lastTwoPoints[1].x) / 2,
            y: (lastTwoPoints[0].y + lastTwoPoints[1].y) / 2,
        }
        drawLine(beginPoint, controlPoint, endPoint);
        beginPoint = endPoint;
    }
}

function up(evt) {
    if (!isDown) return;
    const { x, y } = getPos(evt);
    points.push({x, y});

    if (points.length > 3) {
        const lastTwoPoints = points.slice(-2);
        const controlPoint = lastTwoPoints[0];
        const endPoint = lastTwoPoints[1];
        drawLine(beginPoint, controlPoint, endPoint);
    }
    beginPoint = null;
    isDown = false;
    points = [];
}

function getPos(evt) {
    return {
        x: evt.clientX,
        y: evt.clientY
    }
}

function drawLine(beginPoint, controlPoint, endPoint) {
    ctx.beginPath();
    ctx.moveTo(beginPoint.x, beginPoint.y);
    ctx.quadraticCurveTo(controlPoint.x, controlPoint.y, endPoint.x, endPoint.y);
    ctx.stroke();
    ctx.closePath();
}

在原有的基础上,我们创建了一个变量points用于保存之前mousemove事件中鼠标经过的点,根据该算法可知要绘制二次贝塞尔曲线起码需要3个点以上,因此我们只有在points中的点数大于3时才开始绘制。接下来的处理就跟该算法一毛一样了,这里不再赘述。

代码更新后我们的曲线也变得平滑了许多,如下图所示:

Canvas (コード) を使用して滑らかな曲線を描くにはどうすればよいですか?

本文到这里就结束了,希望大家在canvas画板中“画”得愉快~我们下次再见:)


以上がCanvas (コード) を使用して滑らかな曲線を描くにはどうすればよいですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。