zoukankan      html  css  js  c++  java
  • canvas反向裁剪技巧

    我们都知道在canvas 可以通过clip来实现剪裁功能,其步骤一般是先设置要裁剪的区域(路径),然后通过ctx.clip()的实现裁剪,裁剪之后,后续的绘制只能在裁剪的区域显示效果,比如如下一段代码,实现了一个圆形裁剪:

    ctx.beginPath();
    ctx.arc(100,100,50,0,Math.PI*2);
    ctx.clip();
    
    ctx.rect(0,0,200,200);
    ctx.fillStyle='red';
    ctx.fill();
    

    最终效果如下:

     
    裁剪

    有的时候,我们希望能够实现反向裁剪,比如上面例子中,我们希望是圆圈外面是裁剪区域,而不是圆圈内部是裁剪区域。这就是标题所说的反向裁剪。效果如下图所示:

     
    反向裁剪

    如何实现反向裁剪呢?
    笔者通过实践,发现有以下几种思路。

    使用合成模式globalCompositeOperation

    通过设置globalCompositeOperation的值,可以实现类似的反向裁剪的效果。大致思路是:

    • 首先绘制一个图形(比如圆形),该图形外部的区域将会是裁剪区域
    • 设置globalCompositeOperation的值为source-out
    • 然后绘制想要绘制的图形(比如矩形)

    示例代码如下:

     ctx.beginPath();
    
    ctx.arc(100, 100, 50, 0, Math.PI * 2);
    ctx.fillStyle = 'red';
    ctx.fill();
    
    ctx.beginPath();
    ctx.globalCompositeOperation = 'source-out';
    ctx.rect(0, 0, 200, 200);
    ctx.fillStyle = 'red';
    ctx.fill();
    

    最终效果参考上面的图形“反向裁剪”。

    使用clip + clearRect方法

    另外一种思路是使用clip + clearRect方法,大概的思路如下:

    • 首先绘制要绘制的图形(比如矩形)
    • 然后设置要反向裁剪的图形的路径(比如圆形)
    • 然后调用clip ,再调用clearRect方法清除圆形区域的像素。

    示例代码如下:

       ctx.beginPath();
       ctx.rect(0, 0, 200, 200);
       ctx.fillStyle = 'red';
       ctx.fill();
    
       ctx.beginPath();
       ctx.arc(100, 100, 50, 0, Math.PI * 2);
       ctx.clip();
       ctx.clearRect(0, 0, 200, 200);
    

    最终效果参考上面的图形“反向裁剪”。

    利用非零环绕原则

    我们知道非零环绕原则,可以通过调整路径的方向(顺时针和逆时针),来实现挖空的效果,大致思路如下:

    • 首先构建一个大的区域路径(顺时针方向),比如矩形
    • 然后构建一个小的区域路径(逆时针方向),比如圆形
    • 调用clip裁剪,然后绘制图形

    示例代码如下:

    ctx.beginPath();
    ctx.rect(0, 0, 200, 200); //顺时针方向
    ctx.arc(100, 100, 50, 0, Math.PI * 2, true); // 逆时针方向
    ctx.clip();
    
    ctx.beginPath();
    ctx.rect(0, 0, 200, 200);
    ctx.fillStyle = 'red';
    ctx.fill();
    

    arc方法的最后一个参数可以控制顺时针(false)和逆时针(true),而rect方法没有,可以通过moveTo,lineTo,自己构建逆时针的rect方法,如下代码所示:

    function counterclockwiseRect(ctx, x, y, w, h) {
        ctx.moveTo(x, y);
        ctx.lineTo(x, y + h);
        ctx.lineTo(x + w, y + h);
        ctx.lineTo(x + w, y);
        ctx.lineTo(x, y);
    }
    

    最终效果参考上面的图形“反向裁剪”。

    参考文档

    https://stackoverflow.com/questions/22168619/reverse-clipping-in-canvas
    https://stackoverflow.com/questions/18988118/how-can-i-clip-inside-a-shape-in-html5-canvas
    http://caibaojian.com/canvas/21.html(非零环绕原则 )

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

     
    ITman彪叔公众号
  • 相关阅读:
    效果超酷的textarea的输入字数限提示
    【设计模式(七)】结构型模式之桥接模式
    【设计模式(六)】适配器模式
    【设计模式(四)】原型模式
    【设计模式(三)】工厂模式
    【设计模式(二)】单例模式
    【设计模式(一)】设计模式概览与六大设计原则
    【算法刷题】无重复字符的最长子串
    【算法刷题】全排列 II
    【算法刷题】LRU缓存模拟
  • 原文地址:https://www.cnblogs.com/flyfox1982/p/10382815.html
Copyright © 2011-2022 走看看