马黑黑 发表于 2024-4-3 12:09

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>

马黑黑 发表于 2024-4-3 12:11

<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">&lt;<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>&gt;&lt;<span class="tDarkRed">/canvas</span>&gt;</cl-cd>
<cl-cd data-idx="2">&nbsp;</cl-cd>
<cl-cd data-idx="3">&lt;<span class="tDarkRed">script</span>&gt;</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">&nbsp;</cl-cd>
<cl-cd data-idx="9"><span class="tBlue">function</span> flash() {</cl-cd>
<cl-cd data-idx="10">&nbsp; &nbsp; tctx.clearRect(0, 0, cw2, ch2);</cl-cd>
<cl-cd data-idx="11">&nbsp; &nbsp; <span class="tBlue">for</span> (<span class="tBlue">var</span> i = 0; i &lt; ch2 / 20; i++) {</cl-cd>
<cl-cd data-idx="12">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">for</span> (<span class="tBlue">var</span> j = 0; j &lt; cw2 / 20; j++) {</cl-cd>
<cl-cd data-idx="13">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tctx.save();</cl-cd>
<cl-cd data-idx="14">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tctx.fillStyle = `#${<span class="tRed">Math</span>.random().toString(16).substr(-6)}`;</cl-cd>
<cl-cd data-idx="15">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">var</span> x = 20 * i + 10, y = 20 * j + 10, r = 6;</cl-cd>
<cl-cd data-idx="16">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tctx.beginPath();</cl-cd>
<cl-cd data-idx="17">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tctx.arc(x, y, r, 0, <span class="tRed">Math</span>.PI*2);</cl-cd>
<cl-cd data-idx="18">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tctx.fill();</cl-cd>
<cl-cd data-idx="19">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tctx.restore();</cl-cd>
<cl-cd data-idx="20">&nbsp; &nbsp; &nbsp; &nbsp; }</cl-cd>
<cl-cd data-idx="21">&nbsp; &nbsp; }</cl-cd>
<cl-cd data-idx="22">&nbsp; &nbsp; ctx.clearRect(0, 0, cw1, ch1);</cl-cd>
<cl-cd data-idx="23">&nbsp; &nbsp; <span class="tBlue">var</span> pattern = tctx.createPattern(tcanv, <span class="tMagenta">'repeat'</span>);</cl-cd>
<cl-cd data-idx="24">&nbsp; &nbsp; ctx.fillStyle = pattern;</cl-cd>
<cl-cd data-idx="25">&nbsp; &nbsp; ctx.fillRect(0, 0, cw1, ch1);</cl-cd>
<div class="tGreen"><cl-cd data-idx="26">&nbsp; &nbsp; /* 仅描边</cl-cd>
<cl-cd data-idx="27">&nbsp; &nbsp; ctx.strokeStyle = pattern;</cl-cd>
<cl-cd data-idx="28">&nbsp; &nbsp; ctx.lineWidth = 40;</cl-cd>
<cl-cd data-idx="29">&nbsp; &nbsp; ctx.strokeRect(0, 0, cw1, ch1);</cl-cd>
<cl-cd data-idx="30">&nbsp; &nbsp; */</cl-cd></div>
<div class="tGreen"><cl-cd data-idx="31">&nbsp; &nbsp; /* 动态</cl-cd>
<cl-cd data-idx="32">&nbsp; &nbsp; requestAnimationFrame(flash);</cl-cd>
<cl-cd data-idx="33">&nbsp; &nbsp; */</cl-cd></div>
<cl-cd data-idx="34">};</cl-cd>
<cl-cd data-idx="35">&nbsp;</cl-cd>
<cl-cd data-idx="36">flash();</cl-cd>
<cl-cd data-idx="37">&nbsp;</cl-cd>
<cl-cd data-idx="38">&lt;<span class="tDarkRed">/script</span>&gt;</cl-cd>
</div>

马黑黑 发表于 2024-4-3 12:11

本帖最后由 马黑黑 于 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:11

本帖最后由 马黑黑 于 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 12:12

本帖最后由 马黑黑 于 2024-4-3 13:00 编辑

其他说明

cnavas画布的双缓存技术不一定用于pattern图案绘制,任何复杂的动画都可以使用。本文是想一炮两响,就将实例做成了酱紫。

