Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

使用 stroke-dasharray 制作环形图 #60

Open
YIXUNFE opened this issue Mar 7, 2016 · 0 comments
Open

使用 stroke-dasharray 制作环形图 #60

YIXUNFE opened this issue Mar 7, 2016 · 0 comments

Comments

@YIXUNFE
Copy link
Owner

YIXUNFE commented Mar 7, 2016

使用 stroke-dasharray 制作环形图

近期项目中有一个环形图的需求,经过大家研究后决定使用一款开源插件完成。选用插件主要是考虑到需要兼容 IE678,而目前我们团队中没人熟悉 VML。但是这里我仅简单介绍下我们完成的 N 个实验性方案中的一个 —— 使用 stroke-dasharray 制作环形图。


原理

以前在《玩转虚线边框》的文章中提及过,SVG 元素可以通过 stroke-dasharray 设置虚线边框的长度以及虚线之间的宽度。所以我们只需要通过圆形的路径,使用虚线长度表述所占百分比即可。

比如环状图中有 4 个区域,每个区域各占 25%,那么每个区域应该设置虚线长度为周长的 25%,虚线与虚线之间的间距应该是周长的 75%。

我们可以使用 circle 元素画圆,并指定它的圆心与半径。由于我们最终需要的是圆环,那么我们也需要给圆形指定边宽 stroke-width

<circle cx="200" cy="200" r="120" stroke-width="80" stroke="#ff0000" fill="none"  ></circle>

1

加上 stroke-dasharray 之后:

<circle cx="200" cy="200" r="120" stroke-width="80" stroke="#ff0000" fill="none" stroke-dasharray="189 566" ></circle>

20

stroke-dasharray 的两个值,一个表示虚线的长度,另一个表示虚线与虚线之间的间距,而间距之间是一片空白,所以图形从圆变成了圆弧。然而这好像和我们想象中的有点不一样,我们希望它是从右上方开始的。所以我们需要转换一下坐标系。

<circle cx="200" cy="200" r="120" stroke-width="80" stroke="#ff0000" fill="none" stroke-dasharray="189 566" transform="rotate(-90, 200, 200)" ></circle>

设置 transformrotate 属性,里面的三个参数分别表示旋转的角度、旋转中心的 X 轴坐标,旋转中心的 Y 轴坐标。这里旋转中心就是我们的圆心。

30

这样我们就完成了第一个区域的制作。接下来我们制作第二个区域。第二个区域的圆弧起始位置应该在第一个圆弧的结束位置,即 0 度处(-90 + 360 * 0.25)。

<circle cx="200" cy="200" r="120" stroke-width="80" stroke="#ffff00" fill="none" stroke-dasharray="189 566" transform="rotate(0, 200, 200)" ></circle>

40

以此类推,我们就可以完成整个环状图的构建工作。


抽象

为了能够更加方便的绘制环状图,我们抽象出了一个方法。

/**
 * createArcs 
 * @param {Object} args 配置项
 * @example createArcs({
      bin: div,
      width: 400,
      height: 400,
      r: 180,
      arcWidth: 20,
      data: [
        {color: '#ff0000', percent: 0.2, text: '第1项'},
        {color: '#ffff00', percent: 0.7, text: '第2项'},
        {color: '#ff00ff', percent: 0.1, text: '第3项'}
      ]
    })
 */
function createArcs (args) {
  var html = '<svg id="ringSvg" width="' + args.width + '" height="' + args.width + '" >',
    cx = args.width / 2, cy = args.height / 2,
    r = args.r,
    aw = args.arcWidth,
    data = args.data,
    i = 0, l = 0,
    deltaR = 15, //鼠标移动上去时候的偏移值
    circleLong = r * 2 * Math.PI,
    cl2 = (r + deltaR) * 2 * Math.PI,
    currentReg = 0,
    itemData = []

  html += '<circle class="defaut-arc" cx="' + cx + '" cy="' + cy + '" r="' + r + '" stroke-width="' + aw + '" stroke="none" fill="none"></circle>'

  l = data.length
  for (i; i < l; i++) {
    html += (function (i) {
      return getCircle(data[i])
    }(i))
  }

  function getCircle (d) {
    var l1 = Math.ceil(circleLong * d.percent),
      l2 = Math.ceil(circleLong * (1 - d.percent)),
      l3 = Math.ceil(cl2 * d.percent),
      l4 = Math.ceil(cl2 * (1 - d.percent)),
      html = ''
      // rotate(' + (d.percent * 180 + currentReg * 360) + ', ' + cx + ', ' + cy + ')
    html = '<circle class="arc-item arc-item' + i + '" cx="' + cx + '" cy="' + cy + '" r="' + r + '" stroke-width="' + aw + '" stroke="' + d.color + '" fill="none" transform="rotate(' + (-90 + currentReg * 360) + ', ' + cx + ', ' + cy + ')" stroke-dasharray="' + l1 + ' ' + l2 + '"></circle>'
    currentReg = (d.percent * 100 + currentReg * 100) / 100
    itemData.push([l1 + ' ' + l2, l3 + ' ' + l4])
    return html
  }

  html += '</svg>'

  args.bin.innerHTML = html

}

查看 DEMO


过渡动画

为了能够多点交互效果,我们还尝试了设置半径做放大缩小的效果。

//正常时
<circle cx="200" cy="200" stroke-dasharray="189 566" transform="rotate(-90, 200, 200)" r="120" stroke-width="80" stroke="#ff0000" fill="none"  ></circle>

//放大时
<circle cx="200" cy="200" stroke-dasharray="213 637" transform="rotate(-90, 200, 200)" r="135" stroke-width="80" stroke="#ff0000" fill="none"  ></circle>
.arc-item {transition: r ease-in .25s, stroke-dasharray ease-in .25s}

由于半径改变了,所以 stroke-dasharray 也会相应变化,因为周长和半径相关。

23

这个效果目前在 Chrome 48 下可以完美运行,但 Chrome 42 确认不行,IE 、FF 也不行。可能原因是 CSS 并不支持 r 属性的过渡。因为规范中列出的 CSS 支持的 SVG 属性中并没有 r 属性。


用百分比设置 stroke-dasharray

以前也没想到过使用百分比设置 stroke-dasharray,这次由于是比例关系,用百分比设置看起来会方便很多,比如 stroke-dasharray="25% 75%",是不是就可以省去周长的计算呢?答案是不行。经过一些测试,发现百分比的参照物并不是圆形自身,而是 svg 元素。百分比的取值也比较奇特,100% 表示的是参照物高宽的平均值,即 (width + height) / 2


Thanks


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant