Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

Hi I'm trying to use attrTween for my radar chart. I made it happen for arc chart so I wanted to apply the same logic to my radar chart but it didn't work out.

can anyone let me know which part I missed? I would like to shrink the radar chart on 'click'.

the code is as below.

d3.selectAll('.btn').on('click', function(d) {
        let selectedone = d3.select(this).attr('class').split(' ')[1]
        console.log(selectedone);

        d3.select(`.radar${selectedone}`)
            .transition()
            .duration(1000)
            .attrTween('d', shrinkRadar(0))

        function shrinkRadar(target) {

            return function(d) {
                console.log(d)
                let interpolate = d3.interpolate(rscale(d.value), target)

                return function(t) {
                    console.log(d.radius);
                    d.radius = interpolate(t)
                    return radarLine(d)
                }
            }
        }

        // .attrTween('d',shrinkRadar(finalvalue))
    })

Full code in the following link

https://codepen.io/jotnajoa/pen/dypJzmZ


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
307 views
Welcome To Ask or Share your Answers For Others

1 Answer

Your radar chart's line functions take an array of data to output a path (line and area fill). You are attempting, though, to operate the attrTween like it's manipulating a single piece of data and not an array. Take a look at this quick re-write:

function shrinkRadar(target) {
    return function(d) {    
        // create an array of interpolators
        // one for each point in the line
        // that run the value from starting to 0
        var interps = d.map( da => d3.interpolate(da.value, target));
        return function(t) {
            // for each point call it's interpolator
            // and re-assign the value
            d.forEach( (da,i) => {
              da.value = interps[i](t);
            });
            // regenerate path with new value
            return radarLine(d)
        }
    }
}

Running code:

let margin = {
  top: 100,
  bottom: 100,
  left: 100,
  right: 100
}

let width = Math.min(700, window.innerWidth - 10) - margin.left - margin.right;
let height = Math.min(width, window.innerHeight - margin.top - margin.bottom - 20);


var data = [
  [ //iPhone
    {
      axis: "Battery Life",
      value: 0.22
    },
    {
      axis: "Brand",
      value: 0.28
    },
    {
      axis: "Contract Cost",
      value: 0.29
    },
    {
      axis: "Design And Quality",
      value: 0.17
    },
    {
      axis: "Have Internet Connectivity",
      value: 0.22
    },
    {
      axis: "Large Screen",
      value: 0.02
    },
    {
      axis: "Price Of Device",
      value: 0.21
    },
    {
      axis: "To Be A Smartphone",
      value: 0.50
    }
  ],
  [ //Samsung
    {
      axis: "Battery Life",
      value: 0.27
    },
    {
      axis: "Brand",
      value: 0.16
    },
    {
      axis: "Contract Cost",
      value: 0.35
    },
    {
      axis: "Design And Quality",
      value: 0.13
    },
    {
      axis: "Have Internet Connectivity",
      value: 0.20
    },
    {
      axis: "Large Screen",
      value: 0.13
    },
    {
      axis: "Price Of Device",
      value: 0.35
    },
    {
      axis: "To Be A Smartphone",
      value: 0.38
    }
  ],
  [ //Nokia Smartphone
    {
      axis: "Battery Life",
      value: 0.26
    },
    {
      axis: "Brand",
      value: 0.10
    },
    {
      axis: "Contract Cost",
      value: 0.30
    },
    {
      axis: "Design And Quality",
      value: 0.14
    },
    {
      axis: "Have Internet Connectivity",
      value: 0.22
    },
    {
      axis: "Large Screen",
      value: 0.04
    },
    {
      axis: "Price Of Device",
      value: 0.41
    },
    {
      axis: "To Be A Smartphone",
      value: 0.30
    }
  ]
];

var color = d3.scaleOrdinal()
  .range(['#6678D9', '#61F06C', '#FF36AA'])

var radarChartOptions = {
  w: width,
  h: height,
  margin: margin,
  maxValue: 0.5,
  levels: 5,
  roundStrokes: true,
  color: color
};

//   blue, green pink inorder


RadarChart('.radarChart', data, radarChartOptions)

