首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >干货 | 跨平台 Canvas 绘图引擎背后的黑科技

干货 | 跨平台 Canvas 绘图引擎背后的黑科技

作者头像
携程技术
发布2019-04-22 10:11:04
2K0
发布2019-04-22 10:11:04
举报
文章被收录于专栏:携程技术携程技术

作者简介

月影(十年踪迹),360前端技术委员会委员。奇舞团技术总监,JavaScript程序猿,目前重点关注前端框架和新技术应用。本文来自月影在“2018携程技术峰会”上的分享。

在2018年初的时候,因为要组建可视化团队,接手公司内部的一些可视化项目,做了一些技术调研。

之前的一些可视化项目或者一些内部系统中的可视化功能,奇舞团主要是使用d3.js或echarts实现的。d3.js由于使用上比较灵活,因此也应用的比echarts更广。

但是d3有一个缺点,就是虽然它主要的功能是处理基于数据的文档,其实对如何具体展示并没有特别限定,但是它的官方例子多半是使用SVG和DOM实现的,而考虑性能和跨平台性,我们的项目使用Canvas渲染要优于使用SVG和DOM。

但是因为Canvas的API和DOM/SVG差别较大,因此要把例子移植为Canvas渲染,改动比较大,这样不利于开发人员快速学习和使用D3完成项目。

因此最初我们只是想实现一个很简单的库,封装Canvas,让它对外暴露和DOM/SVG较一直的API,这也就是实现SpriteJS这个库的初衷。

SpriteJS+d3 实现的地图效果及代码

在后来,随着库的设计不断深入和我们对可视化探索的不断深入,SpriteJS逐渐完善成为一个独立的用于可视化底层的渲染库。与其他同类库相比,SpriteJS主要有以下几个优势:

  • 与DOM高度一致的盒模型以及API,使得它与d3.js和其他适合操作DOM的库非常友好
  • 支持属性继承,font、lineHeight、color等许多属性为可继承属性,适合实现可视化的UI组件化
  • 支持CSS,可无缝对接文档中的样式,使用样式来控制SpriteJS的节点元素
  • 支持标准Flex布局,也支持扩展其他类型的布局
  • 支持Web Animation API,也支持CSS3 Animation和Transition
  • 支持文字的排版,支持line-break、word-break等相关属性
  • 支持外部时钟,可以很好地和其他Canvas库无缝集成
  • 支持React、PReact、Vue等现代前端框架
  • 跨平台,支持Node.js服务端渲染、支持微信小程序

SpriteJS有与DOM高度一致的模型,它的对象以树状结构组织:

SpriteJS的默认元素类型有6种,分别是Scene、Layer、Group、Sprite、Label和Path。

其中Sprite、Label和Path分别是可带图片纹理的元素、可带文字的元素和可带SVG Path的矢量元素,Group是容器,Layer可以分层渲染,Scene是根元素。

我们也可以使用React框架:

jsx
import React from 'react';
import ReactDOM from 'react-dom';
import {Scene} from 'sprite-react';

class Block extends React.Component {
  constructor(props) {
    super(props);
    this.state = {color: 'green'};
  }

  handleClick() {
    this.setState({
      color: 'blue',
    });
  }

  render() {
    return (
      <sprite pos={[100, 100]} bgcolor={this.state.color} size={[50, 50]} onClick={this.handleClick.bind(this)}></sprite>
    );
  }
}

ReactDOM.render(
  <Scene>
    <layer id="fglayer" handleEvent={true}>
      <group>
        <sprite pos={[200, 100]} size={[50, 50]} bgcolor="red" onClick={function () { this.attr('bgcolor', 'blue') } }></sprite>
        <Block/>
      </group>
    </layer>
  </Scene>,
  document.getElementById('app')
);

或者使用Vue:

html
<template>
  <scene id="container" :viewport="[300, 300]">
    <layer>
      <sprite
        :textures="imgUrl"
        bgcolor="#fff"
        :pos="[0,0]"
        :size="[400, 400]"
        borderRadius="200"
      />
    </layer>
  </scene>
</template>

<script>
export default {
  data() {
    return {
    imgUrl: 'https://s5.ssl.qhres.com/static/ec9f373a383d7664.svg'
    }
  }
}
</script>

我们还可以使用文档中的CSS来控制SpriteJS元素的样式:

html
<script src="https://unpkg.com/spritejs/dist/spritejs.min.js"></script>
<div id="container"></div>
<style>
  sprite.myclass {
    background-color: red;
    --sprite-x: 0;
    --sprite-y: 0;
    width: 400px;
    height: 400px;
    border-radius: 200px;
  }
