简体   繁体   中英

Using ArcTween to animate arcs in d3.js

Currently trying to animate the drawing of a PieChart in d3.js and I'm currently having difficulty with the ArcTween function, heres my code:

   <!doctype HTML>
    <html>
        <head>
            <title>Page Title</title>
            <meta charset="UTF-8">

            <script type="text/javascript" src="js/d3.min.js"></script>
            <link rel="stylesheet" type="text/css" href="css/style.css">

        </head>
        <body>

            <script type="text/javascript">

     //=========================================================================================================================================
     // initializing variables 


                var data = []; // empty array to hold the objects imported from the JSON file
                var oRadius = 300; //var holding value for the outer radius of the arc
                var iRadius = 80;  //var holding the value for the inner radius of the arc
                var cRadius = 3;   //var holding the value for the corner radius of the arc
                var colors = d3.scale.category20b();//built in D3 function to color pieces of data
                var width = 1400; //setting the width of the svg
                var height = 1000; //setting the height of the svg
                var dRadius = 5; //setting the radius for the dots
                var sColor = "white"; // color for the stroke of the arcs
                var dStrokeColor = "#666"; // stroke color for the dots
                var dFillColor  = "#ccc" // fill color for the dots
                var fontSize = 25; // font size value for the text labels
                var numFont = 15; // font size for the number labels
                var linePadding = 160; // value to translate the text and number labels to the end of each polyLine




                var myArcMaker= d3.svg.arc().outerRadius(oRadius).innerRadius(iRadius).cornerRadius(cRadius); //var that returns the values needed to create the arcs of the pie chart            
                var bigArcMaker=  d3.svg.arc().outerRadius(400).innerRadius(oRadius).cornerRadius(cRadius); //var that returns the values needed to create the arcs of the big pie chart used to draw the polylines            

                var  mySvg =  d3.select('body') //select the html body in the DOM
                              .append('svg') // place an empty svg in the body
                              .attr('width', width) //give the svg this width
                              .attr("height", height)//give the svg this height
                              .append("g")//append a group to the svg
                              .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")// centers the pie chart in the center of the svg this will make sure everything in the group is centered 


                               mySvg.append("g") //creates a group
                               .attr("class", "slices"); // give it a class of slices, used for the pie chart arcs
                               mySvg.append("g") // creates a group
                               .attr("class", "dots"); // give it a class of dots, used for the circles in the centre of the arcs
                                mySvg.append("g")//creates a group
                               .attr("class", "lines");// give it a class of lines, used for the big pie chart surrounding the main piechart
                                mySvg.append("g")//creates a group
                               .attr("class", "polyLines");//gives it a class of polylines, used for the polylines protruding it from the pie chart arcs
                                mySvg.append("g")//creates a group
                               .attr("class", "labels");//gives a class of labels, used for the text labels at the end of the polylines
                                 mySvg.append("g")//creates a group
                               .attr("class", "numlabels");//gives it a class of numlabels, used for the number labels at the end of the polylines




                var myPie =  d3.layout.pie()
                             .sort(null) //removes any d3 sorting 
                            .startAngle((Math.PI)/360) // setting the start angle for the arcs
                            .endAngle((2*(Math.PI))) // setting the end angle for the arcs
                            .padAngle(2*(2*(Math.PI))/360).value(function(d){return d.value}); //setting the values for that start angle, end angle and pad angle for the arcs and takes in the the values from the objects in the data array

    //======================================================================================================================================================


                     d3.json("data.json", function (json) // importing the json file
                    {

                        data = json; // setting the empty data array equal to the values of the objects in the json file
                        visual(); // this function holds all the d3 code to create the arc

                    })



    //======================================================================================================================================================

                function visual() // this function prevents the code that creates the arc from running before the objects from the json file are added into the empty data array
                {

                  //  console.log(data); // checking to see if the objects are loaded into the data ray using the console in chrome








                    var slice = mySvg.select("g.slices") //selecting the class slices
                      .selectAll("path.slice")//selecting all paths in the slice class
                      .data(myPie(data))//binds the data to the DOM elements 
                      .enter()//begin looping through the data
                      .append("path")//append a path
                      .attr("class", "slice")//give it a class of slice
                      .attr("d", function(d) {//runs the data in the dataset through the arcmaker function
                        return myArcMaker(d)
                      })
                      .attr("fill", function(d, i) {
                        return colors(i);
                      }) //using the d3 color brewer to color each arc
                      .each(function(d) { this._current = d; })
                      .attr("stroke", "white") //giving each arc a stroke of white
                      .style('stroke-width',2) //gives the stroke a width of 2px
                      .each(function(d) { this._current = d; })
                      .transition().duration(750).attrTween("d", arcTween); // redraw the arcs




                    var dots = mySvg.select("g.dots") //select the group dots
                      .selectAll("cirlces") //select all circles in the dots group
                      .data(myPie(data))//binds the data to the DOM elements
                      .enter()//begin the loop
                      .append("circle")//draw a circle
                      .attr("class", "g.dots")//gives it a class of dots
                      .attr("transform", function(d)//transform each circle to the centre of each arc using the .centroid method
                      {
                        return "translate(" + myArcMaker.centroid(d) + ")"; 
                      })
                      .attr("r", dRadius)//setting the radius of the circle
                      .attr("fill", dFillColor)//setting the fill color
                      .attr("stroke", sColor)//setting the stroke color
    //                
                    var lines = mySvg.select(".lines") //select the lines class                         *--------------------------------------*
                      .selectAll("path.lines")//select all paths in the lines class                     * This section here is to draw         *
                      .data(myPie(data)) //binds the data to the DOM elements                           * the invisible piechart that          * 
                      .enter()//begin the loop                                                          * surrounds the smaller piechart this  * 
                      .append("path")//draw a path                                                      * used for the polylines later on      *
                      .attr("class", "lines")//give it a class of lines                                 *--------------------------------------*
                      .attr("d", function(d) {//set the d value to the values returned from the bigArcMaker function
                        return bigArcMaker(d)
                      }).attr("fill", "none")//give the fill a value of none
                      .attr("stroke", "none")//give the stroke a value of none


    //                var outerDots =  mySvg.select("g.dots")//select the dots group
    //                  .selectAll("cirlces")//select all circles in the dots group
    //                  .data(myPie(data))//binds the data to the DOM elements
    //                  .enter()
    //                  .append("circle")//create a circle
    //                  .attr("class", "g.dots")//give it a class of dots
    //                  .attr("transform", function(d)//translate it to the centroid of the bigArc surrounding the main pie chart
    //                  {
    //                    return "translate(" + bigArcMaker.centroid(d) + ")"; 
    //                  })
    //                  .attr("r", dRadius)          
    //                  .attr("fill", dFillColor)
    //                  .attr("stroke", sColor)






    //                    var x1 = myArcMaker.centroid(d)[0];
    //                    var y1 = myArcMaker.centroid(d)[1];
    //                    var x2 = bigArcMaker.centroid(d)[0];
    //                    var y2 = bigArcMaker.centroid(d)[1];
    //                    var x3 = function(d){if(x2<0){return bigArcMaker.centroid(d)[0]-160}}

    //                    var lineData = [{'x': x1},
    //                                   ]


                        var polyLines = mySvg.select(".polyLines")//select the class of polylines
                        .selectAll("polylines")//sellect all polylines in the class of polylines
                        .data(myPie(data))//binds the data to the DOM elements
                        .enter()
                        .append("polyline")//create a polyline
                        .attr("class", "polyLines")//give it a class of polylines
                        .attr("points", function(d) {//set the points for the polylines
                          var p = " "; //used because i was getting errors with my strings also makes this the conditional statement more readable
                          p += myArcMaker.centroid(d)[0] + ',' + myArcMaker.centroid(d)[1] + ',' + bigArcMaker.centroid(d)[0] + ',' + bigArcMaker.centroid(d)[1] + ',';
                          p += bigArcMaker.centroid(d)[0] < 0 ? bigArcMaker.centroid(d)[0] - linePadding : bigArcMaker.centroid(d)[0] + linePadding; //if the x value for the bigArc maker is less than zero - linePadding, if its greater, add linePading
                          p +=  ',' + bigArcMaker.centroid(d)[1];
                          return p;

                        })
                       .attr("fill", "none")//gives the fill value none
                       .attr("stroke", sColor) //gives the stroke a color of sColor
                       .style('stroke-width',2)//gives the stroke-width of 2 pixels



                        var labels = mySvg.select(".labels")//select class labels
                                    .selectAll("text")//select all text
                                    .data(myPie(data))//binds the data to the DOM elements
                                    .enter()
                                    .append("text")//create a text
                                    .text(function(d) //setting the text value to the Fruits label in the dataset
                                    {
                                    return d.data.Fruits;
                                    })
                                    .style("fill", sColor)//sets the fill to the sColor attribute
                                    .attr("x", function(d)//conditional statement that determines where the text will be placed, similar to the polylines
                                        {
                                        if(bigArcMaker.centroid(d)[0]>0)
                                        {
                                         return bigArcMaker.centroid(d)[0]+linePadding;
                                        }
                                        else
                                        {
                                         return bigArcMaker.centroid(d)[0]-linePadding;
                                        }

                                          })
                                        .attr("y", function(d){return bigArcMaker.centroid(d)[1]})//set the why value to the y value of the centroid array
                                        .attr("text-anchor", function(d){
                                              if(bigArcMaker.centroid(d)>0)//conditional statement that sets the text anchor for each piece of text, similar to polylines 

                                            {
                                                return "start"
                                            }
                                              else{
                                                  return "end"
                                              }


                                            })
                                            .attr("font-family", "Helvetica") // sets the font family
                                            .style("font-size", fontSize)//sets the font size




                        var numLabels = mySvg.select(".numlabels") //select the class numlabels
                                    .selectAll("text")//selects all text in the class numlabels
                                    .data(myPie(data))//binds the data to the DOM elements
                                    .enter()
                                    .append("text")//append a text
                                    .text(function(d) //sets the value of the text to the value attribute in the dataset array
                                    {
                                    return d.data.value;
                                    })
                                    .style("fill", sColor)//sets the fill color
                                    .attr("x", function(d)//conditional statement to set the x value of the text similar to the polylines and labels
                                        {
                                        if(bigArcMaker.centroid(d)[0]>0)
                                        {
                                         return bigArcMaker.centroid(d)[0]+linePadding;
                                        }
                                        else
                                        {
                                         return bigArcMaker.centroid(d)[0]-linePadding;
                                        }

                                          })
                                        .attr("y", function(d){return bigArcMaker.centroid(d)[1]})//set the why value to the y value of the centroid array
                                         .attr("text-anchor", function(d){
                                              if(bigArcMaker.centroid(d)>0)//conditional statement that sets the text anchor for each piece of text, similar to polylines 

                                            {
                                                return "start"
                                            }
                                              else{
                                                  return "end"
                                              }


                                            })
                                        .attr("dy", 20)
                                        .attr("font-family", "Helvetica") // sets the font family
                                        .style("font-size", numFont)



                     function arcTween(a)
                    {
                      var i = d3.interpolate(this._current, a);
                      this._current = i(0);
                      return function(t)
                      {
                       return myArcMaker(i(t));
                     };
                    }
                }



            </script>
        </body>
    </html>

