首页IT科技html画布怎么画矩形([Html5] 用于分析26种画布合成模式(globalCompositeOperation)的演示页面)

html画布怎么画矩形([Html5] 用于分析26种画布合成模式(globalCompositeOperation)的演示页面)

时间2025-06-20 17:06:27分类IT科技浏览4687
导读:作者: zyl910 一、缘由...

作者: zyl910

一            、缘由

Html5画布(Canvas)的上下文(Context2D)提供globalCompositeOperation属性              ,可用于控制图形的绘制时的合成模式            。

查了一下文档                   ,发现多达共有26种合成模式                    。且文字介绍很简略      ,部分模式看不太懂       。

于是我编写了一个功能丰富的演示页面          ,能够随时调整globalCompositeOperation等绘制参数                    ,且有详细信息文本框能用于分析像素合成的计算算法的等         。使用该页面         ,能够很好的学习这26种合成模式                   。

本文重点介绍演示页面的功能      ,及开发过程中的问题处理           。下一篇文章将介绍合成模式的计算算法      。

二                    、合成说明与功能设计

2.1 MDN文档说明

下图是MDN的globalCompositeOperation属性的说明文档的截图                  。可见                    ,对于每一种合成模式            ,只是用一段话做一下简介而已              。

文档上共列出了26种合成模式:

source-over source-in source-out source-atop destination-over destination-in destination-out destination-atop lighter copy xor multiply screen overlay darken lighten color-dodge color-burn hard-light soft-light difference exclusion hue saturation color luminosity

还好每一种模式都配了一张图片范例   ,让人稍微有一点头绪   。

每一种合成模式的附图                    ,由3张子图片所组成               ,分别是“existing content              ”(现有内容)       、“new content                   ”(新内容)         、“[name]      ”(合成模式的名称,如“source-over          ”)                  。即将“子图1(existing content)                    ”的上面绘制“子图2(new content)         ”时                 ,该合成模式的处理结果是“子图3([name])      ”                 。

每个子图的左上角区域                  ,还有一个小范例   ,演示了 蓝红方块的合成结果。注意是在蓝色方块(子图1:existing content)的上面绘制红色方块(子图2:new content)的              ,且红色方块向左上角偏移了一点点                   ,这样便于观察非重叠时的合成情况               。

该文档的后半部分      ,提供了一段简单的JavaScript范例代码          ,演示了xor合成模式                    。摘录:

const canvas = document.getElementById(canvas); const ctx = canvas.getContext(2d); ctx.globalCompositeOperation = xor; ctx.fillStyle = blue; ctx.fillRect(10, 10, 100, 100); ctx.fillStyle = red; ctx.fillRect(50, 50, 100, 100);

这段范例代码简洁明了                    ,演示了globalCompositeOperation的用法   。但有一个小问题——它绘制的蓝红方块的位置关系为“蓝色在红色的左上侧                    ”         ,于是先前附图里“红色在蓝色的左上侧            ”不同      ,不利于对比分析之前的说明            。

于是我感觉有可开发一个新的演示页面                    ,使红蓝方块的位置关系与文档一致            ,并提供合成模式的下拉框   ,便于随时切换合成模式                    ,观察合成结果                    。

2.2 统一术语

MDN文档为了使文章更好懂               ,尽量减少专业术语,便采用了 “existing content   ”                   、“new content                    ”这样浅显的名词       。

但这也带来一些麻烦                 ,因为该领域的专业资料是用专业术语的                  ,用专业术语才能使逻辑上更清晰         。例如 source-over           、destination-over 等合成模式的名称                   。

常用术语是这3个——

Source(源):待绘制的内容           。一般缩写为“S               ”      。即MDN文档里的 子图1“existing content”(现有内容)                  。 Destination(目标):绘制的目标              。一般缩写为“D                 ”   。即MDN文档里的 子图2“new content                  ”(新内容)                  。 Output(输出结果):合成后的结果                 。一般缩写为“O   ”。即MDN文档里的 子图3“[name]              ”(合成模式的名称   ,如“source-over                   ”)               。

使用 Source      、Destination等术语              ,最开始可能会感到比较抽象                    。但熟悉后                   ,会觉得这些概念很简洁                  、实用   。

