关于浏览器端 Canvas Hit Region API 和结构化的图形

1

原先的计划是 Quamolit 项目在 Canvas 上模拟 DOM 来写应用,
自己的实现会丧失大量 DOM 的好处, 而且力有不逮, 但着重改善几个方面:

  • 每个元素有自身的生命周期, 而生命周期容易绑定动画, 以及生成有视差和延时的渐变

  • 元素通过 id 来标识, 那么当元素从树的一个位置移动到另一个, 也能由框架生成渐变

目前以及尝试过的部分看来, 效果不会太好, 代码生成的插值, 太单调了
另外, 涉及到 transform 的图形的控制, 带来了新的麻烦:

  • transform 之后点击位置难以计算

  • transform 是通过 Canvas 的机制完成的, 元素在树的层级之间跳跃是难以插值

我下面先说这两个问题:

Hit Region 来计算位置

上边的第一个 transform 后的位置, 需要实现事件系统的话就需要能拿到正确的位置
实际上我想不出来, 幸好回过神来去 StackOverflow 问了一下, 拿到了答案:

http://stackoverflow.com/a/28762735/883571

一个是 ctx.isPointInPath() 这个 API, 可以原来判断点是否落在当前渲染的 Path 中
但是这个比较难用, 而且有事件时不大可能会渲染一个判断一次的

如果是我自己来设计 API, 我当然是希望 Canvas 每个像素点上能直接拿到对应的标识
后来我在 W3C 的规范里看到了, 而且答案原来也提到, 有 Hit Region 可以用:

http://www.w3.org/TR/2dcontext/#hit-regions
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/addHitRegion

canvas.addEventListener("mousemove", function(event){
  if(event.region) {
    alert("ouch, my eye :(");
  }
});

// eyes
ctx.beginPath();
ctx.arc(70, 80, 10, 0, 2 * Math.PI, false);
ctx.arc(130, 80, 10, 0, 2 * Math.PI, false);
ctx.fill();
ctx.addHitRegion({id: "eyes"});

这个 API 允许在像素点设置 id, 设置鼠标图片, 以及其他一些选项
问题在于 API 只有 Chrome Firefox 实现, 而 Chrome 需要在 chrome://flags 手动开启
作为 Canvas Exprimental API 存在, 我在 GitHub 搜了, 也极少有项目用到

顺着这个 API 的思路往后想, 当我们给 Canvas 设计一套类似 DOM 的结构以后
通过 Region 返回的标识, 我们就能把像素上的编辑映射到对应的元素
那么, 几乎能够重新实现一套 DOM, 更重要的是, 绕过了 DOM 和 CSS
好处是, 我们的元素非常轻量级, 不受 Layout reflow 约束, 而且能任意绘图
也可以想见没有 CSS Box Model 写代码会挺痛苦.. 不过倒是值得尝试

Transform 以后的插值渐变

比如设计这样一个结构, 作为我在 Quamolit 设计的标签树:

RootElement
  ElementA
    ScaleH
      ElementI
  ElementB
   <PlaceC>

ElementIScaleH 当中, 最好是用 ctx.save() ctx.restore() 这套机制
ElementI 当中就不需要手工计算甚至复杂图形, 就能够完成 transform 了
但是, 假如 ElementI 需要渐变到 PlaceC 呢, 中间怎样插值?
直接的办法是拿到 ElementI 全部的 transform, 然后计算

可是问题可能很复杂, 这里树的结构简单, transform 也简单, 可框架需要考虑复杂情况
ScaleH 的位置被大量的 transform 取代, 甚至 PlaceC 也包裹在 transform 中呢?
我感到情况将会变得非常复杂, 而我毫无信心在自己能控制的复杂度内完成...
我需要找到巧妙的办法才行, 不然又是没法写下去的

结构化的图形

我在设计 Quamolit 的运行环境的时候, 不知不觉用到了虚拟机的思路
就是把 Canvas 基本操作设计成一些指令, 然后框架生成这些指令出来运行
这样, Canvas 对我来说, 就是一个虚拟机, 我用高级的 API 去操作
或者, 类比一下 SVG 的想法, 跟 Canvas API 的写法, 后者就是虚拟机的指令的样子

我是追随声明式编程的, 甚至声明式写动画. 那我自然认为 SVG 是高级的方案
Canvas API 过程式的写法, 不如 SVG 的写法更适合模块化, 也不够直白
现在很多的 Canvas 类库, 采用的虽然更靠近声明式, 但依然是 API 的过程式调用
那么我认为, 未来类似 React Canvas 的方案深化出现, 才是好的方案
...当然我注意到 React 在 DOM 上受到限制, 生成动画和渐变有不少缺陷

同时, 跟着上边虚拟机的类比, 虚拟机提供了新的语言, 可以用更高级的思维编程
那么, 对于图形而言, SVG 一类的方案是否也有着类似编程语言种种特性呢?
比如说, CSS 某些属性在 DOM 上继承, 就像是编程语言作用域的穿透似的
那么是否可以参考编程语言提供给代码控制离的方式, 提供给图形元素这样的控制能力?

fibonacci = (n) ->
  if n <= 2
    fibonacci(n - 1) + fibonacci(n - 2)
  else
    1

比如 fibonacci 能从外层的闭包获取数据, 自身进行判断和计算
那么, 对于图形元素而言, 获取父元素位置信息, 判断和计算是否是基本的功能?
我们原先设计的 CSS 布局, 很大程度就屏蔽这种内部元素探测环境的机能
那么我想, 让每个图形元素都有能力自身进行计算, 就想 React, 是不是更好?

而且作用域以及数值, 就和各种图形元素产生一些对应关系
我们是不是可以模仿编程语言, 提供图形编程更多的编程能力
并且以之为基础, 完善一套声明式编程图形界面的更清晰的思路?
到这里, 是我的一些猜想, 当然我还是要继续考虑和探索一下


如果觉得我的文章对你有用,请随意赞赏

你可能感兴趣的

载入中...