函数 flash 本意是令小小圆球不断闪烁,当然这里的闪烁是一种追求的效果,不是画布受性能影响而产生的闪烁。一楼之所以不启用闪烁功能,主要是为了方便阅读。有兴趣的朋友,可以将代码拿到 pencil code 环境去运行,运行前将第 31、33 行的代码注释符号去掉。

红影 发表于 2024-4-3 13:32

又弄出一块画板,可以把所做的工作分步骤展示了呢。{:4_187:}
这个图案 pattern 的作用相当于把另一块画布的东西弄成块吧,然后调用这些块。

红影 发表于 2024-4-3 13:34

pattern 下的描边很奇特,还以为那些小圆变成描边的,实际不是的,是整个图案只取了边放在四周。

红影 发表于 2024-4-3 13:35

小圆变描边是在tcanv 即画布2中实现的。

马黑黑 发表于 2024-4-3 16:37

红影 发表于 2024-4-3 13:35
小圆变描边是在tcanv 即画布2中实现的。
源拥有的效果,使用者可以用来填充,也可以用来描边,可以用一个矩形去呈现源,也可以是用一个圆来渲染。这些,好比猪肉嘎嘎,你可以只吃猪皮或只吃瘦肉,可以蒸着吃,也可以烤着吃

马黑黑 发表于 2024-4-3 16:38

红影 发表于 2024-4-3 13:34
pattern 下的描边很奇特,还以为那些小圆变成描边的,实际不是的,是整个图案只取了边放在四周。

看9#

马黑黑 发表于 2024-4-3 16:39

红影 发表于 2024-4-3 13:32
又弄出一块画板,可以把所做的工作分步骤展示了呢。
这个图案 pattern 的作用相当于把另一块画布 ...

一楼说的很清楚的。

你也可以这么理解:画布1是餐厅,画布2是后厨。

南无月 发表于 2024-4-3 17:57

开篇眼前一亮,好多漂亮彩色的小圆点。。。
老师新教程必须支持,
不过呢,烧脑的过会再看。先放会假。。。。{:4_199:}

马黑黑 发表于 2024-4-3 18:14

南无月 发表于 2024-4-3 17:57
开篇眼前一亮,好多漂亮彩色的小圆点。。。
老师新教程必须支持,
不过呢,烧脑的过会再看。先放会假。。 ...

重点是pattern图案、双缓存技术。前者很常见,类似于 background 用小图片做背景;后者是一种实现机制,目标是让画布更为流畅、平滑,其实它有点像双簧。

千羽 发表于 2024-4-3 20:24

马黑黑 发表于 2024-4-3 12:11
.mum { position: relative; margin: 0; padding: 10px; font: normal 16px/20px Consolas, Monaco, 'And ...

俺又飘过{:4_187:}

小辣椒 发表于 2024-4-3 20:41

越来越高级了{:4_189:}{:4_178:}

南无月 发表于 2024-4-3 21:00

马黑黑 发表于 2024-4-3 18:14
重点是pattern图案、双缓存技术。前者很常见,类似于 background 用小图片做背景;后者是一种实现机制, ...

{:4_170:}
艾玛,闪得我眼都快花了。。
很快呀,也很流畅。。

南无月 发表于 2024-4-3 21:16

马黑黑 发表于 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 21:23

马黑黑 发表于 2024-4-3 18:14
重点是pattern图案、双缓存技术。前者很常见,类似于 background 用小图片做背景;后者是一种实现机制, ...

这个图案加双缓存技术结合太妙了。。
流畅必须的,动画快速闪得人眼都花了。。
这都不是重点,重点是老师可以有这样的奇思妙想,并且用代码让它实现啊。。{:4_170:}
想象一下
画布1整成圆的。。。闪烁圆点变成三角形,是不是就成一个万花筒。。

南无月 发表于 2024-4-3 21:28

马黑黑 发表于 2024-4-3 12:11
canvas pattern 图案

canvas画布可以将图片标签、视频标签、画布标签等等的内容作为图案 pattern 来绘制 ...

视频标签也可以做为图案绘制?那不是看上去有一排小电视可以看{:4_173:}

马黑黑 发表于 2024-4-3 21:44

南无月 发表于 2024-4-3 21:28
视频标签也可以做为图案绘制?那不是看上去有一排小电视可以看

画面内容是什么就是什么
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: canvas画布 :pattern和双缓存技术应用实例