在D(Destination)的基础上      ,绘制S(Source)图像          ,合成模式的运算用“⊙      ”运算符代替                    ,合成结果为O(Output)            。那么合成处理可以用以下数学式子来简洁的表示: O = D ⊙ S

在很多时候         ,输出结果Output与目标Destination(现有内容      ,existing content)是同一个对象                    。例如Html5的Canva绘图                    ,目标(Destination)是现有的上下文(Context2D)            ,输出结果(Output)也是该上下文   ,只是状态不同(合成后的结果)       。

故O与D其实是等价的                    ,只是人们为了突出表达状态不同               ,才用到了O         。所以很多时候用“D          ”来代替“O                    ”,式子为: D = D ⊙ S

为了进一步简写数学式子                 ,可以将运算符(⊙)与等号(=)写在一起                  ,即:

D ⊙= S

这类似编程语言的“复合赋值运算符         ”——将目标变量D与源值S进行运算   ,运算结果保存在目标变量D里                   。

2.3 演示页面功能设计

首先              ,能显示跟MDN文档一样的红蓝方块                   ,便于对照文档           。

提供合成模式(globalCompositeOperation)的下拉框      ,便于随时切换合成模式          ,观察合成结果      。

红蓝方块能自定义颜色值                  。即提供文本框能随时修改 Source              、Destination 的颜色值              。

红蓝方块支持渐变绘制   。即“to      ”复选框右侧的文本框能输入第2颜色进行渐变                  。勾选“to                    ”复选框时启用渐变                    ,未勾选时不渐变                 。

红蓝方块支持调整透明度。即“Alpha            ”复选框右侧的文本框能输入alpha值(值域为 0~1)               。

为了解决源渐变绘制时Alpha不同问题         ,提供“SourceUseImage   ”复选框                    。当它复选时      ,会先在一个临时图片里绘制好Source                    ,再通过globalCompositeOperation进行绘制   。默认复选            。

这些复选框及文本框能自动生效                    。具体来说            ,当焦点离开文本框时   ,会自动点击“Refresh                    ”按钮                    ,使配置生效       。

显示点击信息的坐标及颜色         。首先               ,会在“Current: (0, 0). Destination sample, Source sample.               ”这一栏显示这些信息,如“Current”是当前点击位置的颜色                 ,其后的括号是点击坐标                  ,“Destination sample                 ”是对应目标像素的颜色   ,“Source sample                  ”是对应源像素的颜色                   。

能显示点击像素的详细信息              ,并尝试给出该合成模式的详细计算过程           。见“Current   ”栏下侧文本框      。

除了像MDN文档那样显示 红框在蓝框左上的合成结果(compositeOffset)外                   ,还展示同一位置的合成结果(composite)      ,并显示了合成前的 destination   、source 图                  。若勾选“SourceUseImage              ”          ,还会显示半透明处理前的source 图              。

在页面背后放上一份颜色名称的表格                    ,便于复制颜色名或rgb值         ,粘贴到自定义颜色值文本框进行测试   。

下面就是演示页面的截图                  。

2.3.1 详细信息文本框

详细信息文本框的内容范例:

Current : RGBA(0.357, 0.106, 0.427, 0.875), Byte(91, 27, 109, 223), #5b1b6ddf, hsl(287, 0.603, 0.267). Pos(132, 94) Destination: RGBA(0.000, 0.255, 1.000, 0.753), Byte(0, 65, 255, 192), #0041ffc0, hsl(225, 1.000, 0.500). Pos(132, 394) Source : RGBA(0.624, 0.000, 0.000, 0.502), Byte(159, 0, 0, 128), #9f000080, hsl(0, 1.000, 0.312). Pos(469, 431) compositeMode: source-over Fa = 1; Fb = 1 - As; Co = As * Cs + Ab * Cb * (1 - As); Ao = As + Ab * (1 - As) Ro = As * Rs + Ab * Rb * (1 - As) = 0.502 * 0.624 + 0.753 * 0.000 * (1 - 0.502) = 0.313 Go = As * Gs + Ab * Gb * (1 - As) = 0.502 * 0.000 + 0.753 * 0.255 * (1 - 0.502) = 0.096 Bo = As * Bs + Ab * Bb * (1 - As) = 0.502 * 0.000 + 0.753 * 1.000 * (1 - 0.502) = 0.375 Ao = As + Ab * (1 - As) = 0.502 + 0.753 * (1 - 0.502) = 0.877 Premultiplie:RGBA(0.314, 0.094, 0.376, 0.878), Byte(80, 24, 96, 224), #501860e0, hsl(287, 0.600, 0.235) Output : RGBA(0.357, 0.110, 0.427, 0.878), Byte(91, 28, 109, 224), #5b1c6de0, hsl(287, 0.591, 0.269)

