前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【ZRender 渲染引擎 - 贰】 | Vue 框架集成与绘制其他图元

【ZRender 渲染引擎 - 贰】 | Vue 框架集成与绘制其他图元

作者头像
张风捷特烈
发布2022-11-01 11:35:59
1.7K0
发布2022-11-01 11:35:59
举报
文章被收录于专栏:Android知识点总结

1. Vue 中使用 ZRender

上一篇中,我们通过最原始的方式体验了一下 ZRender 的使用。接下来,为了更方便管理绘制测试效果,使用 Vue 框架集成 ZRender 库。看完本篇应该能体会出这个管理的必要性,我们要完成如下的绘制样例展示效果:

image.png
image.png

如下,通过 vue cli 创建一个 zrender-player 的项目,这里组件使用类的形式 : class-component

代码语言:javascript
复制
----> vue -V  
@vue/cli 5.0.8

----> vue create zrender-player

然后通过通过 npm 安装 zrender :

代码语言:javascript
复制
----> npm install zrender

如下所示,定义一个 PaperBox 组件来展示绘制的单体,其中组件的布局结构和样式基于 【上一篇】 实现,定义 PaperBox 继承自 Vue,作为独立的组件使用。定义 ZRenderType 类型的 render2d 私有成员,在 mounted 回调中初始化。

image.png
image.png
代码语言:javascript
复制
---->[PaperBox.vue]----
<template>
  <div class="wrapper">
    <div :id= label class="box"></div>
    <span class="label">{{label}}</span>
  </div>
</template>
  
<script lang="ts">
import { Options, Vue } from 'vue-class-component';
import { ZRenderType } from "zrender";
import * as zrender from "zrender";

@Options({
  props: {
    leabel: String
  }
})
export default class PaperBox extends Vue {
  leabel!: string

  private render2d? :ZRenderType;

  mounted(){
    this.render2d = zrender.init(document.getElementById(this.label))
}
</script>

<style scoped lang="scss">
     //同上篇
</style>

2. 遇到问题 -> 分析问题 -> 解决问题

在集成的过程中,没想到这么快就不得不瞄 ZRender 的源码。出现了一个问题 :

Renderer 'undefined' is not imported. Please import it first.

遇到问题,不要怕,分析问题是一项很重要的能力,自主解决问题 是累积经验的重要途径之一。错误出现时,往往都会伴随一些蛛丝马迹,如下所示,其中有错误时的栈信息:

image.png
image.png

点击栈信息,可以看到错误发生的位置:是在 ZRender 对象初始化时,可以在源码稍上一些点断掉调试一下:

image.png
image.png

重点是这段逻辑,核心对象是 painterCtorsrendererType,其中 rendererType 是个字符串,在设置为项中为空时,使用 canvaspainterCtors 是一个对象,通过 rendererType 可以获取个东西,如果获取不到会出问题。

image.png
image.png

所以现在的显示是 painterCtors 对象,在 19 行中对象声明的场合,默认是的空对象。通过观察可以看到它目前仍是一个空对象,也就是说,没有为 painterCtors 添加内容是这个错误的根源:

image.png
image.png

然后追踪 painterCtors 的赋值情况,可以看到在 registerPainter 中会为 painterCtors 根据 name 设置 Ctor 对象。也就是说需要使用 registerPainter 方法来注册绘制器,理论上来说 ZRender 应该会默认注册一个绘制器,但目前来看,实际上没有。

image.png
image.png

所以想要解决这个问题,最简单的方式是手动注册一下, 如下在 mian.ts 中注册一下。

代码语言:javascript
复制
---->[main.ts]----
import { createApp } from 'vue'
import App from './App.vue'
import  CanvasPainter  from "zrender/lib/canvas/Painter"; // 引入 Canvas 绘制器
import * as zrender from "zrender"; // 引入 zrender 绘制器

zrender.registerPainter('canvas', CanvasPainter); // 注册绘制器
createApp(App).mount('#app')

这样再调试时,就可以看到 painterCtors 对象在存在键值对,在网页中也可以正常显示绘制的内容。另外如果需要绘制 svg 的话,也可以注册一下 SVGPainter

image.png
image.png
image.png
image.png

另外,在 zrenderall.ts 中可以看到注册的逻辑,但是实际好像不起作用。网上对 zrenderVue 中的集成文章都比较简单,没有出现这个问题,可能是 Vue3 或者是 TS 引起的。至于具体的原因,我们也不怎么清楚,希望懂行的可以解释一下。好在这个小问题可以解决。

image.png
image.png

3. 封装绘制单体

如下所示,现在的目标是让绘制的单体可以依次排列展示,也就是说根据 数组数据 遍历显示 单体组件 PaperBox 。所以实现需要定义数据的组织方式,从展示的形式上来看,主要需要两个数据:标题图元列表,通过 ExModel 类对数据进行组织管理:

image.png
image.png
代码语言:javascript
复制
import * as zrender from "zrender";

export class ExModel {
    label: string;
    elements: zrender.Element[];
    
