跳到主要内容

使用HTML5画布创建交互式气泡图

信息图表是一种使数据易于理解和娱乐的方法。有些人通过创意图形设计实现了他们的目标,例如印刷图表。其他人通过动画和互动来实现它。我最近看到的最好的例子是富有想象力的演讲者和Hans Rosling教授的一系列演讲。我强烈建议你看他20分钟的TED演讲旋转在那里他揭穿了关于发展中国家的共同神话。

罗斯林教授成功的关键在于他以非专家能够理解的形式呈现公共数据。他的工作核心是一个惊人的泡沫图表,可以比较世界各地的情况。在本文中,我将向您展示如何使用填充了世界银行实际数据的HTML Canvas创建一个有吸引力的气泡图。

什么是气泡图?

气泡图就是它听起来的样子,一个由气泡组成的图表 - 或者真的来自圆圈。但气泡图的强大之处在于它可以同时显示多达五维的数据。每个气泡代表沿X和Y轴的一些数据点,与任何其他线图或散点图非常相似。但是,每个气泡的大小和颜色也可以代表另外两个维度。如果我们为图表设置动画,那么我们可以将时间添加为第五维。

虽然气泡图的五个属性中的每一个都可用于表示任何类型的数据;在实践中,我们通常使用X和Y轴作为典型的数字数据,并保留气泡颜色以区分数据集。例如:X和Y可能代表儿童死亡率和识字率,而泡沫颜色代表国家。泡沫大小应该用于表示一个数量级的东西,例如一个国家的人口。第五个时间轴(动画时)应该用来实际表示时间,比如从1960年到现在的数据点。仔细和创造性地使用这些数据维度将伟大的信息图与另一个图表区分开来。

从模拟数据制作一个简单的图表

让我们首先使用模拟数据制作一个简单的气泡图。数据本身应表示按某些标准分组的随时间变化的数据点。假设我们有五个国家和20个时间点(1980年至2000年)。每个数据点都有x,y和大小。初始化此模拟数据的代码如下所示:

var data = []; for(var t = 0; t <20; t ++){var cdata = []; data [t] = cdata; for(var country = 0; country <5; country ++){cdata.push({x:50 + Math.random()* 500,y:50 + Math.random()* 300,size:3 + Math.random ()* 20,国家:国家}); }}

现在我们需要在屏幕上绘制数据作为气泡。代码看起来像这样。

