>  기사  >  웹 프론트엔드  >  캔버스를 사용하여 부드러운 곡선을 그리는 방법(코드)

캔버스를 사용하여 부드러운 곡선을 그리는 방법(코드)

不言
不言앞으로
2018-10-15 13:55:214325검색

이 글의 내용은 캔버스를 사용하여 부드러운 곡선을 그리는 방법에 관한 것입니다.(코드) 도움이 필요한 친구들이 참고할 수 있기를 바랍니다.

배경 요약

저는 캔버스를 배우거나 프로젝트 개발에서 캔버스를 사용할 때 모든 사람이 작성할 수 있는 스케치패드 가젯을 구현하는 것과 같은 필요성에 직면했어야 한다고 믿습니다.

글쎄요, 캔버스에 익숙한 어린이라면 수십 줄의 코드만으로 이 작업을 수행할 수 있다고 믿습니다. 다음 데모는 간단한 예입니다.

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. 우리는 주로 캔버스 캔버스에서 mousedown, mouseup 및 mousemove의 세 가지 이벤트를 수신합니다. 동시에 isDown 변수도 만들었습니다.

  2. 사용자가 마우스를 누르면(예: 펜 시작) isDown을 true로 설정하고 마우스를 놓으면 false로 설정됩니다(mouseup). 이것의 장점은 사용자가 현재 그리기 상태에 있는지 확인할 수 있다는 것입니다.

  3. 지속적으로 좌표점을 수집합니다. mousemove 이벤트를 통해 마우스의 isDown이 true인 경우에만(즉, 쓰기 상태) 캔버스의 lineTo 메서드를 통해 현재 지점이 이전 지점과 연결되고 그려집니다. 위의 단계를 통해 기본 드로잉 보드 기능을 실현할 수 있습니다. 그러나 모든 것이 그렇게 간단하지는 않습니다. 주의 깊은 어린이 신발은 매우 심각한 문제를 발견할 수 있습니다. 이렇게 그린 선은 들쭉날쭉하고 충분히 매끄럽지 않으며, 빨리 그릴수록 접히는 느낌이 더 강해졌습니다. 공연은 아래 사진과 같습니다.

왜 이런 일이 일어나는 걸까요? 캔버스를 사용하여 부드러운 곡선을 그리는 방법(코드)문제 분석

이 현상의 주요 원인은 다음과 같습니다.

캔버스의 lineTo 메소드를 사용하여 점을 연결합니다. 두 점을 연결하는 것은 곡선이 아니라 직선이므로 그려집니다. 이렇게 하면 폴리라인이 나옵니다.


  • 캔버스를 사용하여 부드러운 곡선을 그리는 방법(코드)
    은 브라우저의 mousemove 이벤트 수집 빈도에 따라 제한됩니다. 브라우저는 짧은 기간마다 현재 마우스의 좌표를 수집합니다. , 마우스가 빠르게 움직일수록 인접한 두 점 사이의 거리가 멀어지므로 "브레이크라인 느낌이 더 뚜렷해집니다."


  • 부드러운 곡선을 그리는 방법은 무엇입니까? 부드러운 곡선 실제로 곡선에 대한 방법이 있습니다. lineTo가 신뢰할 수 없는 경우 2차 베지어 곡선을 그리는 데 사용되는 canvas-quadraticCurveTo의 다른 그리기 API를 사용할 수 있습니다.

  • Quadratic Bezier Curve

quadraticCurveTo(cp1x, cp1y, x, y)quadraticCurveTo 메서드를 호출하려면 제어점을 설명하는 4개의 매개변수가 필요하고 x와 y는 곡선의 끝점입니다.

더 자세한 정보는 MDN에서 찾을 수 있습니다

베지어 곡선을 사용하려고 하므로 데이터가 충분하지 않다는 것이 분명합니다.

2차 베지어 곡선을 완전히 설명하려면 시작점, 제어점 및 end point, 이 데이터는 어디서 오는 걸까요? 캔버스를 사용하여 부드러운 곡선을 그리는 방법(코드)이 정보를 얻는 데 도움이 되는 매우 영리한 알고리즘이 있습니다.

2차 베지어 핵심 포인트를 얻는 알고리즘

이 알고리즘은 이해하기 어렵지 않습니다. 직접 예를 들어 보겠습니다.

우리가 페인팅 중에 총 6개의 마우스 좌표, 즉 A, B, C, D, E, F가 수집되었습니다.

이전 세 지점 A, B, C를 가져와 B의 중간점 B1을 계산합니다. C. A는 시작점, B는 제어점, B1은 끝점입니다. QuadraticCurveTo를 사용하여 2차 베지어 곡선 세그먼트를 그립니다.


  1. 다음으로 점 사이의 중간점 C1을 계산합니다. C와 D는 B1을 시작점으로, C를 제어점으로, C1을 끝점으로 캔버스를 사용하여 부드러운 곡선을 그리는 방법(코드)


  2. 등을 계속해서 그립니다. 마지막 점 F에 도달하면 D와 E의 중간점이 됩니다. 점 D1은 시작점, E는 제어점, F는 베지어 곡선을 끝내는 끝점입니다. 캔버스를 사용하여 부드러운 곡선을 그리는 방법(코드)

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画板中“画”得愉快~我们下次再见:)


위 내용은 캔버스를 사용하여 부드러운 곡선을 그리는 방법(코드)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제