zoukankan      html  css  js  c++  java
  • Canvas绘制圆点线段

    最近一个小伙遇到一个需求,客户需要绘制圆点样式的线条。 大致效果是这样的:

    圆点线

    思路一:计算并使用arc填充

    他自己实现了一种思路,然后咨询我有没有更好的思路。 先看看他的思路是如何实现的,大致代码如下:

    // 绘制圆点线,通过计算在线条上进行插值运算,计算出需要绘制圆点的一系列点位
    // 然后调用drawDot方法绘制圆点
     function DrawDottedLine(x1,y1,x2,y2,dotRadius,dotCount,dotColor){
          var dx=x2-x1;
          var dy=y2-y1;
          var spaceX=dx/(dotCount-1);
          var spaceY=dy/(dotCount-1);
          var newX=x1;
          var newY=y1;
          for (var i=0;i<dotCount;i++){
                  drawDot(newX,newY,dotRadius,dotColor);
                  newX+=spaceX;
                  newY+=spaceY;              
           }
           drawDot(x1,y1,3,"red");
           drawDot(x2,y2,3,"red");
        }
    
    // 绘制圆点
        function drawDot(x,y,dotRadius,dotColor){
            ctx.beginPath();
            ctx.arc(x,y, dotRadius, 0, 2 * Math.PI, false);
            ctx.fillStyle = dotColor;
            ctx.fill();              
        }
    
    

    通过上面的简单的示意代码可以看出,绘制逻辑是通过计算直线之间的点位,然后再相应的点上面绘制圆形。
    该方法最终可以达到效果,可是有如下问题:

    • 存在性能问题
    • 如果是贝塞尔曲线曲线,可能会涉及到复杂的运行。 贝塞尔曲线的相关知识,可以参考下文进行了解:
      深入理解贝塞尔曲线

    当然此思路在绘制一些更加复杂的效果的时候,可能会有用。比如沿线绘制五角星,其他任意形状或者图片等等。或者要绘制的是圆圈而不是填充的圆形的效果,也需要使用此方法。

    但是如果只是绘制圆点线,我们可以使用更加简便的方法,主要思路就是使用setLineDash方法+lineCap设置

    思路二 setLineDash方法+lineCap设置

    lineCap介绍

    CanvasRenderingContext2D.lineCap 是 Canvas 2D API 指定如何绘制每一条线段末端的属性。有3个可能的值,分别是:butt, round and square。默认值是 butt。

    使用时,直接赋值即可:

    ctx.lineCap = "butt";
    ctx.lineCap = "round";
    ctx.lineCap = "square";
    
    

    下面从左到右分别是butt, round ,square的效果:

    lineCap

    可以看出 “round”和“square”都是在原本绘制得线段之外扩展了一个半圆和一个矩形,这点在后面会用到。

    相关知识,可以参考这篇文章:
    canvas基础知识回顾

    setLineDash介绍

    Canvas 2D API的CanvasRenderingContext2D接口的setLineDash()方法在填充线时使用虚线模式。 它使用一组值来指定描述模式的线和间隙的交替长度。

    语法如下:

    ctx.setLineDash(segments);
    //segments数组。一组描述交替绘制线段和间距(坐标空间单位)长
    //度的数字。 如果数组元素的数量是奇数, 数组的元素会被复制并重
    //复。例如, `[5, 15, 25]` 会变成 `[5, 15, 25, 5, 15, 25]。`</dd>
    
    

    绘制圆点线原理

    有了上面两个知识点,只需要把两者结合起来,就可以绘制出圆点线,我们首先使用ctx.setLineDash方法把线段分成一段一段得虚线。 然后把lineCap设置为“round”,我们就会得到一段一段得端点是半圆得小线段,代码如下:

                    ctx.beginPath();
            ctx.lineWidth = 5;
            ctx.lineCap = "round"
            ctx.setLineDash([10, 30]);
            ctx.moveTo(0, 50);
            ctx.lineTo(300, 50);
            ctx.lineTo(300, 200);
            ctx.quadraticCurveTo(500,300,400,400);
            ctx.stroke();
    
    

    最终绘制得效果如下图所示:

    image.png

    到此,又朋友可能有疑问,这个也不是圆点线得效果。 其实只需要把上面得代码稍微得修改,让实线线段本身得长度变成0即可,修改代码:

    ...
    ctx.setLineDash([0, 30]);
    ...
    
    

    最终绘制得效果如下图所示:

    image.png

    结合前面 lineCap 得知识点,相信很容易理解。

    如果要绘制方块得效果,也是很容易得,只需要把lineCap 改成"square" 即可:

                    ctx.beginPath();
            ctx.lineWidth = 10;
            ctx.lineCap = "square"
            ctx.setLineDash([0, 30]);
            ctx.moveTo(0, 50);
            ctx.lineTo(300, 50);
            ctx.lineTo(300, 200);
            ctx.quadraticCurveTo(500,300,400,400);
            ctx.stroke();
    
    

    效果如下:

    image.png

    此处有人可能会说,lineCap 为“butt”同样可以做出方块得效果,只需要调整setLineDash得参数即可:

            ctx.beginPath();
            ctx.lineWidth = 10;
            ctx.lineCap = "butt"
            ctx.setLineDash([10, 30]);
            ctx.moveTo(0, 50);
            ctx.lineTo(300, 50);
            ctx.lineTo(300, 200);
            ctx.quadraticCurveTo(500,300,400,400);
            ctx.stroke();
    
    

    确实如此,但是使用“square” 得情况下setLineDash函数的参数的一个值始终是0,而“butt” 的情况下,setLineDash函数的参数的第一个参数值需要随着lineWidth变化而变化,很不方便,而且“butt”的情况下,还会出现尾部可能不是一个方块的效果,如下图:

    image.png

    扩展

    如果要绘制如下得线条样式,应该怎么做呢:

    image.png

    其实也很简单,就是把同一条线段用不同得lineDash和lineCap绘制两次即可,代码如下:

      // 绘制圆点
        ctx.save();
        ctx.beginPath();
        ctx.lineCap = "round";
        ctx.setLineDash([0, 80]);
        ctx.lineWidth = 20;
        ctx.moveTo(50, 50);
        ctx.lineTo(400, 50);
        ctx.lineTo(400, 400);
        ctx.quadraticCurveTo(750, 450, 800, 800);
        ctx.stroke();
        //绘制直线
        ctx.lineCap = "butt";
        ctx.setLineDash([0, 20, 40, 20]);
        ctx.lineWidth = 10;
        ctx.moveTo(50, 50);
        ctx.lineTo(400, 50);
        ctx.lineTo(400, 400);
        ctx.quadraticCurveTo(750, 450, 800, 800);
        ctx.stroke();
    
    

    需要注意的是绘制第二段的时候,需要调整好lineDash的segments的值。

    欢迎关注公众号“ITman彪叔”。彪叔,拥有10多年开发经验,现任公司系统架构师、技术总监、技术培训师、职业规划师。熟悉Java、JavaScript、Python语言,熟悉数据库。熟悉java、nodejs应用系统架构,大数据高并发、高可用、分布式架构。在计算机图形学、WebGL、前端可视化方面有深入研究。对程序员思维能力训练和培训、程序员职业规划有浓厚兴趣。

  • 相关阅读:
    VS学习笔记2
    VS学习笔记
    分享几个有趣的小程序
    关于类型的转换(抄来的 ,留着,感觉有用。)
    现在觉得IT还挺有意思
    DataGrid 查出一个列 按要求显示格式 例如:操作人(地点)
    WPF DataGrid 列显示0,-1(作废、删除)状态,1,2(支出、收入)类型,操作人(在其他表中),如何转换格式。
    WPF DataGrid中鼠标双击某一列,弹出窗体作为(增加、修改、详细)按钮的快捷键。
    “指定的参数已超出有效值的范围”在【 parameterUpdate.Add(new OracleParameter("STATUS", 0));】报错
    WPF StoreDataSetPaginator
  • 原文地址:https://www.cnblogs.com/flyfox1982/p/13072780.html
Copyright © 2011-2022 走看看