var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); var colors = [“red”,“green”,“blue”,“yellow”,“orange”]; var time = 0; function draw(){// bg and border ctx.fillStyle =“white”; ctx.fillRect(0,0,canvas.width,canvas.height); ctx.strokeStyle =“黑色”; ctx.strokeRect(0,0,canvas.width,canvas.height); //时间指示器ctx.fillStyle =“黑色”; ctx.fillText(“time”+ time,10,20); //绘制当前时间片数据的数据[time] .forEach(function(d){ctx.save(); ctx.fillStyle = colors [d.country%colors.length]; ctx.globalAlpha = 0.5; ctx .beginPath(); ctx.arc(dx,dy,d.size,0,Math.PI * 2); ctx.fill(); ctx.restore();}); }

上面的代码用白色填充背景,并使用画布的大小绘制背景边框。然后它绘制当前时间指示器,最后绘制数据本身。使用当前点的x,y和大小将每个数据点绘制为圆形。颜色基于国家/地区,从预设的颜色列表中选择。

基本图表如下所示:

为了使图表随时间变化,我们只需要使用不同的时间变量重复调用绘图。

$(“#play”)。click(function(){var animdraw = function(){draw(); time ++; if(time <data.length){setTimeout(animdraw,100);} else {time = 0; animdraw();});

世界数据库

这就是基本图表。让我们用一些真实的数据来更新它,使其更有趣。有一个伟大的组织称为世界数据库。他们从联合国和其他公共数据源中挑选出令人惊叹的数据集。除了托管数据库之外,它们还有一个自定义报告生成器,可以创建数据切片以便以各种格式下载。

本文选择“世界发展指标和全球发展金融”数据库。然后选择一些国家或聚合。我选择了“东亚与太平洋”,“欧洲与中亚”,以及“拉丁美洲和加勒比地区”。现在选择要查看的主题。这些将是X和Y变量。我选择了“死亡率”和“人口密度”。我还选择了“人口总数”,因此我们可以使用气泡的大小来比较国家的大小。最后选择所需的年份。我只需按下“全选”按钮即可选择每年可用的1960年至今。

现在我们有一份报告。单击“导出”按钮将数据下载为CSV文件。您可以在Excel中打开CSV文件,以查看World DataBank为我们提供的内容。如果在Excel中打开它,您将看到每个国家/地区有三行,我们选择的每个变量都有一行。早年缺少一些价值观。这意味着那些年份没有为该国家收集价值。

解析CSV数据

如果在Excel中打开报表,它看起来就像一堆行。我们需要将这些数据解析为我们可以使用的东西。要处理数据文件,我们需要一个CSV解析器。这是从头开始编写自己的解析器没有价值的情况之一。我从中下载了一个本纳德尔在这里。它工作得很好。

要加载CSV文件并解析它,我正在使用jQuery调用AJAX。

$(“#load”)。click(function(){$ .ajax({url:“data.csv”,context:document.body,success:function(c){var csvdata = CSVToArray(c); console。 log(“到这里”+ csvdata [1]);}});}

CSV文件被解析为行,这些行被细分为列。我们可以通过循环遍历每一列来处理它,然后执行每一行。

data = []; //从9开始跳过非年份列和前几年(var t = 9; t 

请注意,已从第9列开始跳过元数据(国家/地区名称等)以及数据点的前几年,因为其中某些值缺少值。当我循环遍历各行时,我会按三分行进行处理,以便我们每次通过处理整个区域/国家/地区。

如果我们只是按原样绘制数据,我们将看不到任何内容。整个画布将以单一颜色填充。不是我们想要的。这是因为数据未正确缩放。例如,1960年东亚和太平洋地区的规模已经超过10亿。为了绘制图表,我们需要缩小价值。为了保持数据不被修改,我已经在绘图时应用了缩放。我改变了这样的arc命令:

ctx.arc(dx * 6,canvas.height-dy * 2.5,d.size /(1000 * 1000 * 11),0,Math.PI * 2);

我修改了x,y和size值以适应画布。尺寸必须除以1100万,才能得到合理的气泡半径值。 y值乘以2.5,然后从画布高度中减去。画布中的标准坐标系从左上角开始,因此从画布高度减去将翻转y轴。

我选择修改数据变量的值是任意的。我专门选择它们使图表看起来不错。您为图表选择的值取决于您最终绘制的特定数据。在此图表的未来版本中,您可以编写一个例程来分析数据并查找合理的值,例如计算每个变量的最大值和最小值。

该图表现在看起来像这样:

添加交互性

动画图表很不错,但真正使其具有交互性的是让读者点击不同的气泡来获取更多信息。由于canvas使用纯像素而不是形状,因此我们需要自己的例程来计算单击的形状。幸运的是,由于气泡是完美的圆圈,这很容易。只需计算点击点和每个气泡中心的距离。如果距离小于半径,则单击形状。

$(“#canvas”)。mousedown(function(e){displayInfo = false; data [time] .forEach(function(d){var x = dx * 6; var y = canvas.height-dy * 2.5; var radius = d.size /(1000 * 1000 * 11); var dis = dist(e.offsetX,e.offsetY,x,y); if(dis <radius){displayInfo = true; displayCountry = d.country;} }); 画();});

如果读者点击了一个形状,那么我将displayInfo布尔值设置为true,保存当前国家/地区,然后触发重绘。我还修改了绘图功能,在右上角添加了一个小信息面板。

请注意,在附加的paintin代码中,我从当前时间片中提取国家/地区数据,而不是从用户单击图表时提取数据。这意味着即使图表处于动画状态,国家/地区显示也会更新,因此您可以看到数字也会随着时间的推移而变化。

if(displayInfo){ctx.save(); ctx.translate(canvas.width-305,5); ctx.fillStyle =“rgba(200,200,200,0.7)”; ctx.fillRect(0,0,300,100); ctx.strokeStyle =“黑色”; ctx.lineWidth = 2; ctx.strokeRect(0,0,300,100); ctx.fillStyle =“黑色”; var displayPoint = data [time] [displayCountry]; ctx.fillText(“Region:”+ regions [displayPoint.country],5,20); ctx.fillText(“人口密度:”+ displayPoint.x,5,20 + 20 * 1); ctx.fillText(“死亡率:”+ displayPoint.y,5,20 + 20 * 2); ctx.fillText(“Population:”+ displayPoint.size,5,20 + 20 * 3); ctx.restore(); }

修整视觉效果

一般来说,图表不应该有太多的视觉风格,它会从底层数据中减去,但是一个很好的颜色和图案选择可以产生影响。为了使这个图表看起来更好,我在气泡上添加了一个白色覆盖图,使它们具有圆润效果。我还给了他们一个黑色边框,并选择了一组不同的基色。我没有计算每种气泡颜色的特殊渐变,而是绘制了基色,然后在顶部放置了部分透明的径向渐变。

var radgrad = ctx.createRadialGradient(x-radius / 10,y-radius / 10,0,x-radius / 10,y-radius / 10,radius + 30); radgrad.addColorStop(0,'white'); radgrad .addColorStop(0.5,'white'); radgrad.addColorStop(1,'rgba(255,255,255,0.3)'); ctx.globalAlpha = 0.6; ctx.fillStyle = radgrad; ctx.fill();

为了完成图表,我更改了字体,为气泡添加了边框,并使用了有趣的背景图案SubtlePatterns.com。这是最终结果:

结论

这就是泡沫图表的全部内容。使用上面的基本图表,您可以在自己的图表上有一个良好的开端。要改进气泡图表,您可能希望自动缩放数据,让用户在多个数据集之间进行选择,并在每个时间帧之间进行插值以使动画更加平滑。您可以从这个项目下载完整的源代码这里或者从我的网站JoshOnDesign.com



翻译字数超限