使用SVG和Vue.js开发动态树图

我们今天发布的翻译材料专门用于开发动态树图可视化系统的过程。 为了绘制三次贝塞尔曲线,此处使用了SVG技术(可缩放矢量图形,可缩放矢量图形)。 数据的响应式工作由Vue.js组织。

这是您可以进行实验的系统演示版本。


互动树形图

SVG强大功能与Vue.js框架的结合使我们能够创建一个用于构建基于数据,可交互且可自定义的图表的系统。

图表是从一个点开始的三次贝塞尔曲线的集合。 曲线终止于彼此等距的不同点。 它们的最终位置取决于用户输入的数据。 结果,该图表能够对数据变化做出反应。

首先,我们将讨论Bezier三次曲线的形成方式,然后我们将弄清楚如何在<svg>元素的坐标系中表示它们,并讨论为图像创建蒙版。

资料的作者说,她为他准备了许多插图,试图使他易于理解和有趣。 该材料的目的是帮助每个人获得开发自己的图表系统所需的知识和技能。

SVG


cubic三次贝塞尔曲线如何形成?


该项目中使用的曲线称为三次贝塞尔曲线。 下图显示了这些曲线的关键元素。


贝塞尔曲线的关键元素

曲线由四对坐标描述。 第一对(x0, y0)是曲线的起始锚点。 最后一对坐标(x3, y3)是最终参考点。

在这些点之间,您可以看到所谓的控制点。 这是点(x1, y1)和点(x2, y2)

控制点相对于控制点的位置决定了曲线的形状。 如果曲线仅由起点和终点,坐标(x0, y0)(x3, y3) ,则该曲线看起来像是对角线的直线段。

现在,我们将使用SVG <path>元素使用上述四个点的坐标绘制曲线。 这是<path>元素中用于构造三次贝塞尔曲线的语法:

 <path D="M x0,y0 C x1,y1 x2,y2 x3,y3" /> 

可以在代码中看到的字母是三次贝塞尔曲线的缩写。 小写字母( c )表示使用相对值,大写字母( C )表示使用绝对值。 我使用绝对值来构建该图,该示例中使用的大写字母表示了这一点。

▍创建对称图


对称性是该项目的关键方面。 为了构建对称图,我仅使用了一个变量,并根据其接收诸如某个对象中心的高度,宽度或坐标之类的值。

我们将此变量命名为size 。 由于图表是水平放置的,因此可以将size变量视为图表可用的整个水平空间。

为该变量分配一个实际值。 我们将使用此值来计算图表元素的坐标。

 size = 1000 

查找图表元素的坐标


在找到构建图表所需的坐标之前,我们需要处理SVG坐标系。

ordinate坐标系和viewBox


<svg> viewBox的属性在我们的项目中非常重要。 事实是它描述了SVG图像的用户坐标系。 简而言之, viewBox决定将在其中创建屏幕上可见的SVG图像的空间的位置和大小。

viewBox属性由四个数字组成,这些数字指定坐标系统的参数,其顺序如下: min-xmin-ywidthheightmin-xmin-y参数设置用户坐标系的原点, widthheight参数设置显示图像的宽度和高度。 这是viewBox属性的外观:

 <svg viewBox="min-x min-y width height">...</svg> 

我们上面描述的size变量将用于控制此坐标系的widthheight参数。

稍后,在有关Vue.js的部分中,我们将把viewBox绑定到一个计算属性,以指定widthheight值。 此外,在我们的项目中,属性min-xmin-y将始终设置为0。

请注意,我们不使用<svg>元素本身的heightwidth属性。 我们将使用CSS将它们设置为width: 100%height: 100% 。 这将使我们能够创建可灵活调整为页面大小的SVG图像。

现在,用户坐标系已准备好绘制图表,让我们来谈谈使用size变量计算图表元素的坐标。

▍不变和动态的坐标



图表概念

显示图形的圆是该图的一部分。 因此,从一开始就将其包括在计算中很重要。 让我们根据上面的插图,找出圆和一条实验曲线的坐标。

图表的高度分为两部分。 它们分别是topHeightsize 20%)和bottomHeightsize的其余80%)。 图的总宽度分为两部分-每部分的长度为size 50%。

这样就可以得出圆弧参数的结论,无需特别说明(此处使用halfSizetopHeight指示符)。 radius参数设置为topHeight值的topHeight 。 因此,圆环非常适合可用空间。

现在让我们看一下曲线的坐标。

  • 坐标(x0, y0)定义曲线的起始参考点。 这些坐标始终保持不变。 x0坐标是图表的中心( size一半),而y0是圆形底部结束的坐标。 因此,在用于计算该坐标的公式中,使用了圆的半径。 结果,可以通过以下公式找到该点的坐标: (50% size, 20% size + radius)
  • 坐标(x1, y1)是曲线的第一个控制点。 所有曲线也保持不变。 如果我们不忘记曲线应该是对称的,那么x1y1的值总是等于size一半。 因此,它们的计算公式为: (50% size, 50% size)
  • 坐标(x2, y2)表示贝塞尔曲线的第二个控制点。 x2表示曲线应为什么形状。 该指标是为每条曲线动态计算的。 和以前一样,指标y2size为一半。 因此,以下公式可用于计算这些坐标: (x2, 50% size)
  • 坐标(x3, y3)是曲线的终点参考点。 该坐标指示您要在哪里完成绘制线条。 在此, x3的值(如x2 )是动态计算的。 y3的值等于size 80%。 结果,我们得到以下公式: (x3, 80% size)

