稀土掘金技术社区 2024年12月10日
老板不让用ECharts,还想让我画很多圆环!
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

作者接到在页面动态渲染多个表格且每个表格内实现若干圆环的需求,因老板要求不能用ECharts,最终借助Canvas实现,并将其封装成可复用组件,支持多种自定义配置,文中详细介绍了实现过程。

🎯需求:页面动态渲染多个含圆环的表格

💡方案:用CSS尝试后改用Canvas实现

📦组件:封装成可复用组件,支持多种配置

🎨绘制:实现绘制基本圆环及多个环形区域

原创 快乐就是哈哈哈 2024-12-10 08:31 重庆

点击关注公众号,“技术干货” 及时达!

需求简介

这几天下来个新需求:「要在页面上动态渲染多个表格,每个表格内还要实现若干圆环!」

刚拿到这个需求的时候,我第一反应是用echarts实现,简单快捷

然而,老板无情的拒绝了我!他说:

咱这个项目就一个独立页面,你引入个ECharts项目又要大很多!而且一个页面这么多ECharts实例,性能怎么保障?不准用ECharts,用CSS实现!

没办法,我只好百度如何用CSS画圆环。幸运的是,我确实找到了类似的文章:

「不幸的事」,效果和我的差异很大,代码根本 无法复用!没办法,只能用别的办法实现了。经过一番研究,最终借助「Canvas」搞出来了,简单的分享一下我的实现思路吧。

圆环组件简介

为了方便复用,我把这个功能封装成了项目可直接复用的组件。并支持自定义圆环大小、圆环宽度和圆环颜色比例配置属性。

<Ring    :storkWidth="5"    :size="60"    :ratioList="[        { ratio: 0.3, color: '#FF5733' },        { ratio: 0.6, color: '#33FF57' },        { ratio: 0.1, color: '#3357FF' }    ]"></Ring>

技术方案

实现目标

根据我们的UX需求,我们需要实现一个简单的组件,该组件可以展示一个圆环图表,并根据外部传入的比例数据(如 ratioList)绘制不同颜色的环形区域。

创建 Vue 组件框架

首先,我们创建一个名为 RingChart.vue的组件。组件的初始结构非常简单,只包含一个 canvas 元素。

<template>  <!-- 创建一个 canvas 元素,用于绘制图表 -->  <canvas ref="canvasDom"></canvas></template>
<script lang="ts" setup>import { ref, onMounted } from 'vue';
// 获取 canvas DOM 元素的引用const canvasDom = ref<HTMLCanvasElement | null>(null);
// 初始化 canvas 和上下文变量let ctx: CanvasRenderingContext2D | null = null;let width: number, height: number;
// 初始化 canvas 尺寸和绘图环境const initCanvas = () => { const dom = canvasDom.value; if (!dom) return; ctx = dom.getContext('2d'); if (!ctx) return;
// 设置 canvas 的宽高 dom.width = dom.offsetWidth; dom.height = dom.offsetHeight; width = dom.offsetWidth; height = dom.offsetHeight;};
// 在组件挂载后执行初始化onMounted(() => { initCanvas();});</script>
<style scoped>canvas { width: 100%; height: 100%;}</style>

上述代码中,我们初始化了 canvas 元素,并且设定了 widthheight 属性。

绘制基本的圆环

接下来,我们添加「绘制圆环」的功能:通过 arc 方法来绘制圆环,设置 lineWidth 来调整环的宽度。

<script lang="ts" setup>import { ref, onMounted } from 'vue';
// 获取 canvas DOM 元素的引用const canvasDom = ref<HTMLCanvasElement | null>(null);
// 初始化 canvas 和上下文变量let ctx: CanvasRenderingContext2D | null = null;let width: number, height: number;
// 初始化 canvas 尺寸和绘图环境const initCanvas = () => { const dom = canvasDom.value; if (!dom) return; ctx = dom.getContext('2d'); if (!ctx) return;
// 设置 canvas 的宽高 dom.width = dom.offsetWidth; dom.height = dom.offsetHeight; width = dom.offsetWidth; height = dom.offsetHeight;
// 调用绘制圆环的方法 drawCircle({ ctx, x: width / 2, y: height / 2, radius: 8, lineWidth: 4, color: '#C4C9CF4D', startAngle: -Math.PI / 2, endAngle: Math.PI * 1.5, });};
// 绘制一个圆环的方法const drawCircle = ({ ctx, x, y, radius, lineWidth, color, startAngle, endAngle,}: { ctx: CanvasRenderingContext2D; x: number; y: number; radius: number; lineWidth: number; color: string; startAngle: number; endAngle: number;}) => { ctx.beginPath(); ctx.arc(x, y, radius, startAngle, endAngle); ctx.lineWidth = lineWidth; ctx.strokeStyle = color; ctx.stroke(); ctx.closePath();};
onMounted(() => { initCanvas();});</script>

绘制多个环形区域

现在,我们来实现绘制多个环形区域的功能。我们将通过传入一个 ratioList 数组来动态生成多个环,每个环代表不同的比例区域。

