这正是检验"轮子"是否圆润的最佳时刻。我们将利用目前 MiniRender 已经具备的图形能力(Rect, Text) 、层级管理(Group, z-index) 和 交互能力(Hover, Click) ,构建一个经典的柱状图(Bar Chart)。
虽然我们还没有 ECharts 那样的高级配置项,但通过原生绘图指令,我们完全可以"手搓"一个出来。
功能点:
- 坐标轴:绘制 X 轴和 Y 轴(使用细长的 Rect 模拟线条)。
- 数据可视化:根据数据生成柱子。
- 交互反馈:鼠标悬停时,柱子高亮变色。
- 数据提示:悬停时,在柱子上方显示具体数值(简单的 Tooltip)。
代码实现 (index.ts)
请将以下代码放入你的入口文件。
typescript
import { init } from './core/MiniRender';
import { Group } from './graphic/Group';
import { Rect } from './graphic/shape/Rect';
import { Text } from './graphic/Text';
// 1. 初始化引擎
const dom = document.getElementById('main')!;
const miniRender = init(dom);
// --- 配置数据 ---
const data = [120, 200, 150, 80, 70, 110, 130];
const categories = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
// --- 图表布局配置 ---
const chartConfig = {
x: 50, // 图表左边距
y: 50, // 图表上边距
width: 500, // 绘图区宽度
height: 300, // 绘图区高度
barWidth: 30, // 柱子宽度
barColor: '#5470C6', // 默认颜色
hoverColor: '#91CC75' // 高亮颜色
};
// 创建一个组来容纳整个图表,方便整体移动
const chartGroup = new Group({
position: [chartConfig.x, chartConfig.y]
});
// --- 第一步:绘制坐标轴 (使用 Rect 模拟线) ---
// Y轴 (左侧竖线)
const yAxis = new Rect({
shape: {
x: 0,
y: 0,
width: 1,
height: chartConfig.height
},
style: { fill: '#333' }
});
// X轴 (底部横线)
const xAxis = new Rect({
shape: {
x: 0,
y: chartConfig.height,
width: chartConfig.width,
height: 1
},
style: { fill: '#333' }
});
chartGroup.add(yAxis);
chartGroup.add(xAxis);
// --- 第二步:准备 Tooltip (浮动提示文字) ---
// 我们创建一个共享的 Text 对象,默认隐藏,悬停时移动位置并显示
const tooltip = new Text({
style: {
text: '',
fill: '#000',
fontSize: 14,
fontWeight: 'bold',
textAlign: 'center',
textBaseline: 'bottom'
},
z: 10, // 保证在最上层
invisible: true, // 初始隐藏
silent: true // 关键:让 Tooltip 不阻挡鼠标,防止闪烁
});
// Tooltip 不加到 chartGroup,而是直接加到 miniRender,防止受 group 变换影响(虽然这里 chartGroup 没旋转)
// 但为了坐标方便,加到 chartGroup 里更容易计算相对坐标
chartGroup.add(tooltip);
// --- 第三步:绘制柱子与标签 ---
// 计算每个柱子的间隔
const step = chartConfig.width / data.length;
const maxVal = Math.max(...data);
data.forEach((value, index) => {
// 1. 数据映射计算
// 高度比例:value / maxVal
const barHeight = (value / maxVal) * (chartConfig.height - 40); // 留40px顶部余量
// 柱子左上角坐标 (相对于 chartGroup)
// x = 间隔 * 索引 + 居中偏移
const x = index * step + (step - chartConfig.barWidth) / 2;
// y = 底部Y - 柱子高度
const y = chartConfig.height - barHeight;
// 2. 创建柱子
const bar = new Rect({
shape: {
x: x,
y: y,
width: chartConfig.barWidth,
height: barHeight
},
style: {
fill: chartConfig.barColor
}
});
// 3. 创建 X 轴分类文本
const label = new Text({
style: {
text: categories[index],
fill: '#666',
fontSize: 12,
textAlign: 'center',
textBaseline: 'top'
},
position: [x + chartConfig.barWidth / 2, chartConfig.height + 10],
silent: true // 文本不响应交互
});
// 4. 绑定交互事件
bar.on('mouseover', () => {
// 柱子变色
bar.style.fill = chartConfig.hoverColor;
// 显示 Tooltip
tooltip.invisible = false;
tooltip.style.text = `${value}`; // 设置数值
// 移动 Tooltip 到柱子顶部中间
tooltip.x = x + chartConfig.barWidth / 2;
tooltip.y = y - 5; // 往上飘一点
miniRender.refresh();
});
bar.on('mouseout', () => {
// 颜色复原
bar.style.fill = chartConfig.barColor;
// 隐藏 Tooltip
tooltip.invisible = true;
miniRender.refresh();
});
chartGroup.add(bar);
chartGroup.add(label);
});
// 将整个图表组添加到引擎
miniRender.add(chartGroup);
// 渲染第一帧
miniRender.refresh();