说明——

Current:当前点击位置的颜色信息及坐标信息                 。格式为“RGBA                  、Byte                 、#rrggbbaa、hsl               、Pos                   ”      ,即分别为“归一化的RGBA值                    、各分量的字节值   、十六进制表示的颜色值            、hsl格式的颜色值                    、点击坐标      ”。 Destination:当前点击位置对应目标像素的颜色信息及坐标信息               。格式为“RGBA       、Byte         、#rrggbbaa                   、hsl           、Pos          ”                    ,即分别为“归一化的RGBA值      、各分量的字节值                  、十六进制表示的颜色值              、hsl格式的颜色值   、点击坐标                    ”                    。后面公式里用小写的“b         ”或“d      ”来表示目标像素   。 Source:当前点击位置对应源像素的颜色信息及坐标信息            。格式为“RGBA                  、Byte                 、#rrggbbaa、hsl               、Pos                    ”            ,即分别为“归一化的RGBA值                    、各分量的字节值   、十六进制表示的颜色值            、hsl格式的颜色值                    、点击坐标            ”                    。后面公式里用小写的“s   ”来表示源像素       。 compositeMode:合成模式         。 公式                   。为了表示在引用公式   ,且为了便于与其他内容隔开                    ,公式的的前面加了多个空格           。 Ro       、Go         、Bo                   、Ao:分别显示 R           、G      、B                  、A 通道的计算过程      。 Premultiplie:显示原始计算结果               ,它是 预乘Alpha(Premultiplie Alpha)模式的颜色值                  。格式为“RGBA              、Byte   、#rrggbbaa                  、hsl                    ”,即分别为“归一化的RGBA值                 、各分量的字节值、十六进制表示的颜色值               、hsl格式的颜色值               ”              。 Output:显示计算结果                 ,它是 直通Alpha(Straight Alpha)模式的颜色值   。格式为“RGBA                    、Byte   、#rrggbbaa            、hsl”                  ,即分别为“归一化的RGBA值                    、各分量的字节值       、十六进制表示的颜色值         、hsl格式的颜色值                 ”                  。

注——

Html5的画布   ,总是使用直通Alpha(Straight Alpha)模式                 。因运算公式的中间结果是预乘Alpha(Premultiplie Alpha)的              ,故最终输出时                   ,需做“预乘Alpha 转 直通Alpha                  ”的转换。 Output计算结果      ,理应与Current相同的          ,而有时会发现字节值会有 12的误差               。这是因为Chrome在运算过程中可能用到了低精度整数运算等速度优化手段                    ,而本页面严格按照公式         ,且使用高精度的浮点运算                    。对于有256种值的8位色彩通道来说      ,有字节值12的误差                    ,其实只是 2/256=1/128≈0.781% 的误差            ,人眼看不出差别   ,故这些速度优化处理很常见   。 只是对常用混合模式                    ,编写了了公式与计算过程            。有些混合模式尚没有编写内容                    。

三                   、问题处理经验

在演示页面的开发过程中               ,遇到了一些事先没想到的问题       。现在分享一下处理经验         。

3.1 源渐变绘制时Alpha不同问题

勾选Source的Alpha复选框,并设为“0.5   ”                 ,若未启用渐变(Source的“to              ”复选框 未勾选时)                  ,可观察到此时绘制的Source区域是正常的   ,各像素的Alpha为0.5左右                   。

若启用启用渐变(Source的“to                   ”复选框 被勾选时)              ,可观察到Source区域的Alpha不太对劲                   ,各像素的Alpha为0.75左右           。

为了解决源渐变绘制时Alpha不同问题      ,提供“SourceUseImage      ”复选框      。当它复选时          ,会先在一个临时图片里绘制好Source                    ,再通过globalCompositeOperation进行绘制                  。默认复选              。

“SourceUseImage          ”勾选时         ,可观察到Source区域的Alpha仍是0.5左右      ,与配置的值相符   。

