Published
Edited
Mar 15, 2021
Insert cell
Insert cell
Insert cell
{
// 创建 canvas
const canvas = document.createElement('canvas');
canvas.setAttribute('width', width);
canvas.setAttribute('height', height);
// 获取 2d 上下文
const ctx = canvas.getContext('2d');
// 计算柱子的宽度
const barWidth = Math.floor((width - margin.left - margin.right) / data.length) / 2;
const gap = barWidth;
// 计算柱子的高度
const dataMax = Math.max(...data.map(datum => datum.percent));
const barHeight = (value) => {
return (height - margin.top - margin.bottom) * (value / dataMax);
}
// 将数值信息转换为 x 轴坐标信息的映射函数
const xScale = (i) => {
return (barWidth + gap) * i + margin.left + barWidth / 2;
}
// 将数值信息转换为 y 轴坐标信息的映射函数
const yScale = (value) => {
return (height - margin.top - margin.bottom) - barHeight(value) + margin.top;
}
const drawBar = (datum, i) => {
// 保存默认设置
ctx.save();
// 设置填充颜色
ctx.fillStyle = 'steelblue';
// 创建矩形 ctx.fillRect(x, y, width, height);
ctx.fillRect(xScale(i), yScale(datum.percent), barWidth, barHeight(datum.percent));
// 恢复默认设置
ctx.restore();
}
const drawLabel = (datum, i) => {
// 保存默认设置
ctx.save();
// 设置填充颜色
ctx.font = '14px sans-serif';
ctx.textAlign = 'center';
ctx.fillStyle = '#333';
// 创建矩形 ctx.fillRect(x, y, width, height);
ctx.fillText(`${datum.percent}%`, xScale(i) + barWidth / 2, yScale(datum.percent) - 5);
// 恢复默认设置
ctx.restore();
}
const drawXAxisTick = (i) => {
ctx.save();
ctx.strokeStyle = '#000';
ctx.moveTo(xScale(i) + barWidth / 2, height - margin.bottom + 1);
ctx.lineTo(xScale(i) + barWidth / 2, height - margin.bottom + 6);
ctx.stroke();
ctx.restore();
}
const drawXAxisLabel = (label, i) => {
ctx.save();
ctx.font = '14px sans-serif';
ctx.textAlign = 'center';
ctx.fillStyle = '#333';
ctx.fillText(label, xScale(i) + barWidth / 2, height - margin.bottom + 20);
ctx.stroke();
ctx.restore();
}
// 绘制 xAias 水平线
const drawXAxisLine = () => {
ctx.save();
ctx.strokeStyle = '#000';
ctx.moveTo(margin.left, height - margin.bottom + 1);
ctx.lineTo(width - margin.right, height - margin.bottom + 1);
ctx.stroke();
ctx.restore();
}
const drawYAxis = () => {
// 根据数据集信息手动设置
const yLabels = [0, 10, 20, 30, 40, 50, 60];
ctx.save();
ctx.strokeStyle = '#000';
// 文字垂直居中
ctx.textBaseline = 'middle';
yLabels.forEach(label => {
ctx.moveTo(margin.left - 5, yScale(label) + 1);
ctx.lineTo(margin.left - 10, yScale(label) + 1);
ctx.stroke();
ctx.fillText(label, margin.left - 12 - ctx.measureText(label).width, yScale(label) + 1);
})
ctx.restore();
}
const drawYTitle = () => {
ctx.save();
ctx.strokeStyle = '#000';
ctx.font = '14px sans-serif';
ctx.fillText('Percentage(%)', 0, 20);
ctx.restore();
}
data.forEach((datum, i) => {
// 绘制柱子
drawBar(datum, i);
// 绘制标签
drawLabel(datum, i);
// 绘制 x 坐标轴 tick
drawXAxisTick(i);
// 绘制 x 坐标轴 label
drawXAxisLabel(datum.browser, i);
})
drawXAxisLine();
drawYAxis();
drawYTitle();
return canvas;
}
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more