function RadarChart(id, data, options) {

  let cfg = {
    w: 600,
    h: 600,
    margin: {
      top: 20,
      right: 20,
      bottom: 20,
      left: 20
    },
    levels: 3,
    maxValue: 0,
    labelFactor: 1.25,
    wrapWidth: 60,
    opacityArea: 0.35,
    dotRadius: 4,
    opacityCircles: 0.1,
    strokeWidth: 2,
    roundStroke: false,
    color: ['#6678D9', '#61F06C', '#FF36AA']
  }

  if ('undefined' !== typeof options) {
    for (var i in options) {
      if ('undefined' !== typeof options[i]) {
        cfg[i] = options[i];
      }
    } //for i
  } //if

  //   ????? options? ????? ??? ????? ??? ??????
  var maxValue =

    // Math.max(cfg.maxValue,

    d3.max(data,
      (i) => d3.max(
        i.map((o) => o.value)
        // ??? ???? ???? ???, ? ????? ???? ?????.
      )
    )
  // )


  var allAxis = data[0].map((d) => {
    return d.axis
  })

  var total = allAxis.length,
    radius = Math.min(cfg.w / 2, cfg.h / 2),
    Format = d3.format('%'),
    angleSlice = Math.PI * 2 / total

  var rscale = d3.scaleLinear()
    .range([0, radius])
    .domain([0, maxValue])

  d3.select(id).select('svg').remove()

  var svg = d3.select(id).append('svg')
    .attr('width', cfg.w + cfg.margin.left + cfg.margin.right)
    .attr('height', cfg.h + cfg.margin.top + cfg.margin.bottom)
    .attr('class',
      'radar' + id);

  var g = svg.append('g').attr('transform', `translate(${cfg.w/2+cfg.margin.left},${cfg.h/2+cfg.margin.top})`)

  var filter = g.append('defs').append('filter').attr('id', 'glow')
  var feGaussianBlur = filter.append('feGaussianBlur').attr('stdDeviation', '2.5')
    .attr('result', 'coloredBlur')
  var feMerge = filter.append('feMerge')
  var feMergeNode1 = feMerge.append('feMergeNode').attr('in', 'coloredBlur')
  var feMergeNode2 = feMerge.append('feMergeNode').attr('in', 'SourceGraphic')

  var axisGrid = g.append('g').attr('class', 'axisWrapper')

  axisGrid.selectAll('.levels').data(
      d3.range(1, (cfg.levels + 1))
    )
    .join('circle')
    .attr('class', 'gridCircle')
    .attr('r', (d) => {
      return radius / cfg.levels * d
    })
    .style('fill', '#CDCDCD')
    .style('stroke', '#CDCDCD')
    .style('fill-opacity', cfg.opacityCircles)
    .style('filter', 'url(#glow)')

  axisGrid.selectAll('.axisLabel')
    .data(d3.range(1, cfg.levels + 1))
    .join('text')
    .attr('class', 'axisLabel')
    .attr('x', 4)
    .attr('y', (d) => {
      return -d * radius / cfg.levels
    })
    .attr('dy', '0.4em')
    .style('font-size', '10px')
    .attr('fill', '#737373')
    .text((d, i) => {
      return Format(maxValue * d / cfg.levels)
    })

  var axis = axisGrid.selectAll('.axis')
    .data(allAxis)
    .join('g')
    .attr('class', 'axis')

  axis.append('line')
    .attr('x1', 0)
    .attr('y1', 0)
    .attr("x2", function(d, i) {
      return rscale(maxValue * 1.1) * Math.cos(angleSlice * i - Math.PI / 2);
    })
    .attr("y2", function(d, i) {
      return rscale(maxValue * 1.1) * Math.sin(angleSlice * i - Math.PI / 2);
    })
    .attr("class", "line")
    .style("stroke", "white")
    .style("stroke-width", "2px");

  axis.append("text")
    .attr("class", "legend")
    .style("font-size", "11px")
    .attr("text-anchor", "middle")
    .attr("dy", "0.35em")
    .attr("x", function(d, i) {
      return rscale(maxValue * cfg.labelFactor) * Math.cos(angleSlice * i - Math.PI / 2);
    })
    .attr("y", function(d, i) {
      return rscale(maxValue * cfg.labelFactor) * Math.sin(angleSlice * i - Math.PI / 2);
    })
    .text(function(d) {
      return d
    })
    .call(wrap, cfg.wrapWidth)
  // .call(testfunc, 'testString')

  var radarLine = d3.radialLine()
    .radius(function(d) {

      return rscale(d.value)
    })
    .angle(function(d, i, t) {

      return i * angleSlice
    })
    .curve(d3.curveCardinalClosed)

  // if (cfg.roundStrokes) {
  //     radarLine.interpolate("cardinal-closed");
  // }

  const blobWrapper = g.selectAll(".radarWrapper")
    .data(data)
    .join('g')
    .attr("class", "radarWrapper");

  blobWrapper
    .append("path")
    .attr("class", "radarArea")
    .attr("d", function(d) {

      return radarLine(d)

    })
    .attr('class', function(d, i) {
      return `radar${i}`
    })
    .style("fill", (d, i) => cfg.color(i))
    .style("fill-opacity", cfg.opacityArea)
    .on('mouseover', function(d, i) {
      d3.selectAll('.radarArea').transition().duration(200)
        .style('fill-opacity', 0.1)
      d3.select(this).transition().duration(200).style('fill-opacity', cfg.opacityArea);
    })
    .on('mouseout', function() {
      //Bring back all blobs
      d3.selectAll(".radarArea")
        .transition().duration(200)
        .style("fill-opacity", cfg.opacityArea);
    });


  blobWrapper.append('path')
    .attr('class', 'radarStroke')
    .attr('d', radarLine)
    .attr('class', function(d, i) {
      return `radarStroke${i}`
    })
    .style('stroke', (d, i) => cfg.color(i))
    .style('stroke-width', cfg.strokeWidth + 'px')
    .style('fill', 'none')
    .style('filter', 'url(#glow)')

  let round = 0;

  blobWrapper.selectAll(".radarCircle")
    .data(data)
    .data(function(d) {
      return d
    })
    .join('circle')
    .attr("class", "radarCircle")
    .attr("r", cfg.dotRadius)
    .attr("cx", function(d, i) {
      return rscale(d.value) * Math.cos(angleSlice * i - Math.PI / 2);
    })
    .attr("cy", function(d, i) {
      return rscale(d.value) * Math.sin(angleSlice * i - Math.PI / 2);
    })
    .style("fill", function(d, i, j) {
      if (i == 0) {
        round = round + 1;;
      }
      return cfg.color(round - 1);
    })
    .style("fill-opacity", 0.8);

  var blobCircleWrapper = g.selectAll('.radarCircleWrapper')
    .data(data)
    .join('g')
    .attr('class', 'radarCircleWrapper')

  blobCircleWrapper.selectAll('circles').data(function(d) {
      return d
    })
    .join('circle').attr('class', 'invisibleCircles')
    .attr('r', cfg.dotRadius * 1.5)
    .attr("cx", function(d, i) {
      return rscale(d.value) * Math.cos(angleSlice * i - Math.PI / 2);
    })
    .attr("cy", function(d, i) {
      return rscale(d.value) * Math.sin(angleSlice * i - Math.PI / 2);
    })
    .style('fill', 'none')
    .style('pointer-events', 'all')
    .on('mouseover', function(d, i) {
      newX = parseFloat(d3.select(this).attr('cx')) - 10;
      newY = parseFloat(d3.select(this).attr('cy')) - 10;

      tooltip
        .attr('x', newX)
        .attr('y', newY)
        .text(Format(d.value))
        .transition().duration(200)
        .style('opacity', 1);


    })
    .on("mouseout", function() {
      tooltip.transition().duration(200)
        .style("opacity", 0);
    });

  var tooltip = g.append("text")
    .attr("class", "tooltip")
    .style("opacity", 0);

  d3.selectAll('.btn').on('click', function(d) {
    let selectedone = d3.select(this).attr('class').split(' ')[1]
    console.log(selectedone);

    d3.select(`.radar${selectedone}`)
      .transition()
      .duration(1000)
      .attrTween('d', shrinkRadar(0))

    function shrinkRadar(target) {

      return function(d) {
        var interps = d.map(da => d3.interpolate(da.value, target));
        return function(t) {
          d.forEach((da, i) => {
            da.value = interps[i](t);
          });
          return radarLine(d)
        }
      }
    }

    // .attrTween('d',shrinkRadar(finalvalue))
  })

}

function wrap(text, width) {

  text.each(function() {
    var text = d3.select(this),
      words = text.text().split(/s+/).reverse(),
      word,
      line = [],
      lineNumber = 0,
      lineHeight = 1.4, // ems
      y = text.attr("y"),
      x = text.attr("x"),
      dy = parseFloat(text.attr("dy")),
      tspan = text.text(null).append("tspan").attr("x", x).attr("y", y).attr("dy", dy + "em");

    while (word = words.pop()) {
      line.push(word);
      tspan.text(line.join(" "));
      if (tspan.node().getComputedTextLength() > width) {
        line.pop();
        tspan.text(line.join(" "));
        line = [word];
        tspan = text.append("tspan").attr("x", x).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
      }
    }
  });
}
.radarChart {
    wid

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share

548k questions

547k answers

4 comments

86.3k users

...