I want it to start from the start angle and draw to the end angle, ive tried using d.startAngle and d.endAngle but when I inspect it in chrome I get an uncaught reference error because d is not defined. Currently the code is compiling and I'm getting no errors when I inspect it, I'm not quite sure how this works so an explination would be great

heres my JSFiddle: http://jsfiddle.net/JamieHyland1/gyekfnr2/

You are close, your tween function is a little off. Also, you can scrap all the this._current stuff and just make a closure in the tween function itself.

  function arcTween(a) { //<-- a is the datum bound to each arc
    var startAngle = a.startAngle; //<-- keep reference to start angle
    var i = d3.interpolate(a.startAngle, a.endAngle); //<-- interpolate start to end
    return function(t) {
      return myArcMaker({ //<-- return arc at each iteration from start to interpolate end
        startAngle: startAngle,
        endAngle: i(t)
      });
    };
  }

You call this as:

var slice = mySvg.select("g.slices") //selecting the class slices
    .selectAll("path.slice") //selecting all paths in the slice class
    .data(myPie(data)) //binds the data to the DOM elements 
    .enter() //begin looping through the data
    .append("path") //append a path
    .attr("class", "slice") //give it a class of slice
    .attr("fill", function(d, i) {
      return colors(i);
    }) //using the d3 color brewer to color each arc
    .attr("stroke", "white") //giving each arc a stroke of white
    .style('stroke-width', 2) //gives the stroke a width of 2px
    .transition().duration(750).attrTween("d", arcTween); // redraw the arcs

