欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

canvas小画板之平滑曲线的实现

程序员文章站 2022-03-15 14:23:55
canvas小画板之平滑曲线的实现这篇文章主要介绍了canvas小画板之平滑曲线的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 20-08-12...

功能需求

项目需求:需要实现一个可以*书写的小画板

简单实现

对于熟悉canvas的同学来说,这个需求很简单,大致逻辑如下:

1)监听事件pointerdown,pointermove,pointerup

2)标记是否拖拽画线模式变量 isdrawing,在down事件时置为true,up的时候置为false

3)使用canvas的api,设置线条样式,调用绘制线条接口lineto方法

短短几十行代码就能实现:

<!doctype html>
<html>

<head>
    <meta charset=utf-8>
    <style>
        canvas {
            border: 1px solid #ccc
        }

        body {
            margin: 0;
        }
    </style>
</head>

<body style="overflow: hidden;background-color: rgb(250, 250, 250);touch-action: none;">
    <canvas id="c" width="1920" height="1080"></canvas>
    <script>
        var el = document.getelementbyid('c');
        var ctx = el.getcontext('2d');
        //设置绘制线条样式
        ctx.strokestyle = 'red';
        ctx.linewidth = 1;
        ctx.linejoin = 'round';
        ctx.linecap = 'round';
        var isdrawing;//标记是否要绘制
        //存储坐标点
        let lastx, lasty;
        document.body.onpointerdown = function (e) {
            console.log('pointerdown');
            isdrawing = true;
            lastx = e.clientx;
            lasty = e.clienty;
        };
        document.body.onpointermove = function (e) {
            console.log('pointermove');
            if (isdrawing) {
                draw(e.clientx, e.clienty, lastx, lasty);
            }
            lastx = e.clientx, lasty = e.clienty;
        };
        document.body.onpointerup = function (e) {
            if (isdrawing) {
                draw(e.clientx, e.clienty, lastx, lasty);
            }
            lastx = e.clientx, lasty = e.clienty;
            isdrawing = false;
        };

        function draw(x, y, lastx, lasty) {
            ctx.beginpath();
            ctx.moveto(lastx, lasty);
            ctx.lineto(x, y);
            ctx.stroke();
        }
    </script>
</body>
</html>

实现效果如下图:

canvas小画板之平滑曲线的实现

以上就简单的实现了画板功能,如果要求不高的用户可以使用,但一旦遇到有点要求的用户就无法交付这种产品,仔细看是线条折线感太强。

为什么会有折线感呢?

主要原因:

我们调用的api方法lineto是两点连线也就是直线

浏览器对鼠标事件mousemove的采集是有采集频率的,并不是每个鼠标移动经过的每一个像素点都会触发事件。

当鼠标移动的越快,那么两点之间的间隔就越远,那么折线感就更明显。

canvas小画板之平滑曲线的实现

如何能绘制平滑的曲线?

canvas提供的api中是有现成接口的,贝塞尔系列的接口就能满足我们的要求,接下来我们讲一下使用二次贝塞尔曲线绘制平滑曲线。

quadraticcurveto(cpx,cpy,x,y)

二次贝塞尔曲线接口需要四个参数,cpx,cpy是曲线的控制点,x,y是曲线终点。

有人问那曲线的起点在哪里?其实曲线的起点取决于上一操作状态,可以是moveto的位置,或者是lineto的位置,或者是贝塞尔的终点。

那么怎么调用quadraticcurveto,参数怎么传呢?

我们需要找出关键位置,直接用例子告诉大家吧

1)假如我们用鼠标采集到abcdef六个点

2)取前面三个点abc计算,bc的中点b1,以a为起点,b为控制点,b1为终点,那么利用quadraticcurveto可以绘制出这样一条贝塞尔曲线

canvas小画板之平滑曲线的实现

3)接下来计算cd的中点c1,以b1为起点,c为控制点,c1为终点,那么利用quadraticcurveto可以绘制出这样一条贝塞尔曲线

canvas小画板之平滑曲线的实现

4)以此类推,当到了最后一个点时以d1为起点,e为控制点,f为终点,结束贝塞尔绘制。

canvas小画板之平滑曲线的实现

根据算法进行代码改造

ok我们介绍了具体算法的影响,那用该算法对我们前面的代码进行改造:

<!doctype html>
<html>

<head>
    <meta charset=utf-8>
    <style>
        canvas {
            border: 1px solid #ccc
        }

        body {
            margin: 0;
        }
    </style>
</head>

<body style="overflow: hidden;background-color: rgb(250, 250, 250);touch-action: none;">
    <canvas id="c" width="1920" height="1080"></canvas>
    <script>
        var el = document.getelementbyid('c');
        var ctx = el.getcontext('2d');
        //设置绘制线条样式
        ctx.strokestyle = 'red';
        ctx.linewidth = 1;
        ctx.linejoin = 'round';
        ctx.linecap = 'round';
        var isdrawing;//标记是否要绘制
        //存储坐标点
        let points = [];
        document.body.onpointerdown = function (e) {
            console.log('pointerdown');
            isdrawing = true;
            points.push({ x: e.clientx, y: e.clienty });
        };
        document.body.onpointermove = function (e) {
            console.log('pointermove');
            if (isdrawing) {
                draw(e.clientx, e.clienty);
            }

        };
        document.body.onpointerup = function (e) {
            if (isdrawing) {
                draw(e.clientx, e.clienty);
            }
            points = [];
            isdrawing = false;
        };

        function draw(mousex, mousey) {
            points.push({ x: mousex, y: mousey });
            ctx.beginpath();
            let x = (points[points.length - 2].x + points[points.length - 1].x) / 2,
                y = (points[points.length - 2].y + points[points.length - 1].y) / 2;
            if (points.length == 2) {
                ctx.moveto(points[points.length - 2].x, points[points.length - 2].y);
                ctx.lineto(x, y);
            } else {
                let lastx = (points[points.length - 3].x + points[points.length - 2].x) / 2,
                    lasty = (points[points.length - 3].y + points[points.length - 2].y) / 2;
                ctx.moveto(lastx, lasty);
                ctx.quadraticcurveto(points[points.length - 2].x, points[points.length - 2].y, x, y);
            }
            ctx.stroke();
            points.slice(0, 1);

        }
    </script>
</body>

</html>

在原有基础上我们用了一个数组points保存鼠标经过的点,根据算法可知绘制贝塞尔曲线至少要用三个点,绘制过程中维护points数组。

实现效果如下,可见平滑了很多!

canvas小画板之平滑曲线的实现

后续文章:

实现蜡笔效果,实现笔锋效果,画笔性能优化

到此这篇关于canvas小画板之平滑曲线的实现的文章就介绍到这了,更多相关canvas平滑曲线内容请搜索以前的文章或继续浏览下面的相关文章,希望大家以后多多支持!

相关标签: Canvas 平滑曲线