百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT知识 > 正文

将你的 Virtual dom 渲染成 Canvas

liuian 2024-12-18 15:36 66 浏览

来源:https://zhuanlan.zhihu.com/p/39886896

项目概述

一个基于 vue 的 virtual dom 插件库,按照Vue render 函数的写法,直接将 Vue 生成的 Vnode 渲染到 canvas 中。支持常规的滚动操作和一些基础的元素事件绑定。

demo 地址:https://muwoo.github.io/vnode2canvas/

背景

从一个小的需求说起:某一天,产品提了一个这样的需求,需要制作一个微信活动页,活动页可以分享包含用户相关信息的图片。这些信息是需要从接口取的,而且每个人都不一样。第一次碰到这种需求的时候,基本上都会去手撸 canvasAPI 去做渲染功能,这种情况的步骤大致如下:

  1. 写一大串 dom template 标签
  2. 渲染 template 成 dom 标签
  3. 开始捕捉 dom 元素,绘制 canvas
  4. canvas 渲染图片

面临的主要问题是复用性太差,其次是性能上也有问题,用户看到的界面不一定和正式渲染出的界面一致,可能存在渲染差异。作为一个有追求的前端,当然得想想看有没有更好的法子。于是乎了解到了一个 html2canvas 这样一个库。但是总是感觉还是要转成 dom 再去绘制,而且感觉性能和稳定性也不是很好。

我们知道 vue 通过 vnode 实现了对不同端的渲染工作,那有没有可能通过 vnode 实现对 canvas 的渲染呢?也就是说,没有 vnode -> html -> canvas 而是直接vnode -> canvas。同时利用 vue 的数据驱动,来达到绘制的数据驱动。想法有了,下面开始实施。

调研

这篇文章对此有详细的介绍:60 FPS on the mobile web 这里简单的概括一下:

canvas 是一种立即模式的渲染方式,不会存储额外的渲染信息。Canvas 受益于立即模式,允许直接发送绘图命令到 GPU。但若用它来构建用户界面,需要进行一个更高层次的抽象。例如一些简单的处理,比如当绘制一个异步加载的资源到一个元素上时会出现问题,如在图片上绘制文本。

HTML 中,由于元素存在顺序,以及 CSS 中存在 z-index,因此是很容易实现的。dom 渲染是一种保留模式,保留模式是一种声明性 API,用于维护绘制到其中的对象的层次结构。保留模式 API 的优点是,对于你的应用程序,他们通常更容易构建复杂的场景,例如 DOM。通常这都会带来性能成本,需要额外的内存来保存场景和更新场景,这可能会很慢。

看来 canvas 绘制页面的研究,很久之前就已经有人付出过研究了。而且性能还是很不错的。那我们更要试试看,到底我们的想法能不能实现了!越来越期待....

开始

canvas 的渲染其实也是一种尝试,既然前人以及做了充分的实践,那么我们便站在巨人的肩膀上去基于 vue 来实现一个数据驱动的canvas渲染。说做就做!(我们这里只提供思路,不做具体实现细节的讨论,因为实现起来有点复杂,如果有兴趣可以参考我的项目实现,或者一起交流探讨 )

处理 vnode

熟悉 Vue 源码的应该都知道,Vue 通过 render 函数,传入 createElement 方法来构造出一个 vnode,通过发布--订阅模式来实现对数据的监听,重新生成 vnode。vnode 最后被转成各平台所需的视图。而我们要做的就是在 vnode 这一层开始。所以,我们基于 Vue 源码的方式,实现一个监听函数,并混入 Vue 实例中:

Vue.mixin({
    // ...
    created() {
      if (this.$options.renderCanvas) {
        // ...
        // 监听vnode中引用的变化,重新渲染
        this.$watch(this.updateCanvas, this.noop)
        // ...
      }
    },
    methods: {
      updateCanvas() {
        // 模拟Vue render 函数
        // 寻找实例中定义的 renderCanvas 方法,并传入createElement方法
        let vnode = this.$options.renderCanvas.call(this._renderProxy, this.$createElement)
      }
})

这样我们就可以愉快的在组件内部使用:

renderCanvas (h) {
  return h(...)
}

canvas 元素处理

render 的 vnode 我们需要做额外的一些约束,也就是说我们需要怎么样的渲染标签,来渲染对应的 canvas 元素(举个 ):

  1. view/scrollView/scrollItem --> fillRect
  2. text --> fillText
  3. image --> drawImage

其中这些元素类分别都继承于一个 Super 类,并且由于它们各有不同的展示方式,因此它们分别实现自己的 draw 方法,做定制化的展示。

绘制对象的布局机制实现

绘制 canvas 布局最基础的写法是为 canvas 元素传入一系列坐标点和相关的基础宽高,这样写到实际项目中可能是这样的:

renderCanvas(h) {
  return h('view', {
     style: {
       left: 10,
       top: 10,
       width: 100,
       height: 100
     }
  })
}

这样写确实有点不方便维护,目前有好几种解决方案,一种是使用 css-layout去做管理。css-layout 支持的转换属性如下:

这样也只是做了一层转换,帮我们更好的用 css 思维去写 canvas,但是如果我们很不爽 css in js 的写法,其实我们还可以写一个webpack loader 来加载外部 css:

const css = require('css')
module.exports = function (source, other) {
  let cssAST = css.parse(source)
  let parseCss = new ParseCss(cssAST)
  parseCss.parse()
  this.cacheable();
  this.callback(null, parseCss.declareStyle(), other);
};


class ParseCss {
  constructor(cssAST) {
    this.rules = cssAST.stylesheet.rules
    this.targetStyle = {}
  }

  parse () {
    this.rules.forEach((rule) => {
      let selector = rule.selectors[0]
      this.targetStyle[selector] = {}
      rule.declarations.forEach((dec) => {
        this.targetStyle[selector][dec.property] = this.formatValue(dec.value)
      })
    })
  }

  formatValue (string) {
    string = string.replace(/"/g, '').replace(/'/g, '')
    return string.indexOf('px') !== -1 ? parseInt(string) : string
  }

  declareStyle (property) {
    return `window.${property || 'vStyle'} = ${JSON.stringify(this.targetStyle)}`
  }
}

简单的来说:主要也就是将 css 文件转成 AST 语法树,之后再对语法树做转换,转成 canvas 需要的定义形式,并以变量的形式注入到组件中。

实现列表滚动

如果我们的元素很多,需要滚动时,我们必须解决 canvas 内部元素滚动的问题。这里我选择了使用Zynga Scroller 来模拟用户滚动方法,通过他返回的滚动坐标点,来对 canvas 进行重绘。有兴趣的可以参考这里我的实现:

https://github.com/muwoo/vnode2canvas/blob/master/src/core/shape/scrollView.js

事件模拟

对于 click,touch 等 dom 事件的模拟,我们采用的方案是根据点击区域进行检测,并找出最底层的元素,递归寻找父元素并触发对应事件处理程序,从而模拟事件冒泡。详细的实现可以参考这里:

https://github.com/muwoo/vnode2canvas/blob/master/src/core/event.js

最后

canvas 绘制页面也是一种创新的尝试,希望这里的研究对你有启发,也欢迎你的 PR。这里也做了很多性能优化,限于篇幅不在赘述了,有兴趣也可以一起探讨。

最后:它并不意味着完全取代基于DOM的渲染,这仍然需要文本输入,复制/粘贴,可访问性和SEO。出于这些原因,我们可以使用canvas和基于DOM的渲染的组合。

更多干货文章,可关注小智的公众号:大迁世界 (打开微信,搜索公众号),里面有很多类似的文章,有需要啥资料的也可以在评论区跟我说,我会尽量解答。

相关推荐

驱动网卡(怎么从新驱动网卡)
驱动网卡(怎么从新驱动网卡)

网卡一般是指为电脑主机提供有线无线网络功能的适配器。而网卡驱动指的就是电脑连接识别这些网卡型号的桥梁。网卡只有打上了网卡驱动才能正常使用。并不是说所有的网卡一插到电脑上面就能进行数据传输了,他都需要里面芯片组的驱动文件才能支持他进行数据传输...

2026-01-30 00:37 liuian

win10更新助手装系统(微软win10更新助手)

1、点击首页“系统升级”的按钮,给出弹框,告诉用户需要上传IMEI码才能使用升级服务。同时给出同意和取消按钮。华为手机助手2、点击同意,则进入到“系统升级”功能华为手机助手华为手机助手3、在检测界面,...

windows11专业版密钥最新(windows11专业版激活码永久)

 Windows11专业版的正版密钥,我们是对windows的激活所必备的工具。该密钥我们可以通过微软商城或者通过计算机的硬件供应商去购买获得。获得了windows11专业版的正版密钥后,我...

手机删过的软件恢复(手机删除过的软件怎么恢复)
手机删过的软件恢复(手机删除过的软件怎么恢复)

操作步骤:1、首先,我们需要先打开手机。然后在许多图标中找到带有[文件管理]文本的图标,然后单击“文件管理”进入页面。2、进入页面后,我们将在顶部看到一行文本:手机,最新信息,文档,视频,图片,音乐,收藏,最后是我们正在寻找的[更多],单击...

2026-01-29 23:55 liuian

一键ghost手动备份系统步骤(一键ghost 备份)

  步骤1、首先把装有一键GHOST装系统的U盘插在电脑上,然后打开电脑马上按F2或DEL键入BIOS界面,然后就选择BOOT打USDHDD模式选择好,然后按F10键保存,电脑就会马上重启。  步骤...

怎么创建局域网(怎么创建局域网打游戏)

  1、购买路由器一台。进入路由器把dhcp功能打开  2、购买一台交换机。从路由器lan端口拉出一条网线查到交换机的任意一个端口上。  3、两台以上电脑。从交换机任意端口拉出网线插到电脑上(电脑设置...

精灵驱动器官方下载(精灵驱动手机版下载)

是的。驱动精灵是一款集驱动管理和硬件检测于一体的、专业级的驱动管理和维护工具。驱动精灵为用户提供驱动备份、恢复、安装、删除、在线更新等实用功能。1、全新驱动精灵2012引擎,大幅提升硬件和驱动辨识能力...

一键还原系统步骤(一键还原系统有哪些)

1、首先需要下载安装一下Windows一键还原程序,在安装程序窗口中,点击“下一步”,弹出“用户许可协议”窗口,选择“我同意该许可协议的条款”,并点击“下一步”。  2、在弹出的“准备安装”窗口中,可...

电脑加速器哪个好(电脑加速器哪款好)

我认为pp加速器最好用,飞速土豆太懒,急速酷六根本不工作。pp加速器什么网页都加速,太任劳任怨了!以上是个人观点,具体性能请自己试。ps:我家电脑性能很好。迅游加速盒子是可以加速电脑的。因为有过之...

任何u盘都可以做启动盘吗(u盘必须做成启动盘才能装系统吗)

是的,需要注意,U盘的大小要在4G以上,最好是8G以上,因为启动盘里面需要装系统,内存小的话,不能用来安装系统。内存卡或者U盘或者移动硬盘都可以用来做启动盘安装系统。普通的U盘就可以,不过最好U盘...

u盘怎么恢复文件(u盘文件恢复的方法)

开360安全卫士,点击上面的“功能大全”。点击文件恢复然后点击“数据”下的“文件恢复”功能。选择驱动接着选择需要恢复的驱动,选择接入的U盘。点击开始扫描选好就点击中间的“开始扫描”,开始扫描U盘数据。...

系统虚拟内存太低怎么办(系统虚拟内存占用过高什么原因)

1.检查系统虚拟内存使用情况,如果发现有大量的空闲内存,可以尝试释放一些不必要的进程,以释放内存空间。2.如果系统虚拟内存使用率较高,可以尝试增加系统虚拟内存的大小,以便更多的应用程序可以使用更多...

剪贴板权限设置方法(剪贴板访问权限)
剪贴板权限设置方法(剪贴板访问权限)

1、首先打开iphone手机,触碰并按住单词或图像直到显示选择选项。2、其次,然后选取“拷贝”或“剪贴板”。3、勾选需要的“权限”,最后选择开启,即可完成苹果剪贴板权限设置。仅参考1.打开苹果手机设置按钮,点击【通用】。2.点击【键盘】,再...

2026-01-29 21:37 liuian

平板系统重装大师(平板重装win系统)

如果你的平板开不了机,但可以连接上电脑,那就能好办,楼主下载安装个平板刷机王到你的个人电脑上,然后连接你的平板,平板刷机王会自动识别你的平板,平板刷机王上有你平板的我刷机包,楼主点击下载一个,下载完成...

联想官网售后服务网点(联想官网售后服务热线)

联想3c服务中心是联想旗下的官方售后,是基于互联网O2O模式开发的全新服务平台。可以为终端用户提供多品牌手机、电脑以及其他3C类产品的维修、保养和保险服务。根据客户需求层次,联想服务针对个人及家庭客户...