d3.js 入門學習記錄(七) 生成器

WanFengZ發表於2019-11-24

svg的基本形狀

  1. line 線條
    • x1 定義線條起點的 x 座標
    • y1 定義線條起點的 y 座標
    • x2 定義線條終點的 x 座標
    • y2 定義線條終點的 y 座標
  2. polyline 折線
    • points 定義折線的一系列點座標,座標間用空格隔開
  3. circle 圓
    • cx 定義圓心的 x 座標
    • cy 定義圓心的 y 座標
    • r 定義圓的半徑 r
  4. ellipse 橢圓
    • cx 定義圓心的 x 座標
    • cy 定義圓心的 y 座標
    • rx 定義橢圓在 x 軸方向上的半徑
    • ry 定義橢圓在 y 軸方向上的半徑
  5. rect 矩形
    • x 定義矩形的左上角的 x 座標
    • y 定義矩形的左上角的 y 座標
    • width 定義矩形的寬
    • height 定義矩形的高
  6. polygon 多邊形
    • points 定義多邊形的頂點座標,座標間用空格隔開
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .line,
        .polyline {
            fill: none;
            stroke: steelblue;
            stroke-width: 2;
        }

        .circle {
            stroke: steelblue;
            stroke-width: 2;
            fill: red;
            fill-opacity: .7;
        }

        .ellipse {
            stroke: steelblue;
            stroke-width: 2;
            fill: yellow;
            fill-opacity: .7;
        }

        .rect,
        .polygon {
            stroke: steelblue;
            stroke-width: 2;
            fill: pink;
            fill-opacity: .7;
        }
    </style>
</head>
<body>
<script src="../d3.js"></script>
<script>
    const svg = d3.select('body').append('svg')

    svg.attr('width', 1000)
        .attr('height', 400)

    svg.append('line')
        .attr('x1', 0)
        .attr('y1', 200)
        .attr('x2', 100)
        .attr('y2', 100)
        .classed('line', true)

    svg.append('circle')
        .attr('cx', 200)
        .attr('cy', 150)
        .attr('r', 50)
        .classed('circle', true)

    svg.append('ellipse')
        .attr('cx', 350)
        .attr('cy', 150)
        .attr('rx', 75)
        .attr('ry', 50)
        .classed('ellipse', true)

    svg.append('rect')
        .attr('x', 450)
        .attr('y', 100)
        .attr('width', 100)
        .attr('height', 100)
        .attr('rx', 10)
        .attr('ry', 20)
        .classed('rect', true)

    svg.append('polygon')
        .attr('points', '600,200 650,100 700,200')
        .classed('polygon', true)

    svg.append('polyline')
      .attr('points', '750,133 780,100 850,155 900,122')
      .classed('polyline', true)
</script>
</body>
</html>
複製程式碼

效果如下:

d3.js 入門學習記錄(七) 生成器

線條生成器 line()

d3 的生成器可以幫助我們將資料對映到svg的屬性中,簡化了我們對svg的操作。線條生成器就可以幫助我們十分簡單的繪製路徑。

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .line {
            fill: none;
            stroke-width: 2;
            stroke: steelblue;
        }
        .dot {
            fill: #fff;
            stroke: steelblue;
        }
    </style>
</head>
<body>
<h4>Interpolation Mode:</h4>
<div class="control-group">
    <button onclick="render(d3.curveLinear)">linear</button>
    <button onclick="render(d3.curveLinearClosed)"> linear closed</button>
    <button onclick="render(d3.curveStepBefore)">step before</button>
    <button onclick="render(d3.curveStepAfter)">step after</button>
    <button onclick="render(d3.curveBasis)">basic</button>
    <button onclick="render(d3.curveBasisOpen)">basic open</button>
    <button onclick="render(d3.curveBasisClosed)">basic closed</button>
    <button onclick="render(d3.curveBundle)">bundle</button>
    <button onclick="render(d3.curveCardinal)">cardinal</button>
    <button onclick="render(d3.curveCardinalOpen)">cardinal open</button>
    <button onclick="render(d3.curveCardinalClosed)">cardinal closed</button>
    <button onclick="render(d3.curveMonotoneY)">monotone y</button>
    <button onclick="render(d3.curveCatmullRom)">catmull rom</button>

