Javascript 使用画布创建极坐标图

Javascript 使用画布创建极坐标图,javascript,canvas,graph,charts,html5-canvas,Javascript,Canvas,Graph,Charts,Html5 Canvas,我尝试在此处使用画布创建极坐标图: 代码: var myColor = ["#ff0", "#00f", "#002", "#003", "#004"]; var myData = [10, 30, 20, 60, 40]; var myRadius = [120, 80, 40, 70, 40]; function getTotal() { var myTotal = 0; for (var j = 0; j < myData.length; j++) {

我尝试在此处使用画布创建极坐标图:

代码:

var myColor = ["#ff0", "#00f", "#002", "#003", "#004"];
var myData = [10, 30, 20, 60, 40];
var myRadius = [120, 80, 40, 70, 40];

function getTotal() {
    var myTotal = 0;
    for (var j = 0; j < myData.length; j++) {
        myTotal += (typeof myData[j] == 'number') ? myData[j] : 0;
    }
    return myTotal;
}

function plotData() {
    var canvas;
    var ctx;
    var lastend = 0;
    var myTotal = getTotal();

    canvas = document.getElementById("canvas");
    ctx = canvas.getContext("2d");
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    for (var i = 0; i < myData.length; i++) {
        ctx.fillStyle = myColor[i];
        ctx.beginPath();
        ctx.moveTo(200, 150);
        ctx.arc(200, 150, myRadius[i], lastend, lastend + (Math.PI * 2 * (myData[i] / myTotal)), false);
        console.log(myRadius[i]);
        ctx.lineTo(200, 150);
        ctx.fill();
        lastend += Math.PI * 2 * (myData[i] / myTotal);
    }
}

plotData();
var myColor=[“ff0”、“00f”、“002”、“003”、“004”];
var myData=[10,30,20,60,40];
var myRadius=[120,80,40,70,40];
函数getTotal(){
var-myTotal=0;
对于(var j=0;j
更新: 为了解决问题,我想实现以下目标:

此样式与此搭配:

(这是一个简单的饼图)
我无法使用当前实现实现第二部分(分解切片)。

您不应该更改半径“myRadius”的值,它必须是常量(简单数学)

var myColor=[“ff0”、“00f”、“002”、“003”、“004”];
var myData=[10,30,20,60,40];

var-myRadius=120//[120,80,40,70,40]; 为此,我将使用对象模型,并在图表和切片之间保持父子关系。通过这种方式,我可以只使用图表模型,让它渲染所有子对象,并且可以扩展切片对象来执行更强大的操作。我不支持这个例子中的文本,但是从这里添加应该很容易

好的,首先让我们构建一个父对象-图表本身:

function Chart(x, y) {        
  this.x = x;       // expose these values so we can alter them from outside
  this.y = y;       // as well as within the prototypes (see below)
  this.total = 0;
  this.slices = [];
}
它非常基本,还没有包含我们需要的所有功能。我们可以将函数直接构建到这个对象中,但是如果我们使用图表对象的多个实例,那么在每个实例之间共享内存空间会更明智,因此我们将使用原型模型

让我们首先创建一个函数来添加
切片
对象:

Chart.prototype.addSlice = function(data, radius, color, offset) {

  var slice = new Slice(data, radius, color, offset);

  this.slices.push(slice);  // add slice to internal array
  this.total += data;       // update total value

  return this;              // just to make call chain-able
};
在这里,我们看到它创建了一个
Slice
对象(见下文),然后将其添加到Slice数组中,更新总和并返回自身,以便我们可以链接它

切片
对象(子对象)在这里相当简单,但通过将其保留为对象而不是文字对象或数组,我们可以在以后使用强大的功能对其进行扩展,而不需要对父对象进行太多修改(您可以让父对象在每个片段本身上调用一个渲染向量来渲染自身,而不是在父对象中进行渲染)。此外,对象在现代浏览器中编译良好:

function Slice(data, radius, color, offset) {
  this.data = data;           // self-expl.
  this.radius = radius
  this.color = color;  
  this.offset = offset || 0;  // default to 0 if non is given
}
就是这样。我们支持一个偏移值(从中心开始),如果没有给定,默认为0

我们现在所需要做的就是拥有一个函数,该函数迭代每个切片,并以偏移、角度、颜色等方式将它们渲染到画布上

神奇发生在这里:

Chart.prototype.render = function() {

  var i = 0, s,             // iterator, slice object
      angle, angleHalf,     // angle based on data and total
      currentAngle = 0,     // current angle for render purpose
      pi2 = 2 * Math.PI;    // cache PI*2

  // iterate over each slice in the slice array (see addSlice())
  for(; s = this.slices[i++];) {
      angle = s.data / this.total * pi2; // calc. angle for this slice
      angleHalf = angle * .5;            // calc. half angle for center

      ctx.translate(this.x, this.y);     // move to pivot point
      ctx.rotate(currentAngle);          // rotate to accumulated angle

      // The "explosion" happens here...
      ctx.translate(s.offset * Math.cos(angleHalf),  // translate so slice
                    s.offset * Math.sin(angleHalf)); // explodes using center

      ctx.beginPath();             // draw slice (outer stroke not shown here)
      ctx.moveTo(0, 0);
      ctx.arc(0, 0, s.radius, 0, angle);
      ctx.fillStyle = s.color;
      ctx.fill();

      ctx.setTransform(1, 0, 0, 1, 0, 0);// reset all transforms
      currentAngle += angle;             // accumulate angle of slice
  }
};
就是这样。变换的顺序很重要:

  • 首先平移到旋转中心
  • 围绕那个中心旋转
  • 基于该旋转+半个角度的偏移平移(在本例中)
现在,我们可以通过以下方式创建图表和切片:

var myChart = new Chart(canvas.width * .5, canvas.height * .5);

// add some slices to the chart
myChart.addSlice(10, 120, '#ff0')
       .addSlice(30,  80, '#00f')
       .addSlice(20,  40, '#002')
       .addSlice(60,  70, '#003')
       .addSlice(40,  40, '#004');
对于每个加法,数据值累积为一个总值。该总值随后成为用于确定每个切片的角度大小的值:

angle = s.data / this.total * pi2; // calc. angle for this slice
在这里,我们首先得到了总数的百分比:

s.data / this.total
该百分比用于整圈(2 x PI):

因此,无论我们添加多少个切片,我们都会动态调整它们相对于彼此的角度和总角度

现在,只需拨打:

myChart.render();
来呈现这一切

要调整偏移,甚至设置偏移动画,我们可以在下面的实时代码中创建实用函数,或者直接为阵列中的每个切片设置偏移:

myChart.slices[sliceIndex].offset = value;
requestAnimationFrame
将其放入一个循环中,您可以使用各种偏移量对其进行动画制作,您需要担心的只是一维值(有人关心窦波爆炸吗?)

如何定义对象的参数和方法取决于您,但有了这些,您应该能够根据需要进行扩展和优化

希望这有帮助

//主对象(切片的父对象)
功能图(x,y){
这个.x=x;
这个。y=y;
这个总数=0;
this.slices=[];
}
//将共享函数添加到所有图表实例,以向其自身添加一个切片
Chart.prototype.addSlice=函数(数据、半径、颜色、偏移){
var切片=新切片(数据、半径、颜色、偏移);
这个。切片。推(切片);
此值为总+=数据;
归还这个;
};
//向所有图表实例共享函数以呈现自身
Chart.prototype.render=函数(){
var i=0,s,
角,角的一半,
currentAngle=0,
pi2=2*Math.PI;
ctx.lineWidth=7;
ctx.strokeStyle='#79f';
for(;s=this.slices[i++];){
角度=s.data/this.total*pi2;
角半=角*.5;
ctx.translate(this.x,this.y);
ctx.旋转(当前角度);
ctx.translate(s.offset*Math.cos(angleHalf),s.offset*Math.sin(angleHalf));
ctx.beginPath();
ctx.moveTo(0,0);
弧(0,0,s半径,0,角度);
ctx.fillStyle=s.color;
ctx.fill();
ctx.beginPath();
弧(0,0,s半径,0,角度);
ctx.stroke();
setTransform(1,0,0,1,0,0);
电流角度+=角度;
}
归还这个;
};
//向所有子切片添加偏移的实用方法。
//偏移量也可以添加到每个单独的切片中
Chart.prototype.addOffsetToAll=函数(偏移){
对于(var i=0,s;s=this.slices[i++];)s.offse
myChart.render();
myChart.slices[sliceIndex].offset = value;