最新公告
  • 欢迎您光临网站无忧模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • canvas绘制图像轮廓效果

    正文概述 掘金(ITMan彪叔)   2021-03-31   486

    在2d图形可视化开发中,经常要绘制对象的选中效果。 一般来说,表达对象选中可以使用边框,轮廓或者发光的效果。  发光的效果,可以使用canvas的阴影功能,比较容易实现,此处不在赘述。

    绘制边框

    绘制边框是最容易实现的效果,比如下面的图片 canvas绘制图像轮廓效果

    要绘制边框,只需要使用strokeRect的方式即可。效果如下图所示: canvas绘制图像轮廓效果

    这个代码也很简单,如下所示:

         ctx1.strokeStyle = "red";
         ctx1.lineWidth = 2;
         ctx1.drawImage(img, 1, 1,img.width ,img.height)
         ctx1.strokeRect(1,1,img.width,img.height);
    

    绘制轮廓

    问题是,简单粗暴的加一个边框,并不能满足需求。很多时候,人们需要的是轮廓的效果,也就是图片的有像素和无像素的边缘处。如下图的效果所示: canvas绘制图像轮廓效果

    要实现上述效果,最容易想到的思路就是通过像素的计算来判断边缘,并对边缘进行特定颜色的像素填充。但是像素的计算算法并不容易,简单的算法又很难达到预期的效果,而且由于逐像素操作,效率不高。

    考虑到在三维webgl中,计算轮廓的算法思路是这样的:

    1. 先绘制三维模型自身,并在绘制的时候启动模板测试,把三维图像保存到模板缓冲中。
    2. 把模型适当放大,用纯属绘制模型,并在绘制的时候启用模板测试,和之前的模板缓冲区中的像素进行比较,如果对应的坐标处在之前模板缓冲区中有像素,就不绘制纯色。

    依据上述的原理,就可以绘制处三维对象的轮廓了。下面是一个示例效果,(参考stemkoski.github.io/Three.js/Ou…) canvas绘制图像轮廓效果

    在2d canvas里面有类似的原理可以实现轮廓效果,就是使用globalCompositeOperation了。 大体思路是这样的:

    1. 首先绘制放大一些的图片。
    2. 然后开启globalCompositeOperation = 'source-in', 并用纯色填充整个canvas区域,由于source-in的效果,纯色会填充放大图片有像素的区域。
    3. 使用默认的globalCompositeOperation(source-over),用原始尺寸绘制图片。

    绘制放大一些的图片

    通过drawImage的参数可以控制绘制图片的大小,如下所示,drawImage有几个形式:

    1  void ctx.drawImage(image, dx, dy);
    2  void ctx.drawImage(image, dx, dy, dWidth, dHeight);
    3  void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
    

    其中dx,dy 代表绘制的起始位置,一般绘制的时候使用第一个方法,代表绘制的大小就是原本图片的大小。而使用第二个方法,我们可以指定绘制的尺寸,我们可以使用第二个方法绘制放大的图片,代码如所示:

    ctx.drawImage(img, p - s, p  - s, w + 2 * s, h+ 2 * s);
    

    其中p代表图片本身的绘制位置,s代表向左,向上的偏移量,同时图片的宽和高都增加 2 * s

    用纯色填充放大图片的区域

    在上一步绘制的基础上,开启globalCompositeOperation = 'source-in', 并用纯色填充整个canvas区域。 代码如下所示:

     // fill with color
            ctx.globalCompositeOperation = "source-in";
            ctx.fillStyle = "#FF0000";
            ctx.fillRect(0, 0, cw, ch);
    

    最终的效果如下图所示: canvas绘制图像轮廓效果

    为什么会出现这种效果是因为使用了globalCompositeOperation = 'source-in',具体原理可以参考本人的其他文章。

    绘制原始图片

    最后一步就是绘制原始图片,代码如下所示:

      ctx.globalCompositeOperation = "source-over";
      ctx.drawImage(img, p, p, w, h);
    

    首先恢复globalCompositeOperation为默认值 "source-over",然后按照原本的大小绘制图片。

    经过以上步骤,最终的效果如下图所示: canvas绘制图像轮廓效果

    可以看出最终获得了我们要的效果。

    只显示轮廓

    如果我们只想得到图片的轮廓,则可以在最后绘制的时候,globalCompositeOperation 设置为“destination-out”,代码如下:

            ctx.globalCompositeOperation = "destination-out";
            ctx.drawImage(img, p, p, w, h);
    

    效果图如下: canvas绘制图像轮廓效果

    轮廓粗细不一致的问题

    上面的算法实现,是在图片的有像素值区域中心和图片本身的几何中心基本一直,如果图片的有像素值的中心和图片本身的几何中心相差比较大,则会出现轮廓粗细不一致的情况,比如下面这张图:

    canvas绘制图像轮廓效果

    上半部分是透明的,下半部分是非透明的,像素的中心在3/4出,而几何中心在1/2处。使用上面的算法,该图片的轮廓如下: canvas绘制图像轮廓效果

    可以发现上边缘的轮廓宽度变成了0。

    在比如下图, canvas绘制图像轮廓效果

    绘制后上边缘的轮廓比其他边缘的细。 canvas绘制图像轮廓效果

    怎么处理这种情况呢?可以在绘制放大图片的时候,不直接使用缩放,而是在上下左右,上左,上右,下左,下右几个方向进行偏移绘制,多次绘制,代码如下:

      var dArr = [-1, -1, 0, -1, 1, -1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 1], // offset array
     // draw images at offsets from the array scaled by s
     for (var i = 0; i < dArr.length; i += 2) {
         ctx.drawImage(img, p + dArr[i] * s, p + dArr[i + 1] * s, w, h);
      }
    

    再看上面图片的轮廓效果,如下所示: canvas绘制图像轮廓效果

    半透明的情况

    我在其他文章中说过,globalCompositeOperation为"source-in"的时候,source图形的透明度,会影响到目标绘制图形的透明度。所以会导致轮廓的像素值会乘以透明度。比如,我们在绘制放大图的时候,设置globalAlpha = 0.5进行模拟。 最后的绘制效果如下: canvas绘制图像轮廓效果

    可以看到轮廓的颜色变浅了,解决办法就是多绘制几次放大图。比如:

    ctx.globalAlpha = 0.5;
    ctx.drawImage(img, p - s, p  - s, w + 2 * s, h+ 2 * s);
    ctx.drawImage(img, p - s, p  - s, w + 2 * s, h+ 2 * s);
    

    而上面通过偏移的方式绘制的时候,本身都绘制了好多遍,所以不存在这个问题。如下:

      ctx.globalAlpha = 0.5;
      for (var i = 0; i < dArr.length; i += 2) {
         ctx.drawImage(img, p + dArr[i] * s, p + dArr[i + 1] * s, w, h);
      }
    

    如下图所示: canvas绘制图像轮廓效果

    当然,在透明度很低的情况下,使用绘制很多遍的方式,不是很好的解决方案。

    # 使用算法(marching-squares-algorithm) 上面的方法对于有些图片效果就很不好,比如这张图片: canvas绘制图像轮廓效果

    由于其有很多中空的效果,所以其最终效果如下图所示: canvas绘制图像轮廓效果

    但是想要的只是外部的轮廓,而不需要中空部分也绘制上轮廓效果。此时需要使用其他的算法。 直接使用marching squares algorithm 可以获取图片的边缘。这一块的算法具体实现本文不再讲解,后续有机会单独一篇文章进行讲解。 此处直接使用开源的实现。比如可以使用  github.com/sakri/March…,代码如下:

     function drawOuttline2(){
            var canvas = document.createElement('canvas');
            var ctx = canvas.getContext('2d');
            var w = img.width;
            var h = img.height;
            canvas.width = w;
            canvas.height = h;
            ctx.drawImage(img, 0, 0, w, h);
            var pathPoints = MarchingSquares.getBlobOutlinePoints(canvas);
            var points = [];
           
            for(var i = 0;i < pathPoints.length;i += 2){
              points.push({
                x:pathPoints[i],
                y:pathPoints[i + 1],
              })
            }
    
    
            // ctx.clearRect(0, 0, w, h);
            ctx.beginPath();
            ctx.lineWidth = 2;
            ctx.strokeStyle = '#00CCFF';
            ctx.moveTo(points[0].x, points[0].y);
            for (var i = 1; i < points.length; i += 1) {
              var point = points[i];
              ctx.lineTo(point.x,point.y);
            }
            ctx.closePath();
            ctx.stroke();
            
            ctx1.drawImage(canvas,0,0);
          }
    

    首先使用调用MarchingSquaresJS的方法获取img图像的轮廓点的集合,然后把所有的点连接起来。形成轮廓图,最终效果如下: canvas绘制图像轮廓效果

    不过可以看出,MarchingSquares 算法获得的轮廓效果锯齿相对较多的。有光这块算法的优化,本文不讲解。

    总结

    对于没有中空效果的图片,我们一般不采用MarchingSquares算法,而采用前面的一种方式来实现,效率高,而且效果相对更好。 而对于有中空,就会使用MarchingSquares算法,效果相对差,效率也相对低一些,实际应用中,可以通过缓存来降低性能的损耗。

    本文的起源来资源一个2.5D项目,上一张项目图吧: canvas绘制图像轮廓效果

    参考文档

    www.emanueleferonato.com/2013/03/01/… github.com/sakri/March… github.com/OSUblake/ms… users.polytech.unice.fr/~lingrand/M…

    如果对可视化感兴趣, 关注公号“ITMan彪叔” 可以及时收到更多有价值的文章。也可以加微信541002349进行交流。


    下载网 » canvas绘制图像轮廓效果

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元