bootstrap实践录:用canvas画图

158 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第11天,点击查看活动详情
因工作需要,最近研究了一下 canvas 画图。

概述

最近业主方对网页提了需求,需要添加地图展示的功能,不一定要使用真实的地图,使用示意图即可。于是着手研,找到了 canvas。

一般地,通过<canvas> 标签定义画布,再使用 javascript 画图,即可显示图形或图像文件,画布的坐标和 MFC 图画是一致的,左上角为原点(0, 0),X轴向右,Y轴向下。

代码

使用 canvas 的模板方式如下:

  • 创建画面,包括ID、大小。
  • 获取 canvas 上下文,利用其提供的函数实现画图功能。

模板片段如下:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>foobar</title>
    </head>
    <body>
        <canvas id="canvas" width="800" height="600"></canvas>
        
        <script type="text/javascript">
            var canvas = document.querySelector("#canvas")
            var ctx = canvas.getContext('2d')
            // ...
        </script>
    </body>
</html>

为方便使用,将常用的线段、圆形、三角形、文字等封装成函数。

总体看,将底层的绘制函数封装起来,对外提供的参数有坐标、颜色等,下面一一介绍。

文字

文字使用fillText函数在指定的坐标上绘制,用font定义字体及大小,fillStyle指定颜色。代码如下:

function drawText(x, y, text, color)
{
    ctx.beginPath()
    ctx.font = "30px Arial"; // 如何只指定大小,不指定字体?  Verdana Arial
    ctx.fillStyle = color
    ctx.fillText(text, x, y)
    ctx.closePath()
}

线段

画线段比较简单,用moveTo定位到起点,再用lineTo指定终点,填充颜色用strokeStyle函数,最后用stroke函数绘制。有此基础,后面的图形画法就类似了,代码如下:

function drawLine(x1, y1, x2, y2, color)
{
    ctx.beginPath();
    ctx.strokeStyle = color;
    ctx.lineWidth = 2;
    ctx.moveTo(x1, y1);
    ctx.lineTo(x2, y2);
    ctx.stroke();
    ctx.closePath()
}

圆形

绘制图形使用画弧度的arc函数,从0到2π画即得到圆。该函数可指定圆的外边颜色和填充颜色,代码如下:

function drawCircle(x, y, r, bcolor, fcolor)
{
    ctx.beginPath()
    ctx.lineWidth = 2;
    ctx.arc(x, y, r, 0, 2*Math.PI)
    ctx.strokeStyle = bcolor // 线的颜色
    ctx.fillStyle = fcolor // 填充的颜色
    ctx.fill()
    ctx.stroke()
    ctx.closePath()
}

三角形

绘制三角形提供2个接口,一是指定三个顶点的坐标,二是给出一个坐标和连长,函数自动推导顶点坐标,代码如下:

function drawTriangle1(x1, y1, x2, y2, x3, y3, bcolor, fcolor)
{
    ctx.beginPath();
    ctx.moveTo(x1, y1);
    ctx.strokeStyle = bcolor
    ctx.lineTo(x2, y2);
    ctx.lineTo(x3, y3);
    ctx.lineTo(x1, y1);
    ctx.fillStyle = fcolor;
    ctx.fill();
    ctx.stroke();
    ctx.closePath()
}
​
// 给出中心点和边长,画三角形(等腰,非等边)
function drawTriangle(x1, y1, width, bcolor, fcolor)
{
    half = width * 0.5
    ctx.beginPath();
    ctx.moveTo(x1 - half, y1 + half);
    ctx.strokeStyle = bcolor
    ctx.lineTo(x1 + half, y1 + half);
    ctx.lineTo(x1, y1 - half);
    ctx.lineTo(x1 - half, y1 + half);
    ctx.fillStyle = fcolor;
    ctx.fill();
    ctx.stroke();
    ctx.closePath()
}

带箭头线段

带箭头线段较复杂,参考了网上资料作了修改。这里指定了箭头的角度和箭头长度,代码如下:

function drawLineArrow(x1, y1, x2, y2, color)
{
    theta = 45
    headlen = 7
    width = 1
​
    var angle = Math.atan2(y1 - y2, x1 - x2) * 180 / Math.PI,
    angle1 = (angle + theta) * Math.PI / 180,
    angle2 = (angle - theta) * Math.PI / 180,
    topX = headlen * Math.cos(angle1),
    topY = headlen * Math.sin(angle1),
    botX = headlen * Math.cos(angle2),
    botY = headlen * Math.sin(angle2);
​
    ctx.save();
    ctx.beginPath();
​
    var arrowX = x1 - topX,
    arrowY = y1 - topY;
​
    ctx.moveTo(arrowX, arrowY);
    ctx.moveTo(x1, y1);
    ctx.lineTo(x2, y2);
    arrowX = x2 + topX;
    arrowY = y2 + topY;
    ctx.moveTo(arrowX, arrowY);
    ctx.lineTo(x2, y2);
    arrowX = x2 + botX;
    arrowY = y2 + botY;
    ctx.lineTo(arrowX, arrowY);
    ctx.strokeStyle = color;
    ctx.lineWidth = width;
    ctx.stroke();
    ctx.restore();
}

将前面的函数综合起来进行测试,测试代码如下:

// 单独测试
// 圆
drawCircle(100, 100, 20, "#FF0000", "#FFFF00")
drawCircle(150, 100, 20, "#0000FF", "#FFFFFF")
drawCircle(200, 100, 20, "#0000FF", "#0000FF")
// 圆结束
​
drawTriangle1(100, 70, 200, 70, 150, 10, "#FF8000", "#FF8000")drawTriangle(150, 200, 40, "#FF8000", "#FF8000")drawLine(50, 10, 150, 10, "#0000FF");
drawLine(150, 10, 200, 20, "#FFD700");
​
drawLineArrow(200, 20, 250, 100, "#FFD700");
drawLineArrow(250, 100, 250, 150, "#FFD700");
​
drawText(0, 150, "hello world", "#000000");

效果图如下:

image-20220609151829009.png

小结

目前主要研究如何绘制一些简单的图形。但对于如何快速画出大量图形,且足够性能要求,还是未知数。

工程中是否能用 canvas,有待继续研究。