<script lang="ts" setup>import { ref, computed, onMounted } from 'vue';
// 定义 props 的类型interface RatioItem { ratio: number; color: string;}
const props = defineProps<{ size?: number; // 画布大小 storkWidth?: number; // 环的宽度 ratioList?: RatioItem[]; // 比例列表}>();
// 默认值const defaultSize = 200;const defaultStorkWidth = 4;const defaultRatioList: RatioItem[] = [ { ratio: 1, color: '#C4C9CF4D' },];
// canvas DOM 和上下文const canvasDom = ref<HTMLCanvasElement | null>(null);let ctx: CanvasRenderingContext2D | null = null;
// 动态计算 canvas 的中心点和半径const size = computed(() => props.size || defaultSize);const center = computed(() => ({ x: size.value / 2, y: size.value / 2,}));const radius = computed(() => size.value / 2 - (props.storkWidth || defaultStorkWidth));
// 初始化 canvasconst initCanvas = () => { const dom = canvasDom.value; if (!dom) return;
ctx = dom.getContext('2d'); if (!ctx) return;
dom.width = size.value; dom.height = size.value;
drawBackgroundCircle(); drawDataRings();};
// 绘制背景圆环const drawBackgroundCircle = () => { if (!ctx) return;
drawCircle({ ctx, x: center.value.x, y: center.value.y, radius: radius.value, lineWidth: props.storkWidth || defaultStorkWidth, color: '#C4C9CF4D', startAngle: -Math.PI / 2, endAngle: Math.PI * 1.5, });};
// 绘制数据圆环const drawDataRings = () => { const { ratioList = defaultRatioList } = props; if (!ctx) return;
let startAngle = -Math.PI / 2; ratioList.forEach(({ ratio, color }) => { const endAngle = startAngle + ratio * Math.PI * 2;
drawCircle({ ctx, x: center.value.x, y: center.value.y, radius: radius.value, lineWidth: props.storkWidth || defaultStorkWidth, color, startAngle, endAngle, });
startAngle = endAngle; });};
// 通用绘制函数const drawCircle = ({ ctx, x, y, radius, lineWidth, color, startAngle, endAngle,}: { ctx: CanvasRenderingContext2D; x: number; y: number; radius: number; lineWidth: number; color: string; startAngle: number; endAngle: number;}) => { ctx.beginPath(); ctx.arc(x, y, radius, startAngle, endAngle); ctx.lineWidth = lineWidth; ctx.strokeStyle = color; ctx.stroke(); ctx.closePath();};
// 监听画布大小变化onMounted(() => { initCanvas();});</script>

上述代码中,我们通过 ratioList 数组传递每个环的比例和颜色,使用 startAngleendAngle 来控制每个环的绘制区域。其中,drawDataRings 函数遍历 ratioList,根据每个数据项的比例绘制环形区域。

现在,我们的组件就实现完毕了,可以在其他地方引入使用了

<RingChart    :storkWidth="8"    :size="60"    :ratioList="[        { ratio: 0.3, color: '#F8766F' },        { ratio: 0.6, color: '#69CD90' },        { ratio: 0.1, color: '#FFB800' }    ]"></RRingChart>

组件代码

<template>    <canvas ref="canvasDom"></canvas></template>
<script lang="ts" setup>import { ref, computed, onMounted, watchEffect } from 'vue';
// 定义 props 的类型interface RatioItem { ratio: number; color: string;}
const props = defineProps<{ size?: number; // 画布大小 storkWidth?: number; // 环的宽度 ratioList?: RatioItem[]; // 比例列表}>();
// 默认值const defaultSize = 200; // 默认画布宽高const defaultStorkWidth = 4;const defaultRatioList: RatioItem[] = [{ ratio: 1, color: '#C4C9CF4D' }];
// canvas DOM 和上下文const canvasDom = ref<HTMLCanvasElement | null>(null);let ctx: CanvasRenderingContext2D | null = null;
// 动态计算 canvas 的中心点和半径const size = computed(() => props.size || defaultSize);const center = computed(() => ({ x: size.value / 2, y: size.value / 2}));const radius = computed(() => size.value / 2 - (props.storkWidth || defaultStorkWidth));
// 初始化 canvasconst initCanvas = () => { const dom = canvasDom.value; if (!dom) return;
ctx = dom.getContext('2d'); if (!ctx) return;
dom.width = size.value; dom.height = size.value;
drawBackgroundCircle(); drawDataRings();};
// 绘制背景圆环const drawBackgroundCircle = () => { if (!ctx) return;
drawCircle({ ctx, x: center.value.x, y: center.value.y, radius: radius.value, lineWidth: props.storkWidth || defaultStorkWidth, color: '#C4C9CF4D', startAngle: -Math.PI / 2, endAngle: Math.PI * 1.5 });};
// 绘制数据圆环const drawDataRings = () => { const { ratioList = defaultRatioList } = props; if (!ctx) return;
let startAngle = -Math.PI / 2; ratioList.forEach(({ ratio, color }) => { const endAngle = startAngle + ratio * Math.PI * 2;
drawCircle({ ctx, x: center.value.x, y: center.value.y, radius: radius.value, lineWidth: props.storkWidth || defaultStorkWidth, color, startAngle, endAngle });
startAngle = endAngle; });};
// 通用绘制函数const drawCircle = ({ ctx, x, y, radius, lineWidth, color, startAngle, endAngle}: { ctx: CanvasRenderingContext2D; x: number; y: number; radius: number; lineWidth: number; color: string; startAngle: number; endAngle: number;}) => { ctx.beginPath(); ctx.arc(x, y, radius, startAngle, endAngle); ctx.lineWidth = lineWidth; ctx.strokeStyle = color; ctx.stroke(); ctx.closePath();};
// 监听画布大小变化watchEffect(() => { initCanvas();});
onMounted(() => { initCanvas();});</script>
<style scoped>canvas { display: block; margin: auto; border-radius: 50%;}</style>

使用

<Ring    :storkWidth="5"    :size="60"    :ratioList="[        { ratio: 0.3, color: '#FF5733' },        { ratio: 0.6, color: '#33FF57' },        { ratio: 0.1, color: '#3357FF' }    ]"></Ring>

点击关注公众号,“技术干货” 及时达!

阅读原文

跳转微信打开

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

Canvas 圆环组件 技术实现 自定义配置
相关文章