canvas画布 :pattern和双缓存技术应用实例
<canvas id="canv" width="740" height="300"></canvas><script>
var cw1 = canv.width, ch1 = canv.height, cw2 = 100, ch2 = 100;
var tcanv = document.createElement('canvas');
tcanv.width = cw2, tcanv.height = ch2;
var ctx = canv.getContext('2d'), tctx = tcanv.getContext('2d');
function flash() {
tctx.clearRect(0, 0, cw2, ch2);
for (var i = 0; i < ch2 / 20; i++) {
for (var j = 0; j < cw2 / 20; j++) {
tctx.save();
tctx.fillStyle = `#${Math.random().toString(16).substr(-6)}`;
var x = 20 * i + 10, y = 20 * j + 10, r = 6;
tctx.beginPath();
tctx.arc(x, y, r, 0, Math.PI*2);
tctx.fill();
tctx.restore();
}
}
ctx.clearRect(0, 0, cw1, ch1);
var pattern = tctx.createPattern(tcanv, 'repeat');
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, cw1, ch1);
/* 仅描边
ctx.strokeStyle = pattern;
ctx.lineWidth = 40;
ctx.strokeRect(0, 0, cw1, ch1);
*/
/* 动态
requestAnimationFrame(flash);
*/
};
flash();
</script> <style>
.mum { position: relative; margin: 0; padding: 10px; font: normal 16px/20px Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; color: black; background: rgba(240, 240, 240,.95); box-shadow: 2px 2px 4px gray; border: thick groove lightblue; border-radius: 6px; }
.mum ::selection { background-color: rgba(0,100,100,.35); }
.mum div { margin: 0; padding: 0; }
.mum cl-cd { display: block; position: relative; margin: 0 0 0 50px; padding: 0 0 0 10px; white-space: pre-wrap; overflow-wrap: break-word; border-left: 1px solid silver; }
.mum cl-cd::before { position: absolute; content: attr(data-idx); width: 50px; color: gray; text-align: right; transform: translate(-70px); }
.tRed { color: red; }
.tBlue { color: blue; }
.tGreen { color: green; }
.tDarkRed { color: darkred; }
.tMagenta { color: magenta; }
</style>
<h2>代码:</h2>
<div class='mum'>
<cl-cd data-idx="1"><<span class="tDarkRed">canvas</span> <span class="tRed">id</span>=<span class="tMagenta">"canv"</span> width=<span class="tMagenta">"800"</span> height=<span class="tMagenta">"300"</span>><<span class="tDarkRed">/canvas</span>></cl-cd>
<cl-cd data-idx="2"> </cl-cd>
<cl-cd data-idx="3"><<span class="tDarkRed">script</span>></cl-cd>
<cl-cd data-idx="4"><span class="tBlue">var</span> cw1 = canv.width, ch1 = canv.height, cw2 = 100, ch2 = 100;</cl-cd>
<cl-cd data-idx="5"><span class="tBlue">var</span> tcanv = <span class="tRed">document</span>.createElement(<span class="tMagenta">'canvas'</span>);</cl-cd>
<cl-cd data-idx="6">tcanv.width = cw2, tcanv.height = ch2;</cl-cd>
<cl-cd data-idx="7"><span class="tBlue">var</span> ctx = canv.getContext(<span class="tMagenta">'2d'</span>), tctx = tcanv.getContext(<span class="tMagenta">'2d'</span>);</cl-cd>
<cl-cd data-idx="8"> </cl-cd>
<cl-cd data-idx="9"><span class="tBlue">function</span> flash() {</cl-cd>
<cl-cd data-idx="10"> tctx.clearRect(0, 0, cw2, ch2);</cl-cd>
<cl-cd data-idx="11"> <span class="tBlue">for</span> (<span class="tBlue">var</span> i = 0; i < ch2 / 20; i++) {</cl-cd>
<cl-cd data-idx="12"> <span class="tBlue">for</span> (<span class="tBlue">var</span> j = 0; j < cw2 / 20; j++) {</cl-cd>
<cl-cd data-idx="13"> tctx.save();</cl-cd>
<cl-cd data-idx="14"> tctx.fillStyle = `#${<span class="tRed">Math</span>.random().toString(16).substr(-6)}`;</cl-cd>
<cl-cd data-idx="15"> <span class="tBlue">var</span> x = 20 * i + 10, y = 20 * j + 10, r = 6;</cl-cd>
<cl-cd data-idx="16"> tctx.beginPath();</cl-cd>
<cl-cd data-idx="17"> tctx.arc(x, y, r, 0, <span class="tRed">Math</span>.PI*2);</cl-cd>
<cl-cd data-idx="18"> tctx.fill();</cl-cd>
<cl-cd data-idx="19"> tctx.restore();</cl-cd>
<cl-cd data-idx="20"> }</cl-cd>
<cl-cd data-idx="21"> }</cl-cd>
<cl-cd data-idx="22"> ctx.clearRect(0, 0, cw1, ch1);</cl-cd>
<cl-cd data-idx="23"> <span class="tBlue">var</span> pattern = tctx.createPattern(tcanv, <span class="tMagenta">'repeat'</span>);</cl-cd>
<cl-cd data-idx="24"> ctx.fillStyle = pattern;</cl-cd>
<cl-cd data-idx="25"> ctx.fillRect(0, 0, cw1, ch1);</cl-cd>
<div class="tGreen"><cl-cd data-idx="26"> /* 仅描边</cl-cd>
<cl-cd data-idx="27"> ctx.strokeStyle = pattern;</cl-cd>
<cl-cd data-idx="28"> ctx.lineWidth = 40;</cl-cd>
<cl-cd data-idx="29"> ctx.strokeRect(0, 0, cw1, ch1);</cl-cd>
<cl-cd data-idx="30"> */</cl-cd></div>
<div class="tGreen"><cl-cd data-idx="31"> /* 动态</cl-cd>
<cl-cd data-idx="32"> requestAnimationFrame(flash);</cl-cd>
<cl-cd data-idx="33"> */</cl-cd></div>
<cl-cd data-idx="34">};</cl-cd>
<cl-cd data-idx="35"> </cl-cd>
<cl-cd data-idx="36">flash();</cl-cd>
<cl-cd data-idx="37"> </cl-cd>
<cl-cd data-idx="38"><<span class="tDarkRed">/script</span>></cl-cd>
</div>
本帖最后由 马黑黑 于 2024-4-3 12:35 编辑
canvas双缓存技术
canvas双缓存是这么一个机制:两个canvas画布,一个负责呈现内容(此处成为画布1),另一个在内存中运行、负责实现绘制机制(此处称为画布2)。其作用是预防延时,毕竟画布在绘制复杂的动画时非常耗时,若在指定时间内未能完成,呈现在页面上的效果就会大打折扣,比如闪烁、卡顿、画面空白或花纹紊乱等等。
通常,画布2不作为一个实体标签呈现在页面上,除非需要。二楼代码第五行,创建了一个画布:
var tcanv = document.createElement('canvas');
但后面没有追加到任何地方。tcanv 一旦创建,就是一个事实存在的画布,运行于缓存中,可以像真实的画布一样操作。代码中,函数 flash 从第 10 行到第 21 行,都是在操作 tcanv 即画布2,所做的事情是在上面绘制 5列*5行 的小小圆球,圆球半径为 6 个像素、占位 20px,填充颜色随机。
画布1充当前台呈现的舞台,它可以照搬画布一的一切,在函数的每一次执行中将画布1的成果搬到自己的舞台渲染;因为效果的实际实现过程不再它那里运作,它仅将画布2的结果克隆过来,因此可以保证画面的延续性和流畅性。flash函数代码中,第 22 行到第 25行是针对它的操作,在其上绘制小圆球。
本帖最后由 马黑黑 于 2024-4-3 12:53 编辑
canvas pattern 图案
canvas画布可以将图片标签、视频标签、画布标签等等的内容作为图案 pattern 来绘制图形。首先,画布需要创建 pattern 图案:
let p = ctx.createPattern(源, 是否重复);
//例如: 假如有一个 img 标签 id=“mypic"
let p = ctx.createPattern(mypic, 'repeat');
上例,所创建的pattern图案,第一个参数是 mypic ,即源,这里的 mypic 是一个img标签的id标识。第二个参数放在引号里,因为它是字符串描述语,repeat 标识重复,xy方向都重复,还有另外三个值可选用:repeat-x、repeat-y、no-repeat。
pattern 创建之后,就可以通过 fill() 或 stroke() 使用,例如:
ctx.fillStyle = p;
ctx.fillRect(0,0,400,200);
这将使用已经创建的 pattern 来填充一个 400*200 的矩形。若想填充一个圆,则使用 arc(...) 先描述圆,然后 fill() 即可。
若想以描边方式绘制pattern图案,则将 fillStyle 改为 strokeStyle 即可。
本帖最后由 马黑黑 于 2024-4-3 13:00 编辑
其他说明
cnavas画布的双缓存技术不一定用于pattern图案绘制,任何复杂的动画都可以使用。本文是想一炮两响,就将实例做成了酱紫。
函数 flash 本意是令小小圆球不断闪烁,当然这里的闪烁是一种追求的效果,不是画布受性能影响而产生的闪烁。一楼之所以不启用闪烁功能,主要是为了方便阅读。有兴趣的朋友,可以将代码拿到 pencil code 环境去运行,运行前将第 31、33 行的代码注释符号去掉。
又弄出一块画板,可以把所做的工作分步骤展示了呢。{:4_187:}
这个图案 pattern 的作用相当于把另一块画布的东西弄成块吧,然后调用这些块。 pattern 下的描边很奇特,还以为那些小圆变成描边的,实际不是的,是整个图案只取了边放在四周。 小圆变描边是在tcanv 即画布2中实现的。 红影 发表于 2024-4-3 13:35
小圆变描边是在tcanv 即画布2中实现的。
源拥有的效果,使用者可以用来填充,也可以用来描边,可以用一个矩形去呈现源,也可以是用一个圆来渲染。这些,好比猪肉嘎嘎,你可以只吃猪皮或只吃瘦肉,可以蒸着吃,也可以烤着吃 红影 发表于 2024-4-3 13:34
pattern 下的描边很奇特,还以为那些小圆变成描边的,实际不是的,是整个图案只取了边放在四周。
看9# 红影 发表于 2024-4-3 13:32
又弄出一块画板,可以把所做的工作分步骤展示了呢。
这个图案 pattern 的作用相当于把另一块画布 ...
一楼说的很清楚的。
你也可以这么理解:画布1是餐厅,画布2是后厨。 开篇眼前一亮,好多漂亮彩色的小圆点。。。
老师新教程必须支持,
不过呢,烧脑的过会再看。先放会假。。。。{:4_199:}
南无月 发表于 2024-4-3 17:57
开篇眼前一亮,好多漂亮彩色的小圆点。。。
老师新教程必须支持,
不过呢,烧脑的过会再看。先放会假。。 ...
重点是pattern图案、双缓存技术。前者很常见,类似于 background 用小图片做背景;后者是一种实现机制,目标是让画布更为流畅、平滑,其实它有点像双簧。 马黑黑 发表于 2024-4-3 12:11
.mum { position: relative; margin: 0; padding: 10px; font: normal 16px/20px Consolas, Monaco, 'And ...
俺又飘过{:4_187:} 越来越高级了{:4_189:}{:4_178:} 马黑黑 发表于 2024-4-3 18:14
重点是pattern图案、双缓存技术。前者很常见,类似于 background 用小图片做背景;后者是一种实现机制, ...
{:4_170:}
艾玛,闪得我眼都快花了。。
很快呀,也很流畅。。
马黑黑 发表于 2024-4-3 12:11
canvas双缓存技术
canvas双缓存是这么一个机制:两个canvas画布,一个负责呈现内容(此处成为画布1), ...
画布2只有一小片呢,只有100*100。
它在后台画好之后由画布1调用,四方连续重复出现,就成一楼的800*300的样子。
这又跟PS里图案填充似的。。
打开一个30*30的图片定义为图案。。
填充的时候选择这个小图案,就可以把整个1700*900的画布充满。。{:4_170:}
马黑黑 发表于 2024-4-3 18:14
重点是pattern图案、双缓存技术。前者很常见,类似于 background 用小图片做背景;后者是一种实现机制, ...
这个图案加双缓存技术结合太妙了。。
流畅必须的,动画快速闪得人眼都花了。。
这都不是重点,重点是老师可以有这样的奇思妙想,并且用代码让它实现啊。。{:4_170:}
想象一下
画布1整成圆的。。。闪烁圆点变成三角形,是不是就成一个万花筒。。 马黑黑 发表于 2024-4-3 12:11
canvas pattern 图案
canvas画布可以将图片标签、视频标签、画布标签等等的内容作为图案 pattern 来绘制 ...
视频标签也可以做为图案绘制?那不是看上去有一排小电视可以看{:4_173:} 南无月 发表于 2024-4-3 21:28
视频标签也可以做为图案绘制?那不是看上去有一排小电视可以看
画面内容是什么就是什么