Full code:

 <!DOCTYPE html> <html> <head> <title>Page Title</title> <meta charset="UTF-8" /> <script data-require="d3@3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script> </head> <body> <script type="text/javascript"> //========================================================================================================================================= // initializing variables var data = []; // empty array to hold the objects imported from the JSON file var oRadius = 300; //var holding value for the outer radius of the arc var iRadius = 80; //var holding the value for the inner radius of the arc var cRadius = 3; //var holding the value for the corner radius of the arc var colors = d3.scale.category20b(); //built in D3 function to color pieces of data var width = 1400; //setting the width of the svg var height = 1000; //setting the height of the svg var dRadius = 5; //setting the radius for the dots var sColor = "white"; // color for the stroke of the arcs var dStrokeColor = "#666"; // stroke color for the dots var dFillColor = "#ccc" // fill color for the dots var fontSize = 25; // font size value for the text labels var numFont = 15; // font size for the number labels var linePadding = 160; // value to translate the text and number labels to the end of each polyLine var myArcMaker = d3.svg.arc().outerRadius(oRadius).innerRadius(iRadius).cornerRadius(cRadius); //var that returns the values needed to create the arcs of the pie chart var bigArcMaker = d3.svg.arc().outerRadius(400).innerRadius(oRadius).cornerRadius(cRadius); //var that returns the values needed to create the arcs of the big pie chart used to draw the polylines var mySvg = d3.select('body') //select the html body in the DOM .append('svg') // place an empty svg in the body .attr('width', width) //give the svg this width .attr("height", height) //give the svg this height .append("g") //append a group to the svg .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")") // centers the pie chart in the center of the svg this will make sure everything in the group is centered mySvg.append("g") //creates a group .attr("class", "slices"); // give it a class of slices, used for the pie chart arcs mySvg.append("g") // creates a group .attr("class", "dots"); // give it a class of dots, used for the circles in the centre of the arcs mySvg.append("g") //creates a group .attr("class", "lines"); // give it a class of lines, used for the big pie chart surrounding the main piechart mySvg.append("g") //creates a group .attr("class", "polyLines"); //gives it a class of polylines, used for the polylines protruding it from the pie chart arcs mySvg.append("g") //creates a group .attr("class", "labels"); //gives a class of labels, used for the text labels at the end of the polylines mySvg.append("g") //creates a group .attr("class", "numlabels"); //gives it a class of numlabels, used for the number labels at the end of the polylines var myPie = d3.layout.pie() .sort(null) //removes any d3 sorting .startAngle((Math.PI) / 360) // setting the start angle for the arcs .endAngle((2 * (Math.PI))) // setting the end angle for the arcs .padAngle(2 * (2 * (Math.PI)) / 360).value(function(d) { return d.value }); //setting the values for that start angle, end angle and pad angle for the arcs and takes in the the values from the objects in the data array //====================================================================================================================================================== // d3.json("data.json", function (json) // importing the json file // { data = [{ value: 10 }, { value: 20 }, { value: 30 }]; visual(); // this function holds all the d3 code to create the arc // }) //====================================================================================================================================================== function visual() // this function prevents the code that creates the arc from running before the objects from the json file are added into the empty data array { // console.log(data); // checking to see if the objects are loaded into the data ray using the console in chrome var slice = mySvg.select("g.slices") //selecting the class slices .selectAll("path.slice") //selecting all paths in the slice class .data(myPie(data)) //binds the data to the DOM elements .enter() //begin looping through the data .append("path") //append a path .attr("class", "slice") //give it a class of slice .attr("fill", function(d, i) { return colors(i); }) //using the d3 color brewer to color each arc .attr("stroke", "white") //giving each arc a stroke of white .style('stroke-width', 2) //gives the stroke a width of 2px .transition().duration(750).attrTween("d", arcTween); // redraw the arcs var dots = mySvg.select("g.dots") //select the group dots .selectAll("cirlces") //select all circles in the dots group .data(myPie(data)) //binds the data to the DOM elements .enter() //begin the loop .append("circle") //draw a circle .attr("class", "g.dots") //gives it a class of dots .attr("transform", function(d) //transform each circle to the centre of each arc using the .centroid method { return "translate(" + myArcMaker.centroid(d) + ")"; }) .attr("r", dRadius) //setting the radius of the circle .attr("fill", dFillColor) //setting the fill color .attr("stroke", sColor) //setting the stroke color // var lines = mySvg.select(".lines") //select the lines class *--------------------------------------* .selectAll("path.lines") //select all paths in the lines class * This section here is to draw * .data(myPie(data)) //binds the data to the DOM elements * the invisible piechart that * .enter() //begin the loop * surrounds the smaller piechart this * .append("path") //draw a path * used for the polylines later on * .attr("class", "lines") //give it a class of lines *--------------------------------------* .attr("d", function(d) { //set the d value to the values returned from the bigArcMaker function return bigArcMaker(d) }).attr("fill", "none") //give the fill a value of none .attr("stroke", "none") //give the stroke a value of none // var outerDots = mySvg.select("g.dots")//select the dots group // .selectAll("cirlces")//select all circles in the dots group // .data(myPie(data))//binds the data to the DOM elements // .enter() // .append("circle")//create a circle // .attr("class", "g.dots")//give it a class of dots // .attr("transform", function(d)//translate it to the centroid of the bigArc surrounding the main pie chart // { // return "translate(" + bigArcMaker.centroid(d) + ")"; // }) // .attr("r", dRadius) // .attr("fill", dFillColor) // .attr("stroke", sColor) // var x1 = myArcMaker.centroid(d)[0]; // var y1 = myArcMaker.centroid(d)[1]; // var x2 = bigArcMaker.centroid(d)[0]; // var y2 = bigArcMaker.centroid(d)[1]; // var x3 = function(d){if(x2<0){return bigArcMaker.centroid(d)[0]-160}} // var lineData = [{'x': x1}, // ] var polyLines = mySvg.select(".polyLines") //select the class of polylines .selectAll("polylines") //sellect all polylines in the class of polylines .data(myPie(data)) //binds the data to the DOM elements .enter() .append("polyline") //create a polyline .attr("class", "polyLines") //give it a class of polylines .attr("points", function(d) { //set the points for the polylines var p = " "; //used because i was getting errors with my strings also makes this the conditional statement more readable p += myArcMaker.centroid(d)[0] + ',' + myArcMaker.centroid(d)[1] + ',' + bigArcMaker.centroid(d)[0] + ',' + bigArcMaker.centroid(d)[1] + ','; p += bigArcMaker.centroid(d)[0] < 0 ? bigArcMaker.centroid(d)[0] - linePadding : bigArcMaker.centroid(d)[0] + linePadding; //if the x value for the bigArc maker is less than zero - linePadding, if its greater, add linePading p += ',' + bigArcMaker.centroid(d)[1]; return p; }) .attr("fill", "none") //gives the fill value none .attr("stroke", sColor) //gives the stroke a color of sColor .style('stroke-width', 2) //gives the stroke-width of 2 pixels var labels = mySvg.select(".labels") //select class labels .selectAll("text") //select all text .data(myPie(data)) //binds the data to the DOM elements .enter() .append("text") //create a text .text(function(d) //setting the text value to the Fruits label in the dataset { return d.data.Fruits; }) .style("fill", sColor) //sets the fill to the sColor attribute .attr("x", function(d) //conditional statement that determines where the text will be placed, similar to the polylines { if (bigArcMaker.centroid(d)[0] > 0) { return bigArcMaker.centroid(d)[0] + linePadding; } else { return bigArcMaker.centroid(d)[0] - linePadding; } }) .attr("y", function(d) { return bigArcMaker.centroid(d)[1] }) //set the why value to the y value of the centroid array .attr("text-anchor", function(d) { if (bigArcMaker.centroid(d) > 0) //conditional statement that sets the text anchor for each piece of text, similar to polylines { return "start" } else { return "end" } }) .attr("font-family", "Helvetica") // sets the font family .style("font-size", fontSize) //sets the font size var numLabels = mySvg.select(".numlabels") //select the class numlabels .selectAll("text") //selects all text in the class numlabels .data(myPie(data)) //binds the data to the DOM elements .enter() .append("text") //append a text .text(function(d) //sets the value of the text to the value attribute in the dataset array { return d.data.value; }) .style("fill", sColor) //sets the fill color .attr("x", function(d) //conditional statement to set the x value of the text similar to the polylines and labels { if (bigArcMaker.centroid(d)[0] > 0) { return bigArcMaker.centroid(d)[0] + linePadding; } else { return bigArcMaker.centroid(d)[0] - linePadding; } }) .attr("y", function(d) { return bigArcMaker.centroid(d)[1] }) //set the why value to the y value of the centroid array .attr("text-anchor", function(d) { if (bigArcMaker.centroid(d) > 0) //conditional statement that sets the text anchor for each piece of text, similar to polylines { return "start" } else { return "end" } }) .attr("dy", 20) .attr("font-family", "Helvetica") // sets the font family .style("font-size", numFont) function arcTween(a) { var startAngle = a.startAngle; var i = d3.interpolate(a.startAngle, a.endAngle); return function(t) { return myArcMaker({ startAngle: startAngle, endAngle: i(t) }); }; } } </script> </body> </html> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM