WebGL Tutorial
and more

CanvasEditor Test

Web组件的次序

当使用highlight-with-trimpre.js时,该文件需放在CanvasEditor.js的下面。

<script type="module" src="/js/esm/web-widgets/canvas-editor/CanvasEditor.js"></script> <script type="module" src="/js/esm/web-widgets/trim-pre/TrimPre.js"></script> <script type="module" src="/js/esm/web-widgets/trim-pre/highlight-with-trimpre.js"></script>

否则,highlight-with-trimpre.js将试图加亮CM6的内容,犯了越俎代庖的错误。

与LiveEditor协作的问题

CanvasEditor特殊的地方在于,它需要灵活处置JavaScript标签与CSS标签。

设置自己的iframe模板

利用LiveEditor的钩子方法doOnSetupIframeTemplateSrcCanvasditor进行特定的设置。

doOnSetupIframeTemplateSrc(iframe) { iframe.style.height = '200px'; return '/js/esm/web-widgets/canvas-editor/iframe-template.html'; }

iframe的高度值设置为200px,并选用独立的模板。

JavaScript标签

与其在每个CanvasEditor的JavaScript标签处都要编写:

let canvas = document.querySelector('canvas'); let ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height);

的代码,不如将这三行必不可少的代码自动插入到JavaScript标签的代码中。

LiveEditor有一个钩子方法doBeforeApplyJSFromTab,在用户按下Run按钮时被调用。只要该方法返回一个字符串,则将该字符串作为要运行的JavaScript代码放在JavaScript标签代码的前面。

doBeforeApplyJSFromTab(doc, resultDiv) { return ''; }

作为基类的LiveEditor只返回一个空字符串,而作为子类的CanvasEditor可返回实际需要的字符串。例如:

doBeforeApplyJSFromTab(doc, resultDiv) { let src = ` let canvas = document.querySelector('#canvas'); canvas.style.width = canvas.clientWidth + 'px'; canvas.style.height = canvas.clientHeight + 'px'; canvas.width = canvas.clientWidth * devicePixelRatio; canvas.height = canvas.clientHeight * devicePixelRatio; let ctx = canvas.getContext('2d'); ctx.scale(devicePixelRatio, devicePixelRatio); ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight); let ctxUtils = new CtxDrawingUtils(ctx); `; return src; }

上面代码可自动应用高分辨率。

钩子方法doOnImportJSModule可用于导入所需的JavaScript模块:

doOnImportJSModule() { return ''; }

同样,作为子类,CanvasEditor导入了多个工具模块:

doOnImportJSModule() { return ` import {MathUtils} from '/js/esm/MathUtils.js'; import {ColorUtils} from '/js/esm/ColorUtils.js'; import {CanvasUtils} from '/js/esm/CanvasUtils.js'; import {grids} from '/utils/canvas-grids.js'; import {CtxDrawingUtils} from '/js/esm/CtxDrawingUtils.js'; `; }

如此,CanvasEditor中的JavaScript代码便可使用:

ctx.fillStyle = ColorUtils.GetRandomSoftRGB(); ctx.fillRect(250, 10, 200, 200);

设置多种CSS样式

从实际需要来看,CanvasEditor共需要3个设置CSS的地方。

第一个是canvas-default-style.css文件,作为每个CanvasEditor都需共享的初始样式,由iframe-template.html文件设置选用。一般可设置如下:

canvas { border: 1px solid gray; display: block; width: 100vw; height: 100vh; }

因为用户可能要多次运行,当运行到以下代码:

canvas.width = canvas.clientWidth * devicePixelRatio; canvas.height = canvas.clientHeight * devicePixelRatio;

必须确保clientWidthclientHeight回到初始值,否则,其大小将持续不断地增大。因此,给定初始值很重要。

第二个CSS可设置特定特殊的样式,以供同一页面上的部分CanvasEditor共同使用。这种样式通过设置CanvasEditorresult-default-css属性来设置。

...

上面的文件中设为style-at-running.css文件,表示这种设置只有当用户按下Run按钮后才会发生作用。

iframe-template.html文件有个地方专门接收上面CSS文件的内容:

<style id="custom-default-css"></style>

第三个CSSCSS标签下的的内容,可应用于每个CanvasEditor的具体样式。同样,iframe-template.html文件也有地方专门接收此类设置:

<style id="css-from-tab"></style>

通过上面的设置,应足可应对各种复杂的情况。

通过CSS标签设置CSS样式

ctx.beginPath(); ctx.strokeStyle = 'cyan'; ctx.strokeRect(100, 50, 100, 100);
canvas { width: 400px; height: 200px; }

无论是否有CSS设置,均能自动应对高分辨率设备。

通过属性设置CSS样式

下面通过CanvasEditor的属性result-default-css来设置特定的样式。

<canvas-editor result-default-css="style-at-running.css"> <div title="JavaScript"> <trim-pre><esc-code> ... </esc-code></trim-pre> </div> </canvas-editor>

style-at-running.css文件的内容:

canvas { width: 300px; height: 300px; }

实际效果:

ctx.beginPath(); ctx.arc(150, 100, 50, 0, 360); ctx.strokeStyle = 'cyan'; ctx.stroke();

引用Canvas的宽高值

ctx.beginPath(); let padding = 20; let xStart = padding; let xEnd = canvas.clientWidth - padding; let yStart = padding; let yEnd = canvas.clientHeight - padding; ctx.moveTo(xStart, yStart); ctx.lineTo(xEnd, yStart); ctx.moveTo(xStart, yStart); ctx.lineTo(xStart, yEnd); ctx.strokeStyle = 'cyan'; ctx.stroke();
canvas { width: 300px; height: 200px; }

上面的代码:

let xEnd = canvas.clientWidth - padding; let yEnd = canvas.clientHeight - padding;

在涉及到需要引用canvas的宽度及高度的值以参与运算时,需分别使用clientWidthclientHeight

支持网格

设置is-use-grids属性值:

...

则可绘制网格。

ctx.font = 'bold italic 48px Helvetica'; ctx.textBaseline = 'middle'; ctx.textAlign = 'left'; let x = 50; let y = 50; ctx.fillStyle = 'DEEPSKYBLUE'; ctx.fillText('Hello Canvas', x, y);

精细调节Canvas

可新建一个名为min-460.css的文件,其内容为:

body { overflow: hidden; } @media screen and (max-width: 460px) { body { overflow-x: scroll } } canvas { border: unset; min-width: 460px; }

上面的CSS设置,可取得以下效果:

  1. 当横屏时,在Chrome浏览器中不显示横向及竖向的滚动条
  2. 当竖屏时,在Chrome浏览器中只显示横向滚动条
  3. canvas的宽度至少为460px

canvas的高度已由iframe-height来设置,默认为200px

ctx.font = 'bold italic 48px Helvetica'; ctx.textBaseline = 'middle'; ctx.textAlign = 'left'; let x = 50; let y = 50; ctx.fillStyle = 'DEEPSKYBLUE'; ctx.fillText('Hello Canvas', x, y);

从视觉上看,canvas将完全铺满父容器,且在Chrome中自动隐藏滚动条,但在竖屏时,可保证获得最小的宽度值。