圆点与区域形状不对齐

时间:2019-04-02 09:48:21

标签: javascript d3.js svg

我正在尝试用面积图形状拟合线上的点,但是不适合。我认为这是由于.curve使用了d3.curveBasis。

enter image description here

data = [
  {
    "login_date": "2017-09-24",
    "unique_user_count": 2,
    "total_login_count": 2
  },
  {
    "login_date": "2017-09-25",
    "unique_user_count": 25,
    "total_login_count": 46
  },
  {
    "login_date": "2017-09-26",
    "unique_user_count": 31,
    "total_login_count": 74
  },
  {
    "login_date": "2017-09-27",
    "unique_user_count": 29,
    "total_login_count": 58
  },
  {
    "login_date": "2017-09-28",
    "unique_user_count": 29,
    "total_login_count": 60
  },
  {
    "login_date": "2017-09-29",
    "unique_user_count": 31,
    "total_login_count": 71
  },
  {
    "login_date": "2017-09-30",
    "unique_user_count": 1,
    "total_login_count": 1
  },
  {
    "login_date": "2017-10-01",
    "unique_user_count": 1,
    "total_login_count": 1
  },
  {
    "login_date": "2017-10-02",
    "unique_user_count": 41,
    "total_login_count": 71
  },
  {
    "login_date": "2017-10-03",
    "unique_user_count": 30,
    "total_login_count": 67
  },
  {
    "login_date": "2017-10-04",
    "unique_user_count": 28,
    "total_login_count": 45
  },
  {
    "login_date": "2017-10-05",
    "unique_user_count": 32,
    "total_login_count": 48
  },
  {
    "login_date": "2017-10-06",
    "unique_user_count": 30,
    "total_login_count": 50
  },
  {
    "login_date": "2017-10-07",
    "unique_user_count": 1,
    "total_login_count": 1
  },
  {
    "login_date": "2017-10-08",
    "unique_user_count": 1,
    "total_login_count": 1
  },
  {
    "login_date": "2017-10-09",
    "unique_user_count": 35,
    "total_login_count": 76
  },
  {
    "login_date": "2017-10-10",
    "unique_user_count": 37,
    "total_login_count": 63
  },
  {
    "login_date": "2017-10-11",
    "unique_user_count": 41,
    "total_login_count": 76
  },
  {
    "login_date": "2017-10-12",
    "unique_user_count": 42,
    "total_login_count": 83
  },
  {
    "login_date": "2017-10-13",
    "unique_user_count": 41,
    "total_login_count": 68
  },
  {
    "login_date": "2017-10-15",
    "unique_user_count": 1,
    "total_login_count": 1
  },
  {
    "login_date": "2017-10-16",
    "unique_user_count": 48,
    "total_login_count": 84
  },
  {
    "login_date": "2017-10-17",
    "unique_user_count": 50,
    "total_login_count": 98
  },
  {
    "login_date": "2017-10-18",
    "unique_user_count": 38,
    "total_login_count": 56
  },
  {
    "login_date": "2017-10-19",
    "unique_user_count": 38,
    "total_login_count": 98
  },
  {
    "login_date": "2017-10-20",
    "unique_user_count": 40,
    "total_login_count": 71
  },
  {
    "login_date": "2017-10-22",
    "unique_user_count": 2,
    "total_login_count": 2
  }];
  
 //Define SVG container full width and height
const fullWidth = 600;
const fullHeight = 200;

//Define bar chart area  widht and height
const margin = {
    top: 10,
    bottom: 10,
    left: 20,
    right: 20
}

const chartWidth = fullWidth - margin.left - margin.right;
const chartHeight = fullHeight - margin.top - margin.bottom;

//Draw SVG container
let svg = d3.select('body')
    .append('svg')
    .attr('width', fullWidth)
    .attr('height', fullHeight);

//Define xand y scale range of the bar chart
const xScale = d3.scaleBand()
    .range([0, chartWidth]);

const yScale = d3.scaleLinear()
    .range([chartHeight, 0]);

