html画布怎么画矩形([Html5] 用于分析26种画布合成模式(globalCompositeOperation)的演示页面)
作者: 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版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!