</div>
<script src="../d3.js"></script>
<script>
  const width = 500,
    height = 500,
    margin = 50,
    x = d3.scaleLinear()
      .domain([0, 10])
      .range([margin, width - margin]),
    y = d3.scaleLinear()
      .domain([0, 10])
      .range([height - margin, margin])

  const data = [
    [
      {x: 0, y: 5},
      {x: 1, y: 9},
      {x: 2, y: 7},
      {x: 3, y: 5},
      {x: 4, y: 3},
      {x: 6, y: 4},
      {x: 7, y: 2},
      {x: 8, y: 3},
      {x: 9, y: 2}
    ],
    d3.range(10).map(function (i) {
      return {x: i, y: Math.sin(i) + 5}
    })
  ]

  const svg = d3.select('body')
    .append('svg')
      .attr('width', width)
      .attr('height', height)

  const xScale = d3.scaleLinear()
    .domain([0, 10])
    .range([0, width - 2 * margin])

  const xAxis = d3.axisBottom()
    .scale(xScale)

  svg.append('g')
    .classed('x-axis', true)
    .attr('transform', function () {
      return `translate(${margin}, ${height - margin})`
    })
    .call(xAxis)

  const yScale = d3.scaleLinear()
    .domain([10, 0])
    .range([0, height - 2 * margin])

  const yAxis = d3.axisLeft()
    .scale(yScale)

  svg.append('g')
    .classed('y-axis', true)
    .attr('transform', function () {
      return `translate(${margin}, ${margin})`
    })
    .call(yAxis)

  function render(mode) {
    const line = d3.line()
      .x(function (d) {
        return x(d.x)
      })
      .y(function (d) {
        return y(d.y)
      })
      .curve(mode)

    const lines = svg.selectAll('path.line')
      .data(data)

    lines.enter()
      .append('path')
          .classed('line', true)
      .merge(lines)
        .attr('d', function (d) {
          return line(d)
        })
  }

  function renderDots() {
    data.forEach(function (item) {
      svg.append('g').selectAll('circle')
        .data(item)
        .enter()
          .append('circle')
             .classed('dot', true)
        .attr('cx', function (d) {
          return x(d.x)
        })
        .attr('cy', function (d) {
          return y(d.y)
        })
        .attr('r', 4.5)
    })
  }

  render(d3.curveLinear)
  renderDots()
</script>
</body>
</html>
複製程式碼

效果如下:

d3.js 入門學習記錄(七) 生成器

在圖表中,我們的線條是用 path 繪製的而不是 line,我們用line生成器來對資料進行處理,直接生成了 path 的 d 屬性而不用我們去計算。

 const line = d3.line()
      .x(function (d) {
        return x(d.x)
      })
      .y(function (d) {
        return y(d.y)
      })
      .curve(mode)
複製程式碼

上面這段程式碼就定義了一個線條生成器,我們通過 x() y() 去指定拿來計算座標的 x y 值,返回資料在尺度中的對應值,因為這才是對應到圖表中的座標,然後 curve() 可以指定線條的型別。

檢視內建的曲線型別可以在 github.com/d3/d3-shape…

區域生成器 area()

程式碼和線條生成器的示例一樣,在 render 函式里加入以下:

const area = d3.area()
        .x(function (d) {
            return x(d.x)
        })
        .y0(y(0))
        .y1(function (d) {
            return y(d.y)
        })
        .curve(mode)

const areas = svg.selectAll('path.area')
    .data([data])

areas.enter()
    .append('path')
        .classed('area', true)
    .merge(areas)
    .attr('d', function (d) {
        return area(d)
    })
複製程式碼

效果如下:

d3.js 入門學習記錄(七) 生成器

線上圖的基礎上,我們在其中多渲染了面積的部分,同樣是藉助於 path 元素。

區域生成器需要我們去定義上邊界和下邊界 x0() x1() y0() y1(),這裡因為我們的 x0() x1()是同樣的,可以用 x() 方法統一指定。

curve() 指定區域生成器的曲線型別,和線條生成器一般用同種。

圓弧生成器 arc()