const yScale2 = d3.scaleLinear()
    .range([chartHeight, 0]);

    console.log('Data received from an API:', data)

    //defiene x and y scale domain
    yScale
        .domain([0, d3.max(data, d => +d.total_login_count)]);

    yScale2
        .domain([0, d3.max(data, d => +d.unique_user_count)]);

    xScale
        .domain(data.map(d => d.login_date));

    //Generate total login area chart
    let area = d3.area()
        .curve(d3.curveBasis)
        .x(function (d) {
            return xScale(d.login_date);
        })
        .y0(fullHeight)
        .y1(function (d) {
            return yScale(+d.total_login_count)
        });
    //Generate unique user count area chart
    let area2 = d3.area()
        .curve(d3.curveBasis)
        .x(function (d) {
            return xScale(d.login_date);
        })
        .y0(fullHeight)
        .y1(function (d) {
            return yScale2(+d.unique_user_count)
        });

    //Draw bar chart
    let group = svg.selectAll('g')
        .data([data])
        .enter()
        .append('g');

    //Draw area for total login count
    group
        .append('path')
        .attr('class', 'area')
        .attr('d', area);

    //Draw area for unique user count 
    group
        .append('path')
        .attr('class', 'area2')
        .attr('d', area2);

    //Dot points
    let points = group.selectAll('circle')
        .data(data)
        .enter()
        .append('circle');

    //Dot points
    let points2 = group.select('circle')
        .data(data)
        .enter()
        .append('circle');

    points.attrs({
            "cx": d => xScale(d.login_date),
            "cy": d => yScale(+d.total_login_count) + 10,
            "r": 5
        })
        .style("opacity", 1)
        .style('fill', '#F9A2CB');

    points2.attrs({
            "cx": d => xScale(d.login_date),
            "cy": d => yScale2(+d.unique_user_count) + 5,
            "r": 5
        })
        .style("opacity", 1)
        .style('fill', '#8BDBCE');
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Bar chart</title>
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <script src="https://d3js.org/d3-fetch.v1.min.js"></script>
    <script src='https://d3js.org/d3-selection-multi.v0.4.min.js'></script>
</head>
<style>
    .line {
        fill: none;
        stroke: orange;
        stroke-width: 1px;
    }

    .area {
        fill: #F9A2CB;
        stroke: none;
        opacity: 0.6;
        }
    
    .area2 {
        fill: #8BDBCE;
        stroke: none;
        opacity: 0.6;
        }
</style>

<body>
    <script type="text/javascript" src="main.js"></script>
</body>

</html>

这是我的方法:

//define x and y scale domain
    yScale
        .domain([0, d3.max(data, d => +d.total_login_count)]);

    yScale2
        .domain([0, d3.max(data, d => +d.unique_user_count)]);

    xScale
        .domain(data.map(d => d.login_date));

    //Generate total login area chart
    let area = d3.area()
        .curve(d3.curveBasis)
        .x(function (d) {
            return xScale(d.login_date);
        })
        .y0(fullHeight)
        .y1(function (d) {
            return yScale(+d.total_login_count)
        });
    //Generate unique user count area chart
    let area2 = d3.area()
        .curve(d3.curveBasis)
        .x(function (d) {
            return xScale(d.login_date);
        })
        .y0(fullHeight)
        .y1(function (d) {
            return yScale2(+d.unique_user_count)
        });

    //Draw bar chart
    let group = svg.selectAll('g')
        .data([data])
        .enter()
        .append('g');

    //Draw area for total login count
    group
        .append('path')
        .attr('class', 'area')
        .attr('d', area);

    //Draw area for unique user count 
    group
        .append('path')
        .attr('class', 'area2')
        .attr('d', area2);

    //Dot points
    let points = group.selectAll('circle')
        .data(data)
        .enter()
        .append('circle');

    //Dot points
    let points2 = group.select('circle')
        .data(data)
        .enter()
        .append('circle');

    points.attrs({
            "cx": d => xScale(d.login_date),
            "cy": d => yScale(+d.total_login_count) + 10,
            "r": 5
        })
        .style("opacity", 1)
        .style('fill', '#F9A2CB');

    points2.attrs({
            "cx": d => xScale(d.login_date),
            "cy": d => yScale2(+d.unique_user_count) + 5,
            "r": 5
        })
        .style("opacity", 1)
        .style('fill', '#8BDBCE');

1 个答案:

答案 0 :(得分:2)

您是正确的,问题出在d3.curveBasis上。

如您所见,d3.curveBasis是一个内插器,其生成的路径不会完全越过控制点:

enter image description here

我的建议是使用插值器,该插值器生成一条经过控制点的路径,例如d3.curveCatmullRom

enter image description here

这是您所做的更改的代码:

data = [
  {
    "login_date": "2017-09-24",
    "unique_user_count": 2,
    "total_login_count": 2
  },
  {
    "login_date": "2017-09-25",
    "unique_user_count": 25,
    "total_login_count": 46
  },
  {
    "login_date": "2017-09-26",
    "unique_user_count": 31,
    "total_login_count": 74
  },
  {
    "login_date": "2017-09-27",
    "unique_user_count": 29,
    "total_login_count": 58
  },
  {
    "login_date": "2017-09-28",
    "unique_user_count": 29,
    "total_login_count": 60
  },
  {
    "login_date": "2017-09-29",
    "unique_user_count": 31,
    "total_login_count": 71
  },
  {
    "login_date": "2017-09-30",
    "unique_user_count": 1,
    "total_login_count": 1
  },
  {
    "login_date": "2017-10-01",
    "unique_user_count": 1,
    "total_login_count": 1
  },
  {
    "login_date": "2017-10-02",
    "unique_user_count": 41,
    "total_login_count": 71
  },
  {
    "login_date": "2017-10-03",
    "unique_user_count": 30,
    "total_login_count": 67
  },
  {
    "login_date": "2017-10-04",
    "unique_user_count": 28,
    "total_login_count": 45
  },
  {
    "login_date": "2017-10-05",
    "unique_user_count": 32,
    "total_login_count": 48
  },
  {
    "login_date": "2017-10-06",
    "unique_user_count": 30,
    "total_login_count": 50
  },
  {
    "login_date": "2017-10-07",
    "unique_user_count": 1,
    "total_login_count": 1
  },
  {
    "login_date": "2017-10-08",
    "unique_user_count": 1,
    "total_login_count": 1
  },
  {
    "login_date": "2017-10-09",
    "unique_user_count": 35,
    "total_login_count": 76
  },
  {
    "login_date": "2017-10-10",
    "unique_user_count": 37,
    "total_login_count": 63
  },
  {
    "login_date": "2017-10-11",
    "unique_user_count": 41,
    "total_login_count": 76
  },
  {
    "login_date": "2017-10-12",
    "unique_user_count": 42,
    "total_login_count": 83
  },
  {
    "login_date": "2017-10-13",
    "unique_user_count": 41,
    "total_login_count": 68
  },
  {
    "login_date": "2017-10-15",
    "unique_user_count": 1,
    "total_login_count": 1
  },
  {
    "login_date": "2017-10-16",
    "unique_user_count": 48,
    "total_login_count": 84
  },
  {
    "login_date": "2017-10-17",
    "unique_user_count": 50,
    "total_login_count": 98
  },
  {
    "login_date": "2017-10-18",
    "unique_user_count": 38,
    "total_login_count": 56
  },
  {
    "login_date": "2017-10-19",
    "unique_user_count": 38,
    "total_login_count": 98
  },
  {
    "login_date": "2017-10-20",
    "unique_user_count": 40,
    "total_login_count": 71
  },
  {
    "login_date": "2017-10-22",
    "unique_user_count": 2,
    "total_login_count": 2
  }];
  
 //Define SVG container full width and height
const fullWidth = 600;
const fullHeight = 200;

//Define bar chart area  widht and height
const margin = {
    top: 10,
    bottom: 10,
    left: 20,
    right: 20
}

const chartWidth = fullWidth - margin.left - margin.right;
const chartHeight = fullHeight - margin.top - margin.bottom;

//Draw SVG container
let svg = d3.select('body')
    .append('svg')
    .attr('width', fullWidth)
    .attr('height', fullHeight);

//Define xand y scale range of the bar chart
const xScale = d3.scaleBand()
    .range([0, chartWidth]);

const yScale = d3.scaleLinear()
    .range([chartHeight, 0]);

const yScale2 = d3.scaleLinear()
    .range([chartHeight, 0]);

    console.log('Data received from an API:', data)

    //defiene x and y scale domain
    yScale
        .domain([0, d3.max(data, d => +d.total_login_count)]);

    yScale2
        .domain([0, d3.max(data, d => +d.unique_user_count)]);

    xScale
        .domain(data.map(d => d.login_date));

    //Generate total login area chart
    let area = d3.area()
        .curve(d3.curveCatmullRom)
        .x(function (d) {
            return xScale(d.login_date);
        })
        .y0(fullHeight)
        .y1(function (d) {
            return yScale(+d.total_login_count)
        });
    //Generate unique user count area chart
    let area2 = d3.area()
        .curve(d3.curveCatmullRom)
        .x(function (d) {
            return xScale(d.login_date);
        })
        .y0(fullHeight)
        .y1(function (d) {
            return yScale2(+d.unique_user_count)
        });

    //Draw bar chart
    let group = svg.selectAll('g')
        .data([data])
        .enter()
        .append('g');

    //Draw area for total login count
    group
        .append('path')
        .attr('class', 'area')
        .attr('d', area);

    //Draw area for unique user count 
    group
        .append('path')
        .attr('class', 'area2')
        .attr('d', area2);

    //Dot points
    let points = group.selectAll('circle')
        .data(data)
        .enter()
        .append('circle');

    //Dot points
    let points2 = group.select('circle')
        .data(data)
        .enter()
        .append('circle');

    points.attrs({
            "cx": d => xScale(d.login_date),
            "cy": d => yScale(+d.total_login_count),
            "r": 5
        })
        .style("opacity", 1)
        .style('fill', '#F9A2CB');

    points2.attrs({
            "cx": d => xScale(d.login_date),
            "cy": d => yScale2(+d.unique_user_count),
            "r": 5
        })
        .style("opacity", 1)
        .style('fill', '#8BDBCE');
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Bar chart</title>
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <script src="https://d3js.org/d3-fetch.v1.min.js"></script>
    <script src='https://d3js.org/d3-selection-multi.v0.4.min.js'></script>
</head>
<style>
    .line {
        fill: none;
        stroke: orange;
        stroke-width: 1px;
    }

    .area {
        fill: #F9A2CB;
        stroke: none;
        opacity: 0.6;
        }
    
    .area2 {
        fill: #8BDBCE;
        stroke: none;
        opacity: 0.6;
        }
</style>

<body>
    <script type="text/javascript" src="main.js"></script>
</body>

</html>