3.2 Image.onload事件是异步触发的

因“SourceUseImage                    ”复选框                    ,故需要先在另一块区域绘制源图            ,且需要将它转为Image对象   ,这样能便于使用 drawImage 进行绘图                  。

使用Image时要注意                    ,它的加载处理是异步的                 。

若在设置了Image.src后立即进行绘图               ,会发现大多数时候是空的。

为了解决这一问题,应处理onload事件                 ,该事件触发时才表示已加载完毕                  ,可进行后续的绘图等操作               。

代码摘录:

// sourceUseImage let sourceImage = null; if (sourceUseImage) { try{ //let canvasTemp = document.createElement("canvas"); let canvasTemp = document.getElementById(canvasTemp); canvasTemp.style = "display:block"; canvasTemp.width = blockWidth; canvasTemp.height = blockHeight; //canvas.getContext("2d").drawImage(image, 0, 0); let ctxTemp = canvasTemp.getContext("2d"); ctxTemp.save(); ctxTemp.clearRect(0, 0, blockWidth, blockHeight); //ctxTemp.globalAlpha = alphaS; drawRectS(ctxTemp, 0, 0, blockWidth, blockHeight, sColor0, sColor1); ctxTemp.restore(); // to image. sourceImage = new Image(); sourceImage.onload = function() { doRefresh_draw(sourceImage); } sourceImage.src = ctxTemp.canvas.toDataURL("image/png"); } catch(ex) { sourceImage = null; console.log("Make sourceImage fail! ", ex); } } //console.log("sourceUseImage: ", sourceUseImage, "sourceImage: ", sourceImage); if (null!=sourceImage) return; let canvasTemp = document.getElementById(canvasTemp); canvasTemp.style = "display:none"; doRefresh_draw(sourceImage);

3.3 部分合成模式会将区域外的颜色均清除为透明的

使用 source-out           、destination-out 等合成模式时   ,不仅影响了Sourcet覆盖的区域              ,且会将区域外的颜色均清除为透明的                    。

若画布里只需绘制Sourcet                   ,这种情况还可接收   。但若是画布里还有其他内容      ,这种情况会将区域外的其他内容均清理          ,变为透明            。

例如本演示页面上会绘制 compositeOffset      、composite                  、destination              、source 这四类图形                    。因composite是最后进行合成绘制的                    ,当选择使用 source-out   、destination-out 等合成模式时         ,会将compositeOffset                  、destination                 、source 的内容均清除      ,仅保留composite的       。

为了解决这一问题                    ,需要在合成绘制前            ,设置好剪裁区域         。

对于Html5画布来说   ,剪裁功能是这样使用的:先调用beginPath方法开启路径                    ,随后进行rect等操作添加路径形状               ,最后调用clip将路径转为剪裁区域                   。

另外为了在处理后恢复为未剪裁的最初环境,可利用Html5画布的 save/restore 机制           。save方法用于在处理前保存环境                 ,restore方法用于在处理后回复环境      。

代码摘录:

// Top Left: compositeOffset //ctx.globalCompositeOperation = "source-over"; ctx.save(); ctx.globalAlpha = alphaD; drawRectD(ctx, blockLeft, blockTop, blockWidth, blockHeight, dColor0, dColor1); if (useClip) { ctx.beginPath(); ctx.rect(0, 0, blockWidth*2, blockHeight*2); ctx.clip(); } ctx.globalCompositeOperation = compositeModeLast; ctx.globalAlpha = alphaS; if (null==sourceImage) { drawRectS(ctx, blockLeft-blockOffsetX, blockTop-blockOffsetY, blockWidth, blockHeight, sColor0, sColor1); } else { ctx.drawImage(sourceImage, blockLeft-blockOffsetX, blockTop-blockOffsetY); } ctx.restore();

四、小结

源码地址:

https://github.com/zyl910/zhtml5info/blob/master/src/canvas/CanvasComposite.htm

参考文献

MDN《CanvasRenderingContext2D.globalCompositeOperation》. https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation W3C《Compositing and Blending Level 1》. https://www.w3.org/TR/compositing-1/ 么贺贵《canvas像素操作               、save与restore                    、合成与变形》. https://blog.csdn.net/ayhg1/article/details/118071037

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
2013年手机品牌大全(2013年版世界销量TOP16手机)