圓弧生成器同樣要和 path 一起使用。

常使用的設定有:

  • arc.innerRadius() 設定環的內半徑
  • arc.outerRadius() 設定環的外半徑
  • arc.cornerRadius() 設定拐角半徑
  • arc.startAngle() 設定起始角度的取值,預設讀取資料中的 startAngle 屬性
  • arc.endAngle() 設定終止角度的取值,預設讀取資料中的 endAngle 屬性
  • arc.padAngle() 設定相鄰兩個環之間的間隙角度,預設讀取資料中的 padAngle 屬性
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<div class="control-group">
    <button onclick="render(0)">circle</button>
    <button onclick="render(100)">annulus</button>
    <button onclick="render(0, Math.PI)">circle sector</button>
    <button onclick="render(100, Math.PI)">annulus sector</button>
</div>
<script src="../d3.js"></script>
<script>
    const width = 400,
          height = 400,
          colors = d3.scaleOrdinal(d3.schemeCategory10)

    const svg = d3.select('body').append('svg')
            .classed('pie', true)
            .attr('height', height)
            .attr('width', width)

    function render(innerRadius, endAngle) {
        if (!endAngle) {
            endAngle = 2 * Math.PI
        }

        const data = [
            {startAngle: 0, endAngle: 0.1 * endAngle},
            {startAngle: 0.1 * endAngle, endAngle: 0.2 * endAngle},
            {startAngle: 0.2 * endAngle, endAngle: 0.4 * endAngle},
            {startAngle: 0.4 * endAngle, endAngle: 0.6 * endAngle},
            {startAngle: 0.6 * endAngle, endAngle: 0.7 * endAngle},
            {startAngle: 0.7 * endAngle, endAngle: 0.9 * endAngle},
            {startAngle: 0.9 * endAngle, endAngle: endAngle}
        ]

        const arc = d3.arc()
            .outerRadius(200)
            .innerRadius(innerRadius)

        svg.select('g').remove()

        svg.append('g')
            .attr('transform', 'translate(200, 200)')
        .selectAll('path')
              .data(data)
              .enter()
                .append('path')
                    .classed('arc', true)
                    .attr('fill', function (d, i) {
                        return colors(i)
                    })
                    .attr('d', function (d, i) {
                        return arc(d)
                    })
    }

    render(0)
</script>
</body>
</html>
複製程式碼

效果如下:

d3.js 入門學習記錄(七) 生成器

圓弧的過渡

給圓弧新增過渡效果時,要使用中間幀 attrTween()。

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script src="../d3.js"></script>
<script>
    const width = 400,
          height = 400,
          endAngle = 2 * Math.PI,
          colors = d3.scaleOrdinal(d3.schemeCategory10)

    const svg = d3.select('body').append('svg')
        .classed('pie', true)
        .attr('width', width)
        .attr('height', height)

    function render(innerRadius) {
        const data = [
            {startAngle: 0, endAngle: 0.1 * endAngle},
            {startAngle: 0.1 * endAngle, endAngle: 0.2 * endAngle},
            {startAngle: 0.2 * endAngle, endAngle: 0.4 * endAngle},
            {startAngle: 0.4 * endAngle, endAngle: 0.6 * endAngle},
            {startAngle: 0.6 * endAngle, endAngle: 0.7 * endAngle},
            {startAngle: 0.7 * endAngle, endAngle: 0.9 * endAngle},
            {startAngle: 0.9 * endAngle, endAngle: endAngle}
        ]

        const arc = d3.arc().outerRadius(200).innerRadius(innerRadius)

        svg.select('g').remove()

        svg.append('g')
            .attr('transform', 'translate(200, 200)')
        .selectAll('path')
            .data(data)
            .enter()
                .append('path')
                    .classed('arc', true)
                    .attr('fill', (d, i) => colors(i))
            .transition().duration(1000).delay(3000)
            .attrTween('d', d => {
                const scale = d3.scaleLinear().domain([0, 1]).range([{startAngle: 0, endAngle: 0}, d])
                return t => arc(scale(t))
            })
    }

    render(100)
</script>
</body>
</html>
複製程式碼

效果如下:

d3.js 入門學習記錄(七) 生成器

相關文章