通常,我们考虑到刚得出的公式来重写<path>元素的代码。 上面使用的百分比通过将它们除以100得出。

 <path d="M size*0.5, (size*0.2) + radius          C size*0.5, size*0.5           x2,    size*0.5           x3,    size*0.8" > 

请注意,乍看之下,仅根据我自己的观点,在我们的公式中使用百分比似乎是可选的。 但是,这些值并不是一时兴起的,而是因为它们的使用有助于实现对称性和正确比例的图表。 在感觉到它们在图表中的作用之后,您可以尝试自己的百分比值并检查通过应用它们获得的结果。

现在让我们讨论一下如何寻找坐标x2x3 。 它们使您可以根据相应数组中元素的index动态创建许多曲线。

将图表的可用水平空间分成相等的部分是基于数组中元素的数量。 结果,每个零件沿x轴接收相同的空间。

我们得出的公式随后应可用于任何数量的元素。 但是这里我们将尝试一个包含5个元素的数组: [0,1,2,3,4] 。 这种阵列的可视化意味着必须绘制5条曲线。

dynamic查找动态坐标(x2和x3)


首先,我将size除以元素数,即除以数组的长度。 我称此为可变distance 。 它代表两个元素之间的距离。

 distance = size/arrayLength // distance = 1000/5 = 200 

然后,我在数组周围走来走去,将其每个元素的indexindex )乘以distance 。 为了简单起见,我只将x2参数和x3参数都称为x

 //  x2  x3 x = index * distance 

如果在构建图表时应用获得的值,也就是说,对x2x3使用上面计算的x值,将会有些奇怪。


该图是不对称的

如您所见,这些元素位于应放置的区域中,但是该图却是不对称的。 似乎在其左侧部分比右侧更多。

现在,我需要使x3值位于相应线段的中心,其长度是使用distance变量设置的。

为了使该图成为我需要的形式,我只是将distance一半添加到x

 x = index * distance + (distance * 0.5) 

结果,我找到了distance段的中心,并在其中放置了x3坐标。 另外,我将表格x2坐标设为x2坐标。


对称图

distance值的一半加到x2x3坐标上,使这些坐标的计算公式适合于可视化包含偶数和奇数元素的数组。

▍图像遮罩


我们需要在圆图的顶部显示一定的图像。 为了解决这个问题,我创建了一个包含圆圈的剪贴蒙版。

 <defs>  <mask id="svg-mask">     <circle :r="radius"             :cx="halfSize"             :cy="topHeight"             fill="white"/>  </mask> </defs> 

然后,使用<image> <svg> <image>元素的<image>标签显示图像,我使用<image>元素的mask属性将图像链接到上面创建的<mask> <image>元素。

 <image mask="url(#svg-mask)"      :x="(halfSize-radius)"      :y="(topHeight-radius)" ... > </image> 

由于我们试图将正方形图像拟合为圆形“窗口”,因此我通过从相应参数中减去radius参数来调整元素的位置。 结果,通过以圆形形式制成的掩模可以看到图像。

让我们收集我们在一张图纸中讨论的所有内容。 这将帮助我们了解工作进度的整体情况。


用于计算图表参数的数据

使用Vue.js创建动态SVG图像


至此,我们找出了三次贝塞尔曲线,并进行了必要的计算以形成图表。 结果,我们现在可以创建静态SVG图。 如果我们结合SVG和Vue.js的功能,则可以创建由数据驱动的图。 静态图表将变为动态。

在本节中,我们修订SVG图,将其显示为一组Vue组件。 我们还将SVG属性附加到计算的属性,并使图表响应数据更改。

此外,在项目结束时,我们将创建一个配置面板组件。 该组件将用于输入要传输到图表的数据。

▍将数据绑定到viewBox参数


让我们从调整坐标系开始。 如果不这样做,我们将无法绘制SVG图像。 计算的viewbox属性将使用size变量返回我们需要的内容。 将有四个值,以空格分隔。 所有这些将成为<svg>元素的viewBox属性的值。

 viewbox() {   return "0 0 " + this.size + " " + this.size; } 

在SVG中, viewBox属性的名称已经使用骆驼样式编写。

 <svg viewBox="0 0 1000 1000"> </svg> 

因此,为了正确地将此属性绑定到计算出的属性,我以.camel样式写下了属性名称,并在.camel放置了.camel修饰符。 使用这种方法,可以“欺骗” HTML并正确实现属性绑定。

 <svg :view-box.camel="viewbox">   ... </svg> 