    constructor(label: string, elements: zrender.Element[]) {
        this.label = label;
        this.elements = elements;
    }
}

也就是说,ExModel 数据就是对绘制样例的封装, 就代表一个数据,决定一个单体的表现。如下,使用 PaperBox 组件的处理对 ExModel 对象进行渲染出现。

代码语言:javascript
复制
---->[PaperBox]----
<script lang="ts">
import { Options, Vue } from 'vue-class-component';
import { ZRenderType } from "zrender";
import * as zrender from "zrender";
import { ExModel } from '../examples/ExModel';

@Options({
  props: {
    model: ExModel,
  }
})

export default class PaperBox extends Vue {
  model!: ExModel
  private render2d?: ZRenderType;

  mounted() {
    this.render2d = zrender.init(document.getElementById(this.model.label))
    for (let i = 0; i < this.model.elements.length; i++) {
      this.render2d.add(this.model.elements[i])
    }
  }
}
</script>

其中 Html 布局将之前的死数据通过 ExModel 对象进行呈现设置。其实从本质上来看,这和 Flutter 中的组件封装的思想并没有什么区别,都是通过 数据 来驱动 视图

代码语言:javascript
复制
<template>
  <div class="wrapper">
    <div :id=model.label class="box"></div>
    <span class="label">{{model.label}}</span>
  </div>
</template>

4. 数据的供应

单体组件封装完毕后,接下来需要考虑数据的来源问题,这里的数据自然是我们自己创造的。如下,通过一个文件来统一收录数据, 其中每个 exModels 数组中的元素觉得一个样例的展示:

image.png
image.png
代码语言:javascript
复制
---->[examples/exData.ts]----
import polylineExs from './polyline';
import lineExs from './line';
import circleExs from './circle';
import rectExs from './rect';
import ellipsExs from './ellipse';
import { ExModel } from './ExModel';

const exModels = [
    new ExModel('折线: Polyline',polylineExs),
    new ExModel('直线: Line',lineExs),
    new ExModel('圆形: CirCle',circleExs),
    new ExModel('矩形: Rect',rectExs),
    new ExModel('椭圆: Ellipse',ellipsExs),
];

export default exModels;

为了方便独立管理,这里将每个样例放在一个文件在,比如下面是 polyline 的图元对象:

代码语言:javascript
复制
---->[examples/polyline.ts]----
import * as zrender from "zrender";

const polylineEx1 = new zrender.Polyline({
    shape: {
        points: [
            [0, 50],
            [10, 60,],
            [20, 40,],
            [30, 80,],
            [40, 20,],
            [50, 50,],
            [60, 40,],
            [100, 40,],
        ]
    },
    style: {
        stroke: 'red',
        lineWidth: 1,
        fill: 'none',
    },
})

const polylineExs = [polylineEx1]

export default polylineExs;

5. 展示组件

这样数据和单体都准备完毕,接下来遍历渲染即可,如下定义 ZrenderPage 组件,通过 v-for 指令遍历 exModels 的数据渲染 PaperBox 组件,即可完成显示:

image.png
image.png
代码语言:javascript
复制
<template>
    <div class="container">
        <PaperBox 
          v-for=" item ,i in models" 
          :key=i 
          :model=item 
        PaperBox />
    </div>
</template>

<script lang="ts">
import { Options, Vue } from 'vue-class-component'
import exModels from '../examples/exData'
import { ExModel } from '../examples/ExModel';
import PaperBox from '../components/PaperBox.vue'

@Options({
    props: {
        leabel: String
    },
    components: {
        PaperBox,
    },
})
export default class ZrenderPage extends Vue {
    leabel!: string
    models: ExModel[] = exModels;
}
</script>

<style scoped lang="scss">
.container {
    display: flex;
    justify-items: center;
    justify-items: center;
    gap: 20px;
}
</style>

这样,如果需在界面上添加一个图元的样例,只需要在 exModels 中添加数据即可。比如现在想在界面中添加圆弧的样例。

image.png
image.png

只要在 exModels 中添加一个元素即可:

image.png
image.png
代码语言:javascript
复制
import * as zrender from "zrender";

const arcEx1 = new zrender.Arc({
    shape: {
        cx: 50,
        cy: 50,
        r: 40,
        startAngle: 0,
        endAngle: 135 * Math.PI / 180,
    },
    style: {
        stroke: 'red',
        lineWidth: 1,
        fill: 'none',
    },
})

const arcExs = [arcEx1]

export default arcExs;

这样就可以将上一篇中绘制的图元同屏展示,也方便接下来的绘制测试。所以 Vue 通过组件化实现 数据界面 的映射关系,对于批量同类视图的显示是很有益的。如果使用原始的 html 结构进行布局,一个个写出来将非常繁琐,而且难以维护。

image.png
image.png

5. 扇形区、多边形、正多边形、星形

扇形区多边形正多边形星形 分别通过 SectorPolygonIsogonStar 进行绘制。简单看下效果,这里就不细说属性了,想具体了解的可以参考官方文档:zrender-doc。官网的文档没有配图,这里就当给文档配图了:

image.png
image.png

扇形区: Sector

扇形区域对于统计图中的 饼图 是非常重要的,它可以绘制内外半径间的部分圆环区域。上图中红色区域是 sectorEx1 ,黄色区域是 sectorEx2 :

代码语言:javascript
复制
const sectorEx1 = new zrender.Sector({
    shape: {
        cx: 50,
        cy: 50,
        r: 40,
        r0: 30,
        startAngle: 0,
        endAngle: 135 * Math.PI / 180,
    },
    style: {
        stroke: 'red',
        lineWidth: 1,
        fill: 'none',
    },
})

const sectorEx2 = new zrender.Sector({
    shape: {
        cx: 50,
        cy: 50,
        r: 40,
        r0: 30,
        startAngle: -90 * Math.PI / 180,
        endAngle: -165 * Math.PI / 180,
        clockwise: false
    },
    style: {
        stroke: 'blue',
        lineWidth: 1,
        fill: 'yellow',
    },
})

多边形: Polygon

多边形就是一个会自动封闭首尾的折线,也是指定 shape 中的点集进行连线:

代码语言:javascript
复制
const polygonEx1 = new zrender.Polygon({
    shape: {
        points: [
            [0, 50],
            [10, 60,],
            [20, 40,],
            [30, 80,],
            [40, 20,],
            [50, 50,],
            [60, 40,],
            [100, 40,],
        ]
    },
    style: {
        stroke: 'red',
        lineWidth: 1,
        fill: 'none',
    },
})

正多边形: Isogon

代码语言:javascript
复制
const isogonEx1 = new zrender.Isogon({
    shape: {
        x: 50,
        y: 50,
        r: 40,
        n: 8
    },
    style: {
        stroke: 'red',
        lineWidth: 1,
        fill: 'none',
    },
})

星形: Star

代码语言:javascript
复制
const starEx1 = new zrender.Star({
    shape: {
        cx: 50,
        cy: 50,
        r: 40,
        n: 7,
        r0: 20,
    },
    style: {
        stroke: 'red',
        lineWidth: 1,
        fill: 'none',
    },
})

6. 水滴、心形、玫瑰线、旋轮线、圆环

下面来看五个没啥大用的形状,水滴心形玫瑰线旋轮线圆环 分别通过 DropletHeartRoseTrochoidRing 进行绘制。

image.png
image.png

水滴: Droplet

代码语言:javascript
复制
const dropletEx1 = new zrender.Droplet({
    shape: {
        cx: 50 ,
        cy: 65, 
        width: 25,
        height: 60,
    },
    style: {
        stroke: 'red',
        lineWidth: 1,
        fill: 'none',
    },
})

心形: Heart

代码语言:javascript
复制
const heartEx1 = new zrender.Heart({
    shape: {
        cx: 50 ,
        cy: 30, 
        width: 35,
        height: 60,
    },
    style: {
        stroke: 'red',
        lineWidth: 1,
        fill: 'none',
    },
})

玫瑰线: Rose

代码语言:javascript
复制
const roseEx1 = new zrender.Rose({
    shape: {
        cx: 50,
        cy: 50,
        r: [50],
        k: 2,
        n: 15
    },
    style: {
        stroke: 'red',
        lineWidth: 1,
        fill: 'none',
    },
})

玫瑰线: Trochoid 其中 location 为 out 表示 外旋轮 ,图中红线所示; in 表示 内旋轮,图中蓝线所示。

代码语言:javascript
复制
const trochoidEx1 = new zrender.Trochoid({
    shape: {
        cx: 50,
        cy: 50,
        r: 40,
        r0: 5,
        d:6 ,
        location: 'out'
    },
    style: {
        stroke: 'red',
        lineWidth: 1,
        fill: 'none',
    },
})

const trochoidEx2 = new zrender.Trochoid({
    shape: {
        cx: 50,
        cy: 50,
        r: 40,
        r0: 5,
        d:6 ,
        location: 'in'
    },
    style: {
        stroke: 'blue',
        lineWidth: 1,
        fill: 'none',
    },
})

圆环: Ring

代码语言:javascript
复制
const ringEx1 = new zrender.Ring({
    shape: {
        cx: 50,
        cy: 50,
        r: 40,
        r0: 30,
    },
    style: {
        stroke: 'red',
        lineWidth: 1,
        fill: 'none',
    },
})

到这里,ZRender 中的 18 的基本图元的绘制了解完毕,而且通过 Vue 框架搭建了一个方便测试的环境。另外还有路径 Path 的绘制这里暂时不介绍,将在后期单独说明。那本文就到这里,谢谢观看 ~

image.png
image.png
  • @张风捷特烈 2022.10.21 未允禁转
  • 我的 公众号: 编程之王
  • 我的 github 主页 :  toly1994328
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-10-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 2. 遇到问题 -> 分析问题 -> 解决问题
  • 3. 封装绘制单体
  • 4. 数据的供应
  • 5. 展示组件
  • 5. 扇形区、多边形、正多边形、星形
  • 6. 水滴、心形、玫瑰线、旋轮线、圆环
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档