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

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

时间2025-08-02 04:37:18分类IT科技浏览5580
导读:作者: 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
macos catalina怎么升级(macOS Catalina好不好用 macOS Catalina上手体验) 极化码的编码原理(极化码理论)