</style>
<script>
    const imgUrl = 'https://s5.ssl.qhres.com/static/ec9f373a383d7664.svg'
    const {Scene, Sprite} = spritejs;
    const paper = new Scene('#container', {
      viewport: [400, 400],
      useDocumentCSS: true,
    })

    const sprite = new Sprite(imgUrl)
    sprite.className = 'myclass';
    paper.layer().appendChild(sprite)
</script>

这些特性使得我们能够使用DOM的方式来构建我们的图表和UI组件库,实际上我们也正是使用SpriteJS的这一系列特性来实现类似于element-ui这样的UI设计系统的。

基于SpriteJS的图表库

那么如何实现上面这些特性,尤其是高性能地实现这些特性呢?

SpriteJS对上述特性的具体实现比较复杂,有兴趣的同学可以自己去浏览GitHub仓库的源代码,在这里将简化的流程试解释一二。

一、渲染时机

SpriteJS的更新机制与传统的Canvas框架不同,不是固定时间刷新的,而是采用类似DOM的方式在属性更新的时候才刷新:

SpriteJS有属性比较机制,当属性变化的时候,标记更新并通知Layer,这时候Layer会启动一个Promise任务等待下一帧并更新画布。

如果涉及到Label或带有Layout的Group,还有可能会触发retypesetting和relayout操作,如果使用文档中的CSS,涉及到的属性恰好包含在CSS规则中,那么还可能会触发更复杂的updateStyles操作。

总之,SpriteJS有一套判断机制,控制Canvas更新的时机,并确保retypesetting、relayout、updateStyles等耗性能的操作尽可能减少,以保证渲染性能。

文字排版

Flex布局

二、缓存和批次

为了提升性能,SpriteJS支持自定义缓存策略和批次渲染。

如果渲染对象的形态可枚举,我们可以采用自定义的缓存策略,利用少量的缓存对象来大大提升性能:

缓存策略

或者通过批次渲染的方式,使用起来更加方便:

批次渲染

三、SVG和过渡动画

SpriteJS对SVG-Path的支持非常的好,不仅能支持Path的绘制,还能支持过渡动画:

SpriteJS支持Web Animation API,因此可以用标准的Web Animation动画,也可以用CSS3的keyframe动画:

html
<style>
@keyframes myfirst
{
  0%   {background: red;}
  25%  {background: yellow;}
  50%  {background: blue;}
  100% {background: green;}
}
group.list > label:nth-child(2n+1) {
  color: #f37;
  --sprite-scale: 1.5,1.5;
  animation: myfirst 2s;
}
</style>

四、选择器和CSS

在对CSS的支持方面,SpriteJS支持几乎所有的CSS3选择器,包括元素选择器、类选择器、属性选择器和伪类选择器。而且在文档里可以将DOM和SpriteJS的选择器混合使用,就像是使用原生的DOM一样操作SpriteJS的元素。

用CSS定义样式

SpriteJS支持大部分DOM的CSS属性,对于一部分SpriteJS独有的属性,可以使用--sprite-属性名的方式设置。

五、外部时钟

SpriteJS支持外部时钟,因此可以很好地和第三方库联合使用:

SpriteJS与CurveJS一同使用

SpriteJS与粒子系统

六、跨平台

SpriteJS依赖于独立Canvas环境而不依赖于DOM,因此它有很好的跨平台属性,可以在Node.js中通过node-canvas渲染使用,也可以支持微信小程序。

SpriteJS与微信小程序

目前SpriteJS主要用于360可视化项目中,作为底层渲染库使用,在未来会进一步提升它的跨平台能力,以及渲染性能,还会集成WebGL增加3D渲染的能力。

除了上面介绍的这些之外,SpriteJS还有许多有用的能力,比如屏幕适配、资源预加载、css雪碧图、.9背景图片等等,具体可以参考官方文档。如果有做可视化项目的小伙伴,可以试试这个库,相信你们会有惊喜的~

  • GitHub仓库地址: https://github.com/spritejs/spritejs (点击阅读原文直达)
  • 官网: http://spritejs.org
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-01-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 携程技术中心 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、渲染时机
  • 二、缓存和批次
  • 三、SVG和过渡动画
  • 四、选择器和CSS
  • 五、外部时钟
  • 六、跨平台
相关产品与服务
云开发 CloudBase
云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档