前言
本文翻译自 data sketch
上 Nadieh Bremer
的一篇文章,讲述了一个优秀的可视化项目背后一步步的实现过程。由于英文苦手、水平有限,对一些内容的理解与翻译可能没那么准确,但出于分享的缘故,还是硬着头皮翻译了下,有看不懂、无法理解的部分可阅读原文。
优秀的可视化作品之前也分享过一些,网上也不少,但可能大家此前很少有了解,创作者是如何一步步创作出那些优秀作品的,本篇文章即是带大家简单开开眼,当然看完也并不代表就能作出这样的作品,真刀真枪地实践还得看源码,之前的文章古柳也说过会复现很多优秀可视化作品,自然也少不了「百变小樱」这个项目,更多实践讲解下,敬请期待。
原文链接:http://www.datasketch.es/june/
百变小樱可视化作品:http://www.datasketch.es/june/code/nadieh/
源码:https://github.com/nbremer/datasketches/tree/gh-pages/june/code/nadieh
第一周| 数据 data
离4月份的项目已经过去了6个月,我知道我应该先做五月份的项目再做六月份的。但是,我还没确定五月份项目的切入点,而对六月份的项目已经有了想法。
我开始明白我能真正充满热情的主题或许有些小众,没有太多人真得知道这个。我只能希望对于像我这样的粉丝来说,本月的探索将是快乐的。而我为「六月份的勇气月」Fearless
选择的主题是「百变小樱/Cardcaptor Sakura」
!
约20年前的日本魔法少女漫画,是我拥有的第一部漫画,当时在荷兰几乎没有人知道漫画(实际上现在也是)。我甚至不得不一路前往我省最大的城市购买新一册漫画。我至今仍非常嫉妒漫画的每个 panel (我也不知道这个应该翻译成什么,对应漫画里的术语是什么) 看起来多么完美和可爱😍我差点在一月份的怀旧月选择了这个主题,但后来还是选择了 龙珠Z/Dragonball Z
。然而,20年过去了,「百变小樱」
新番最近又重新启动了。因此,在思考这个「勇气月」做些什么时,我无法动摇想要围绕勇敢、无所畏惧、善良的魔法少女小樱/Cardcaptor Sakura
(以下简称CCS
)做些什么。
我喜欢 CCS
的其中一个理由是作者 CLAMP
将每一页都画得很漂亮,尤其是每一章的封面,堪称小小的艺术品(上图是第23章的封面)。因此,我想以某种方式通过数据来研究封面。此前我从未做过任何基于图像数据的分析。因此,我想:从每个封面中抽取3-8种颜色,并进行可视化展示,这将是对我来说有趣且新颖的一件事。
最初的 CCS
漫画有50章,分为两篇(古柳:补充自百度词条:库洛牌篇和小樱牌篇)。我浏览了全部12卷 CCS
,以查看每章封面的图片(顺便一提,你也可以在线阅读 CCS
,例如在这里)。在我买的漫画里,封面是黑白印刷的。但是,所有这些章节的封面都已在几本 CCS
艺术书籍中以全彩色出版。因此,我从 CCS Wiki 页面中搜索并下载了相应的彩色图像。
使用 R 语言
中的图片处理库 imager,我将图像加载到 R 中,将每个像素都转换为以 RGBA
值表示的多维数组。我将这一复杂数组转换为更简单的数据格式即:(像素数) * 3 (分别为 r/g/b)
。为了弄清楚哪种算法能较好的将像素值聚类成合适的颜色分组,我尝试了几件事。首先,我尝试使用不同的聚类技术:从标准的均值聚类 K-means
到分层聚类 hierarchical clustering
,甚至 tSNE
;此外,我还使用 colorspace 库,将每个像素的 RGB
值转换到其他颜色空间(此时颜色间具有不同的“距离”,因此可以产生不同的聚类效果)。
我将每个测试结果转换成如下所示的条形图,以查看得到的颜色分组。最终,我发现使用 K-means
聚类搭配颜色转换成 Lab
时,效果最佳。
但是,使用 K-means
有个棘手问题是要确定使用多少个聚类数来进行分组。起初我尝试结合分层聚类来使用,但最终还是决定用自己的眼睛来判断,可能会更好(虽然更耗时)!我为每一章都创建了一张下面的图,该图显示了抽取3-11
(不想过多)个颜色簇时各自的颜色分布。然后,我将实际封面与这些分相比较,选择了最合适的一组:在捕获所有颜色和很好地融合不同颜色之间取得平衡。其中最佳分组的十六进制颜色 hex colors
及其百分比 %s
(柱形高度)保存到 json 中后面会用到。
为了补充章节封面的数据,我还想收集每章中出现了哪些人物以及每张卡牌在哪章被捕获的信息( CCS
是关于 小樱/Sakura
收集魔法库洛牌 magical Clow cards
的故事)。每章的CCS Wiki页面似乎有用,但遗憾的是,这里只有前8章有上述信息。别无他法,我只能自己重新阅读所有章节,并将所需信息慢慢填到 Excel 文件里😜。
由于本月的项目最终以“分层”可视化(一圈圈)的形式呈现,所以在整个创作过程中我最终将所有这些信息拆分成7个小文件(每个章节/封面中出现的人物,每个人物出现过的章节数百分比,人物之间的关系,颜色分布等等)。我更喜欢用 R 语言
预先准备好所有数据,我发现这是最简单的方式,这样不会使我的 JavaScript
代码太混乱。
第2周| 草图 sketch
弄清本月可视化怎么设计的进展很缓慢。这更像多米诺骨牌效应,某个方面更具体的想法会导致后续另一方面数据探索想法的模糊(古柳:毕竟时间就那么多)。我从如何可视化颜色开始:漫画的每章用一簇带颜色的小圆圈表示
,这似乎是合逻辑且有趣的一步;之后,将颜色簇以径向布局
,也是显而易见的选择。尽管起初我想做一个半圆,颜色簇在右,人物信息在左。但是,因为有50章,就需要尽可能多的空间。 因此,这就是为什么我采用分层的方式,内圈小圆圈是所有人物,外圈大圆圈是所有章节的颜色簇,将这两个圆圈用线连接起来,以显示哪些人物出现在哪些章节中
。
我一直对 CMYK 点打印过程/CMYK dot printing process
着迷;当你近距离观察时可以看到单独的点,但远点观察,可以看到更大的画面(我想我并不是唯一一个会用鼻子从字面上触摸旧杂志、或旧电视(看RGB条纹)的人,对吧?)。重新创建这种 CMYK点技术
,以可视化展示(印刷)漫画,似乎是一种合适的方式,当然也更具挑战。确实很有挑战!我不会在这里进行详细介绍(你可以在代码部分了解更多内容),但是在下图的右侧,您可以看到我为了解如何重新创建 CMYK
效果而画的一些草稿(需要进行旋转操作...)。
本月的另一个数学挑战是我最终未使用的一些东西......最初,我想用螺旋线将人物内圈与章节外圈相连(从技术上讲,两个相连的 SVG 三次贝塞尔曲线
/SVG Cubic Bézier Curves)。为确保这些线将始终围绕内圈并看起来不错,我在素描本中花了大量的时间和注释!我总是喜欢画出我能想到的近似 SVG
路径形状,然后尝试找出应该放置的新点和锚点的位置。真正困难的部分是知道这些点和锚点在数据变化时如何相应变化,如何创建适用于所有实例的“公式”。
下图专门展示了在不同情况下如何处理圆切线的计算。我需要这些信息以用于上面的那些螺旋线。尽管我尝试了不同的线,但幸运的是,我最终项目里只用到了这两页里部分的内容以方便地转换线条(更圆的线)。
第三周和第四周| 编程
我首先专注于在屏幕上放置颜色圆圈。主要是因为我想尽快看看 CMYK
这一想法的效果如何。多亏了Shirley 这个多数据点引力布局的出色示例,使一切简单了很多!但糟糕的是,这些圆圈必须变得很小,才能为所有50个章节腾出空间。我开始怀疑 CMYK
效果是否能像我希望的那样在这次特殊设计中发挥作用......
尽管如此,我还是先投入 Veltman 的令人惊叹的代码,在这里他已经将 CMYK
点效果巧妙地编码成 SVG
模式(顺便提下,我开始在Pinterest / DataViz | D3.js Demos - Nadieh Bremer上收集自己喜欢的 d3.js 内容),所以我有个视觉化的“书签”方便检索。重写代码,为每种颜色创建一种单独的图案,然后我得到一个充满了基于 CMYK
颜色圆圈的圆环。但是仔细检查后,我发现了一些我不喜欢的东西,尽管里面的圆圈看起来确实像我想要的,但它们仍然是 SVG
元素。它们已经完美地被剪裁成一个圆圈,就像被剪裁的图案。但是我希望我的 CMYK
点的边缘更平滑;以及我还想尝试下使圆圈部分重叠,并使颜色进一步混合,这些都是当前的实现无法达到的。
因此,我在网上进行了广泛搜索,以查找其他示例。我已经预料到使用HTML5 Canvas
或许能行。我确实找到了两个示例:canvas-halftoning.html和COLOR HALFTONE FILTER FOR CANVAS 2D,并花了3-4个小时将其合并到一起,其中进行了大量的测试和调整......
最终,我得到了视觉上的另一种可选方案,并且看起来像是我想要的。首先是平滑的边缘,点在边缘部分会变小,但是整个 CMYK
点仍然是完整的点,而且,我还可以在两个圆的 CMYK
效果都可见的情况下,画出彼此重叠的圆。
然后我将其应用到50个章节中的所有圆圈上......预料之中,圆圈太小,以至于没有“足够的CMYK”。有时很难感受到圆圈的真实颜色,因为它只包含少数的CMYK点😖。
好吧,(可视化)设计有时候就是这样,花费数小时完成的工作可能最后也不一定生效。因此,我将一个简单的版本整理出来:Canvas CMYK Halftone effect,没准哪天还能用上,并且再次看看最初的 SVG
版本。那些粗糙的外边缘该如何处理? ...嗯...加上粗笔触如何?是的,这对我来说就足够了,哈哈😅
我以为内圈和外圈之间的连线可能是接下来要解决的最困难的事情,但是要正确地做到这一点,我首先需要内圈。使用d3的弧线和饼图布局(d3.arc()/d3.pie()
,Donut Chart),可以轻松地绘制出我草图中预想的那种薄薄的甜甜圈图donut chart
。在进行外部线条处理之前,我首先想看看内圈人物之间的连接/关系是否在“视觉上生效”。因为如果它不生效,我可能不得不考虑其他总布局。
是时候再次编写一些自定义 SVG
路径了!下图展示了路径优化的过程,从左上到右下,最初版本是最简单的直线,最终版本由圆形组成(路径形状更巧妙),使用 SVG arc
指令,重用了 「十一月奇幻小说」
一文中为小圆弧编写的代码。
然后我根据连接的关系类型(如家人、爱人)为线条上色,这使我看到那里不会因为有太多线条而无法获得洞见,没有视觉上的负担。OK,接下来就可以处理外部线条了......
我创建的最夸张的线条是连接人物与圆圈另一侧的章节,这条线将不得不绕过内圈,而不触碰到其他人物的名字。我想我可以通过组合2条三次贝塞尔曲线
Cubic Bézier Curve来实现这一目标。但是,依照数据,使其中一条曲线如所想的绘制就已经够麻烦,两条曲线更是两倍以上的麻烦。o_O
对于困难的 SVG
路径,我总是首先沿线条路径自身放置小圆圈(下图中的红色圆圈)+ 锚点(蓝色、绿色和橙黄色的圆圈,由于另一个原因,我放置了粉色的圆圈,但太过技术性所以这里不做解释了)
经过一番手动调整后,我得到了还算喜欢的长线条版路径。我保存了这些设置,然后又做了版短线条的。接着,检查这两种方式所有设置的不同,这给了我如何提炼公式的灵感,以便无论起点和终点如何变化,都能生成不错的线条。但是,如我所言,这次并没有我想的那么简单...
唉,我甚至不敢回想我是怎么一步步得到想要的线条的。我的草稿中大多数注释都和这部分有关。因为当线逆时针而不是顺时针旋转时,需要做些略微不同的事情。如果您把所有事情都搞砸了,那么就像会像下图这样......
经过多少个小时的测试、绘制、思考和调整,我才使所有线条都至少围绕中心排列。尽管这里线条的细微细节仍然相当奇怪。
我没有记录“创建线条”这部分耗时多久,但大概在8到10个小时之间。之后,当我将所有线条可视化,效果如下(这里显示了每个人物出现的所有章节):
好吧,看起来还是很混乱!线条太多以致无法获取任何洞见。这意味着我将不得不创建某种悬浮交互效果,当鼠标悬浮在人物或章节上时,显示相应部分的线条。
到目前为止,在这些线条上花费了太多时间。因此,我想继续添加章节和卷号。内部的甜甜圈图也启发了我在章节部分尝试类似的方式。一个包含50个大小相等部分的甜甜圈图,并在每一部分放置一个数字表示章节号。我对最终结果感到满意,并且快速推进。
对于漫画的卷(通常为±4章),一开始我也带着相同的想法:一个更薄的甜甜圈图,将其放在圆圈簇外侧。但我对此并不满意,与此同时,还有些事情困扰着我。现在,我要在页面上放置更多元素,并且所有这些元素似乎都具有“统一性”,除了那些内部线条😕
也许像我在一月份 Dragonball Z 的可视化中所做的那样,通过使它们变细,让它们具有更多的“主体内容”?再次花许多时间来调整我的线条公式......尽管我认为它确实比我以前使用的相同宽度的线条有所改善:
当我实现悬浮交互时,效果不错。例如看看某一章出现了哪些人...
...或将鼠标悬浮在人物上以查看他们出现的所有章节...
...但是,我只是觉得就设计而言,其余的视觉效果还是不太好。啊!接下来该怎么办!投入了这么多时间了!😭
突然,在知道我的螺旋线不合适后,我很快对这些线条有了新的想法。我甚至忘了是什么启发了我的灵感,它似乎凭空而来(尽管事实并非如此)。除了使它们环绕,我还可以使它们沿着圆形路径绘制。有点像地铁线或房屋中的管道,只不过变成径向布局。
这个想法实际上很容易实现。我可以遍历要绘制的每条线,生成由[半径, 弧度]
/[radius,angle]
组成的数组,并将其传给 d3.radialLine()
函数。同时设置插值函数使边缘弯曲。与之前的三次贝塞尔曲线相比,计算半径和角度的数组并传给 d3.radialLine()
不知简单了多少倍。(虽然享受解决几何难题确实有帮助)。当然,所有这些在第一次尝试时没有马上成功,但值得庆幸的是,我可以用到螺旋线中做过的一些工作。下图均在1小时内完成,进展不错。😉
然后我使用非常有用的 .context
方法将所有这些线条转换成 HTML5 canvas
,这在 d3 的许多绘图函数中都可用(例如 d3.radialLine()
),这样悬浮交互就更流畅了!
通过线条的变化,我觉得当悬浮在某个人物或章节上时,所得的线条在结束位置均以“直角”呈现(我希望这更有道理的,呵呵)。另外一个好处是,没有更多的线条发生重叠!
细心的人会注意到,我实际上实现了两种略有不同的线条“类型”。默认情况,不悬浮,或悬浮在人物与颜色簇上时,会绘制一种;当鼠标悬浮在章节上时,会绘制另一个。可自行尝试,看看区别。
由于可视化的一部分与各章的封面有关,而且我知道大多数打开这个页面的人可能并不知道 CCS
,所以我想收录其中的某些图像。而且恰好在中央有一个不错的圆形区域:) 我花了些时间才从所有50个章节的封面中手动“切出”了一个漂亮的方形图像,那时我还在机场和飞机上(从“信息就是美丽奖” Beautiful Awards
回来的一个美好夜晚,那次我们的 data sketches
拿了金奖!Woohoo!)
以上可视化中包含了所有这些元素和信息层后,我确实需要加个图例。由于最近的两个给客户的项目中,我写了许多代码来创建自定义图例(例如,Article 19 上的这个项目)因而,我不想再来一遍,于是改用 Illustrator 创建图例,这比用代码实现节省了很多的时间。
我最初将它们放置在可视化图表的下方,但是(本月已习惯了这种情况),这并不是最终的图例......
但是扯回来,既然图表本身快要完成了,我将重点放在一般的页面布局和注释说明上。对于布局,我遇到了很多麻烦,甚至提出了一些看起来很有趣的东西。老实说,我对最终结果仍然不满意,但我还不能真正为它设计网页,而只能设计数据可视化,hehe😅
随后为了使该布局兼容移动设备和电脑端显示...😫由于不是什么值得记述的内容,就此略过,只需知道我又花了许多时间和精力即可。
我加上了些我想强调的、有趣的故事看点,以便大家阅读所有章节时能有所了解。使用 Susie Lu
出色的 d3-annotation 库,将这些添加到圆圈周围非常简单,尤其是使用超级方便的编辑模式 editmode
,我可以拖动注释,获取合适的位置,然后将这些硬编码的位置写到代码中。
但是在放置完所有注释后(上图为合适的视觉效果),我对结果仍不满意。通常,我喜欢在要注释的线上加上文字注释。但是,这样会引入太多视觉重量visual weight
。
因此,我想用从主圆圈向外辐射的短线,并将注释放在与颜色簇中心对齐的外围。而且,尽管 d3-annotation
库提供了诸多便捷,但是却不包含上述特殊设计,因此,我自己创建短线,然后使用编辑模式(参见下图的虚线小圆圈,您可以随意拖动这些圆)将注释精确地放在我想要的位置。
在我觉得可视化已经差不多完成后,我和一些朋友分享了该作品,以征求反馈,也获得了改善交互理解方面的很棒的建议。其中,还有一个人给我分享了一个更好的图例的案例,可视化里展示了每圈并解释每圈的真正含义,这比我之前那三个独立的图例要好得多,所以我在 Illustrator
中重画了个图例。
花费许多的时间和精力后,我终于做出了这个可视化项目,与大家分享:),链接在此:Cardcaptor Sakura - Fifty chapters of adorable cuteness
这个月我花了86个小时
来完成这个项目。但是,大部分时间都花在了最终没用上的事情上。例如在基于 CMYK
画布的点效果上花了±5小时,在螺旋线上花了±15小时,在最终可能没人会点击查看的页面布局上花了±6小时(除非您点页面上的“read more”),以及在水平滚动的、愚蠢的 Chrome bug
上花了±2个小时(以及如何“修复”)。
尽管如此,我对可视化的结果还是很满意的 :),我觉得我之前没有看到过类似的径向可视化作品。data sketches
总是尝试新的想法,所以当某个想法结果不错时总是件值得高兴的事^ _ ^希望您在交互这个可视化项目时会喜欢,即使您此前从未听说过「百变小樱」(很棒的漫画!相信我!)。
网友评论