现在,更改size独立重新配置图表。 我们不需要手动更改布局。

curve曲线参数的计算


由于构造曲线所需的大多数值都是基于单个变量( size )计算的,因此我使用计算出的属性来查找所有固定坐标。 我们所谓的“固定坐标”是根据size来计算的,此后它不会改变,并且不取决于图表将包含多少条曲线。

如果您更改size ,则会重新计算“固定坐标”。 在那之后,它们将不会更改,直到下一次更改size 。 鉴于以上所述,我们需要绘制五个值以绘制贝塞尔曲线:

  • topHeight — size * 0.2
  • bottomHeight — size * 0.8
  • width — size
  • halfSize — size * 0.5
  • distance — size/arrayLength

现在我们只剩下两个未知值x2x3 。 我们已经得出了计算它们的公式:

 x = index * distance + (distance * 0.5) 

若要查找特定值,我们需要在此公式中替换数组元素的索引。

现在,让我们问问自己,计算出的属性是否适合我们找到x 。 简短地回答这个问题,然后-不,它不会做。

计算的属性不能传递参数。 事实是这是一个属性,而不是一个函数。 另外,需要使用参数来计算某些内容,这意味着在缓存方面使用计算出的属性没有明显的优势。

请注意,上述原则是一个例外。 关于Vuex。 如果使用返回函数的Vuex getter,则可以将参数传递给它们。

在这种情况下,我们不使用Vuex。 但是即使在这种情况下,我们也有两种方法可以解决此问题。

▍选件编号1


您可以声明一个函数,将index作为参数传递给该函数,并返回我们需要的结果。 如果我们要在模板的多个位置使用相似函数返回的值,则此方法看起来更干净。

 <g v-for="(item, i) in itemArray">  <path :d="'M' + halfSize + ','     + (topHeight+r) +' '+            'C' + halfSize + ','     + halfSize +' '+                     calculateXPos(i) + ',' + halfSize +' '+                  calculateXPos(i) + ',' + bottomHeight"  /> </g> 

每次调用calculateXPos()方法都会执行计算。 此方法将元素的索引i作为参数。

 <script>  methods: {    calculateXPos (i)    {      return distance * i + (distance * 0.5)    }  } </script> 

这是使用此解决方案的CodePen上的示例。


第一个应用程序变体的屏幕

▍选项2


此选项比第一个更好。 我们可以提取将曲线构建为单独的小子组件所需的小SVG标记,并将index作为属性之一传递给它。

通过这种方法,您甚至可以使用计算属性查找x2x3

 <g v-for="(item, i) in items">    <cubic-bezier :index="i"                   :half-size="halfSize"                   :top-height="topHeight"                   :bottom-height="bottomHeight"                   :r="radius"                   :d="distance"     >     </cubic-bezier> </g> 

此选项使我们有机会更好地组织代码。 例如,我们可以为遮罩创建另一个子组件:

 <clip-mask :title="title"           :half-size="halfSize"           :top-height="topHeight"                               :r="radius"> </clip-mask> 

▍配置面板



配置面板

您可能已经看过配置面板,该面板由上屏幕左上角的按钮调用。 通过此面板,可以轻松地向数组中添加元素并将其从数组中删除。 遵循“选项2”部分中讨论的想法,我为配置面板创建了一个子组件。 因此,顶层组件是干净且易于阅读的。 结果,我们的Vue组件树看起来有点像下面的树。


项目组件树

是否想看看实现此项目版本的代码? 如果是这样,请在这里看看。


第二个应用程序变体的屏幕

项目资料库


这是项目 GitHub存储库(此处实现了“选项2”)。 我相信在继续下一部分之前,对它进行研究将对您很有用。

家庭作业


尝试创建与我们在此描述的图表相同的图表,但使其垂直定向。 利用本文概述的思想。

如果您认为这是一件容易的事,只要构建这样一个图就足够交换xy坐标,那么您是对的。 考虑到此处考虑的项目并未创建为通用项目,因此在更改了所需位置的坐标后,还需要通过重命名一些变量和方法来编辑代码。

感谢Vue.js,我们的简单图表可以配备其他功能。 例如,以下内容:

  • 您可以创建一种机制,使您可以在水平和垂直图表模式之间切换。
  • 曲线可以尝试设置动画。 例如,使用GSAP。
  • 您可以从配置面板调整曲线的属性(例如-颜色和线宽)。
  • 您可以使用外部库来组织以任何图形格式或PDF文件格式保存的图表。 可以允许将这些材料下载到使用图表的人员。

试试这个作业。 如果您有任何问题,请在下面找到其解决方案的链接。

总结


<path>元素是SVG的强大功能之一。 该元素使您可以高精度创建各种图像。 在这里,我们了解了Bezier曲线的结构,以及如何将其实践以创建自己的图。

, , JavaScript-. Vue.js . , , , , DOM. , — .

, , , , , Vue.js SVG. — , Vue.js. — .

, - , , , — .

亲爱的读者们! ?

Source: https://habr.com/ru/post/zh-CN463329/


All Articles