Javascript HTML5 Canvas GlobalComposite用于叠加渐变的操作没有增加到更高的强度?

Javascript HTML5 Canvas GlobalComposite用于叠加渐变的操作没有增加到更高的强度?,javascript,canvas,html5-canvas,heatmap,Javascript,Canvas,Html5 Canvas,Heatmap,我目前正在进行修复,我想知道是否有人知道使用的2d渲染上下文是否可以实现以下效果 我有一个从黑色(alpha 0.5)到透明40像素半径的径向渐变。径向梯度的中心位于x=50,y=50 我有另一个径向渐变,从黑色(alpha 0.5)到透明的40像素半径。径向梯度的中心位于x=80,y=50 这两个渐变重叠。我现在的问题是:重叠区域加在一起,导致alpha值高于径向梯度中心,从而显示错误的数据(例如,由于梯度之间的添加,热图中的区域更热) 看看下面的内容,通过在控制台中执行它,您可以看到问题

我目前正在进行修复,我想知道是否有人知道使用
的2d渲染上下文是否可以实现以下效果

  • 我有一个从黑色(alpha 0.5)到透明40像素半径的径向渐变。径向梯度的中心位于x=50,y=50
  • 我有另一个径向渐变,从黑色(alpha 0.5)到透明的40像素半径。径向梯度的中心位于x=80,y=50
这两个渐变重叠。我现在的问题是:重叠区域加在一起,导致alpha值高于径向梯度中心,从而显示错误的数据(例如,由于梯度之间的添加,热图中的区域更热)

看看下面的内容,通过在控制台中执行它,您可以看到问题所在

预期的行为将是: 最暗的区域是渐变中心,两个渐变的重叠区域合并但不相加

在看到所有GlobalComposite操作都没有产生预期的行为后,我尝试了这些操作的组合。 我认为可能的一种方式是:

  • 绘制第一个渐变
  • 使用复合操作“目标输出”
  • 绘制第二个渐变->从第一个渐变中减去重叠区域
  • 使用复合操作“源代码结束”
  • 再次绘制第二个渐变
但不幸的是,我没有找到一个有效的组合。我很想听到你的反馈,提前谢谢

PS:我知道这可以通过手动操作像素来实现,但我想知道是否有更简单、更优雅、更快速的解决方案。

这些都是你找到的。据我所知,没有手动操作,您无法合成比这更好的内容。如果你更新你的问题,明确描述你想要如何混合像素,我会相应地更新我的答案

那么每像素的解决方案是什么呢

有两种主要的基于像素的方法可以解决这个问题

  • 利用隐藏的上下文并与手动功能混合

  • 编写一个手动计算渐变并应用自定义混合函数的函数

  • 第一个选项比第二个选项更通用,因为您可以使用普通画布方法绘制任何您喜欢的内容,然后将此画布混合到可见画布上。有关如何将一个上下文混合到另一个上下文的好主意,请参见@Phrogz项目

    当您需要以画布默认情况下不方便的方式绘制时,第二个选项是必需的

    最大的困难是alpha透明度,因为在径向渐变后面可能有内容。一旦你在背景图像上画了,你几乎不可能看到你在上面画之前是什么,除非你保留了背景的副本。即使是以每像素为单位,您也有困难:将一个图像混合到另一个图像的顶部将不起作用

    从本质上说,这意味着在可见画布上合成多个半透明渐变的图像,无论您选择的是通用合成功能,都是行不通的,除非画布有一个透明的背景

    根据选项1的精神,您可以创建一个空白上下文并在其上渲染多个渐变。然后在上面渲染这个

    根据选项2的精神,如果您能够在渲染之前定义所有点,那么您可以在一次过程中计算图像并从这些点进行混合

    将一次渲染技术与背景上下文相结合,将允许您在可见画布的顶部绘制轮廓,而无需手动读取单个像素,只需写入像素

    据我所知,这远没有那么优雅,但这可能是在2D画布上实现我称之为alpha混合轮廓效果的唯一真实方法


    我认为您需要的每像素混合函数将从源和目标中选择alpha值最大的像素

    if (src.a <= dst.a) {
        result = dst;
    } else {
        result = src;
    }
    

    if(src.a这真的很奇怪,但它做了你想做的事情,却没有涉及到图像数据

    我想到的是,当你在画布上绘制路径时,你需要路径本身在画布上的确切功能。引用规范:

    由于跟踪路径的算法是如何定义的,因此在一次笔划操作中,路径的重叠部分被视为它们的并集是绘制的

    你可以阅读更多关于这方面的内容

    无论如何,你想要的,本质上,是一条模糊的路径,除了弧,你可以划过一次,你会完全得到你想要的效果

    唯一的问题是没有办法在画布上画出模糊的路径,或者几乎没有办法

    代替使用路径本身,我们可以使用路径的阴影来模拟模糊的圆,这些圆遵循与路径相同的规则

    因此,唯一的问题是,你不想看到实际的路径,你只想看到它的阴影。使笔划透明是行不通的:阴影只会绘制,不会以比它所阴影的东西更高的不透明度绘制

    但是阴影确实具有属性
    shadowOffsetX
    shadowOffsetY
    ,它们通常用于将阴影移动一两个像素以产生光源的错觉

    但是,如果我们把阴影画得太远以至于你看不见它们呢?或者更确切地说,如果我们把路径画得太远以至于你看不见它们,你所能看到的只是阴影呢

    好吧,这正好是关键。下面是一个快速结果,您的原始代码位于顶部,阴影是第二个画布:

    var gradient1 = context.createRadialGradient(75,100,2,75,100,80);
    gradient1.addColorStop(0,"yellow");
    gradient1.addColorStop(1,"black");
    
    var gradient2 = context.createRadialGradient(125,100,2,125,100,80);
    gradient2.addColorStop(0,"blue");
    gradient2.addColorStop(1,"black");
    
    context.beginPath();
    context.globalCompositeOperation = "lighter";
    context.globalAlpha = 0.5;
    context.fillStyle = gradient1;
    context.fillRect(0,0,200,200);
    context.fillStyle = gradient2;
    context.fillRect(0,0,200,200);
    context.globalAlpha = 1.0;
    context.closePath();
    
    IE 11 (64-bit Windows 10)
     Rects     =   4 ms
     Arcs      =  35 ms
     Gradients =  57 ms
     Images    =   8 ms
     Shadows   = 160 ms
    
    Edge (64-bit Windows 10)
     Rects     =   3 ms
     Arcs      =  47 ms
     Gradients =  52 ms
     Images    =   7 ms
     Shadows   = 171 ms
    
    Chrome 48 (64-bit Windows 10)
     Rects     =   4 ms
     Arcs      =  10 ms
     Gradients =   8 ms
     Images    =   8 ms
     Shadows   = 203 ms
    
    Firefox 44 (64-bit Windows 10)
     Rects     =   4 ms
     Arcs      =  21 ms
     Gradients =   7 ms
     Images    =   8 ms
     Shadows   = 468 ms
    
    Opera 34 (64-bit Windows 10)
     Rects     =   4 ms
     Arcs      =   9 ms
     Gradients =   8 ms
     Images    =   8 ms
     Shadows   = 202 ms
    
    Mobile Safari (iPhone5, iOS 9)
     Rects     =  12 ms
     Arcs      =  31 ms
     Gradients =  67 ms
     Images    =  82 ms
     Shadows   =  32 ms