如何在d3中的每行中制作带有虚线段的多折线图

如何解决如何在d3中的每行中制作带有虚线段的多折线图

数据如下:

var data = [
  {
    name: 'USA',values: [
      { date: '2000',price: '100',isDashed:true },{ date: '2001',price: '110',{ date: '2002',price: '145',isDashed:false },...,{
    name: 'Canada',price: '200',isDashed:true  },price: '120',price: '33',isDashed:true  }...
    ]
  }
]

我正在关注此链接https://codesandbox.io/s/multi-line-chart-example-forked-hjix5?file=/src/index.js

问题是,当数据为true时,在我的数据中有一个isisshed标志,例如,我需要在图表中使用虚线段(例如)。

enter image description here

var data = [{
    name: "USA",values: [{
        date: "2000",price: "100",r: 1
      },{
        date: "2001",price: "110",{
        date: "2002",price: "145"
      },{
        date: "2003",price: "241"
      },{
        date: "2004",price: "101"
      },{
        date: "2005",price: "90"
      },{
        date: "2006",price: "10"
      },{
        date: "2007",price: "35"
      },{
        date: "2008",price: "21"
      },{
        date: "2009",price: "201"
      }
    ]
  },{
    name: "Canada",price: "200"
      },price: "120"
      },price: "33"
      },price: "51"
      },price: "190"
      },price: "85"
      },price: "221"
      },price: "101"
      }
    ]
  },{
    name: "Maxico",price: "50"
      },price: "5"
      },price: "71"
      },price: "20"
      },price: "9"
      },price: "220"
      },price: "235"
      },price: "61"
      },price: "10"
      }
    ]
  }
];

var width = 500;
var height = 300;
var margin = 50;
var duration = 250;

var lineOpacity = "0.25";
var lineOpacityHover = "0.85";
var otherLinesOpacityHover = "0.1";
var lineStroke = "1.5px";
var lineStrokeHover = "2.5px";

var circleOpacity = "0.85";
var circleOpacityOnLineHover = "0.25";
var circleRadius = 3;
var circleRadiusHover = 6;

/* Format Data */
var parseDate = d3.timeParse("%Y");
data.forEach(function(d) {
  d.values.forEach(function(d) {
    d.date = parseDate(d.date);
    d.price = +d.price;
    d.r = +d.r;
  });
});

/* Scale */
var xScale = d3
  .scaleTime()
  .domain(d3.extent(data[0].values,(d) => d.date))
  .range([0,width - margin]);

var yScale = d3
  .scaleLinear()
  .domain([0,d3.max(data[0].values,(d) => d.price)])
  .range([height - margin,0]);

var color = d3.scaleOrdinal(d3.schemeCategory10);

/* Add SVG */
var svg = d3
  .select("#chart")
  .append("svg")
  .attr("width",width + margin + "px")
  .attr("height",height + margin + "px")
  .append("g")
  .attr("transform",`translate(${margin},${margin})`);

/* Add line into SVG */
var line = d3
  .line()
  .x((d) => xScale(d.date))
  .y((d) => yScale(d.price));

let lines = svg.append("g").attr("class","lines");

lines
  .selectAll(".line-group")
  .data(data)
  .enter()
  .append("g")
  .attr("class","line-group")
  .on("mouseover",function(d,i) {
    svg
      .append("text")
      .attr("class","title-text")
      .style("fill",color(i))
      .text(d.name)
      .attr("text-anchor","middle")
      .attr("x",(width - margin) / 2)
      .attr("y",5)
      .attr("r",5);
  })
  .on("mouseout",function(d) {
    svg.select(".title-text").remove();
  })
  .append("path")
  .attr("class","line")
  .attr("d",(d) => line(d.values))

  .style("stroke",(d,i) => color(i))
  .style("opacity",lineOpacity)
  .on("mouseover",function(d) {
    d3.selectAll(".line").style("opacity",otherLinesOpacityHover);
    d3.selectAll(".circle").style("opacity",circleOpacityOnLineHover);
    d3.select(this)
      .style("opacity",lineOpacityHover)
      .style("stroke-width",lineStrokeHover)
      .style("cursor","pointer");
  })
  .on("mouseout",lineOpacity);
    d3.selectAll(".circle").style("opacity",circleOpacity);
    d3.select(this)
      .style("stroke-width",lineStroke)
      .style("cursor","none");
  });

/* Add circles in the line */
lines
  .selectAll("circle-group")
  .data(data)
  .enter()
  .append("g")
  .style("fill",i) => color(i))
  .selectAll("circle")
  .data((d) => d.values)
  .enter()
  .append("g")
  .attr("class","circle")
  .on("mouseover",function(d) {
    d3.select(this)
      .style("cursor","pointer")
      .append("text")
      .attr("class","text")
      .text(`${d.price}`)
      .attr("x",(d) => xScale(d.date) + 5)
      .attr("y",(d) => yScale(d.price) - 10);
  })
  .on("mouseout","none")
      .transition()
      .duration(duration)
      .selectAll(".text")
      .remove();
  })
  .append("circle")
  .attr("cx",(d) => xScale(d.date))
  .attr("cy",(d) => yScale(d.price))
  .attr("r",circleRadius)
  .style("opacity",circleOpacity)
  .on("mouseover",function(d) {
    d3.select(this)
      .transition()
      .duration(duration)
      .attr("r",circleRadiusHover);
  })
  .on("mouseout",function(d) {
    d3.select(this).transition().duration(duration).attr("r",circleRadius);
  });

/* Add Axis into SVG */
var xAxis = d3.axisBottom(xScale).ticks(5);
var yAxis = d3.axisLeft(yScale).ticks(5);

svg
  .append("g")
  .attr("class","x axis")
  .attr("transform",`translate(0,${height - margin})`)
  .call(xAxis);

svg
  .append("g")
  .attr("class","y axis")
  .call(yAxis)
  .append("text")
  .attr("y",15)
  .attr("transform","rotate(-90)")
  .attr("fill","#000")
  .text("Total values");
.App {
  font-family: sans-serif;
  text-align: center;
}

svg {
  font-family: Sans-Serif,Arial;
}

.line {
  stroke-width: 2;
  fill: none;
}

.axis path {
  stroke: black;
}

.text {
  font-size: 12px;
}

.title-text {
  font-size: 12px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div class="App">
  <div id="chart" />
</div>

解决方法

正如我在评论中说的那样,您似乎希望线段既是点缀又是较浅的颜色。最简单的解决方法是仅绘制两个单独的线段。较难的解决方案是将stroke-dasharray和线性渐变结合使用作为笔触,但这可能容易出错。

由于您似乎不太在意,因此以下代码显示了更简单的解决方法:

var data = [{
    name: "USA",values: [{
        date: "2000",price: "100",r: 1,isDashed: true
      },{
        date: "2001",price: "110",{
        date: "2002",price: "145",{
        date: "2003",price: "241",{
        date: "2004",price: "101",{
        date: "2005",price: "90",{
        date: "2006",price: "10",{
        date: "2007",price: "35"
      },{
        date: "2008",price: "21"
      },{
        date: "2009",price: "201"
      }
    ]
  },{
    name: "Canada",price: "200"
      },price: "120"
      },price: "33"
      },price: "51",price: "190",price: "120",price: "85"
      },price: "221"
      },price: "101"
      }
    ]
  },{
    name: "Maxico",price: "50"
      },price: "10"
      },price: "5",price: "71"
      },price: "20"
      },price: "9"
      },price: "220"
      },price: "235"
      },price: "61"
      },price: "10"
      }
    ]
  }
];

var width = 500;
var height = 300;
var margin = 50;
var duration = 250;

var lineOpacity = "0.25";
var lineOpacityHover = "0.85";
var otherLinesOpacityHover = "0.1";
var lineStroke = "1.5px";
var lineStrokeHover = "2.5px";

var circleOpacity = "0.85";
var circleOpacityOnLineHover = "0.25";
var circleRadius = 3;
var circleRadiusHover = 6;

/* Format Data */
var parseDate = d3.timeParse("%Y");
data.forEach(function(d) {
  d.values.forEach(function(d) {
    d.date = parseDate(d.date);
    d.price = +d.price;
    d.r = +d.r;
  });
});

/* Scale */
var xScale = d3
  .scaleTime()
  .domain(d3.extent(data[0].values,(d) => d.date))
  .range([0,width - margin]);

var yScale = d3
  .scaleLinear()
  .domain([0,d3.max(data[0].values,(d) => d.price)])
  .range([height - margin,0]);

var color = d3.scaleOrdinal(d3.schemeCategory10);

/* Add SVG */
var svg = d3
  .select("#chart")
  .append("svg")
  .attr("width",width + margin + "px")
  .attr("height",height + margin + "px")
  .append("g")
  .attr("transform",`translate(${margin},${margin})`);

/* Add line into SVG */
var line = d3
  .line()
  .defined(d => !isNaN(d.price)) // to make it deal with NaNs
  .x((d) => xScale(d.date))
  .y((d) => yScale(d.price));

let lines = svg.append("g").attr("class","lines");

let lineGroups = lines
  .selectAll(".line-group")
  .data(data)
  .enter()
  .append("g")
  .attr("class","line-group")
  .on("mouseover",function(d,i) {
    svg
      .append("text")
      .attr("class","title-text")
      .style("fill",color(i))
      .text(d.name)
      .attr("text-anchor","middle")
      .attr("x",(width - margin) / 2)
      .attr("y",5)
      .attr("r",5);
  })
  .on("mouseout",function(d) {
    svg.select(".title-text").remove();
  });

function getDashedParts(values,lookingForDashed) {
  const results = [];
  let previousWasMatch = false;
  
  // For every node we add,we need also the node just before,// so we can draw a line between them
  values.forEach((v,i) => {
    if(lookingForDashed === !!v.isDashed) {
      if (!previousWasMatch && i > 0) {
        results.push(values[i - 1]);
      }
      results.push(v);
      previousWasMatch = true;
    } else {
      results.push({ date: v.date,price: NaN });
      previousWasMatch = false;
    }
  });
  
  // console.log(results);
  return results;
}

lineGroups
  .append("path")
  .attr("class","line")
  .attr("d",(d) => line(getDashedParts(d.values,false)));

lineGroups
  .append("path")
  .attr("class","line line-dashed")
  .attr("d",true)));

lineGroups.each(function(d,i) {
  const lineParts = d3.select(this).selectAll("path");
  lineParts
    .style("stroke",color(i))
    .style("opacity",lineOpacity)
    .on("mouseover",() => {
      d3.selectAll(".line").style("opacity",otherLinesOpacityHover);
      d3.selectAll(".circle").style("opacity",circleOpacityOnLineHover);
      lineParts
        .style("opacity",lineOpacityHover)
        .style("stroke-width",lineStrokeHover)
        .style("cursor","pointer");
    })
    .on("mouseout",lineOpacity);
      d3.selectAll(".circle").style("opacity",circleOpacity);
      lineParts
        .style("stroke-width",lineStroke)
        .style("cursor","none");
    });
});

/* Add circles in the line */
lines
  .selectAll("circle-group")
  .data(data)
  .enter()
  .append("g")
  .style("fill",(d,i) => color(i))
  .selectAll("circle")
  .data((d) => d.values)
  .enter()
  .append("g")
  .attr("class","circle")
  .on("mouseover",function(d) {
    d3.select(this)
      .style("cursor","pointer")
      .append("text")
      .attr("class","text")
      .text(`${d.price}`)
      .attr("x",(d) => xScale(d.date) + 5)
      .attr("y",(d) => yScale(d.price) - 10);
  })
  .on("mouseout","none")
      .transition()
      .duration(duration)
      .selectAll(".text")
      .remove();
  })
  .append("circle")
  .attr("cx",(d) => xScale(d.date))
  .attr("cy",(d) => yScale(d.price))
  .attr("r",circleRadius)
  .style("opacity",circleOpacity)
  .on("mouseover",function(d) {
    d3.select(this)
      .transition()
      .duration(duration)
      .attr("r",circleRadiusHover);
  })
  .on("mouseout",function(d) {
    d3.select(this).transition().duration(duration).attr("r",circleRadius);
  });

/* Add Axis into SVG */
var xAxis = d3.axisBottom(xScale).ticks(5);
var yAxis = d3.axisLeft(yScale).ticks(5);

svg
  .append("g")
  .attr("class","x axis")
  .attr("transform",`translate(0,${height - margin})`)
  .call(xAxis);

svg
  .append("g")
  .attr("class","y axis")
  .call(yAxis)
  .append("text")
  .attr("y",15)
  .attr("transform","rotate(-90)")
  .attr("fill","#000")
  .text("Total values");
.App {
  font-family: sans-serif;
  text-align: center;
}

svg {
  font-family: Sans-Serif,Arial;
}

.line {
  stroke-width: 2;
  fill: none;
}

.line-dashed {
  stroke-dasharray: 5,5;
}

.axis path {
  stroke: black;
}

.text {
  font-size: 12px;
}

.title-text {
  font-size: 12px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div class="App">
  <div id="chart" />
</div>

我所做的:

  1. 我在您的isDefined()的定义中添加了d3.line(),以便可以处理NaN;
  2. 我为每个数组过滤了isDashed true或false的值。当我找到一个匹配的元素而前一个不匹配时,我将两者都添加到了结果集中。这样,将在它们之间画一条线,并且不会出现间隙;
  3. 我稍微改变了鼠标悬停的行为,因此,如果将鼠标悬停在虚线和实线部分上,都将突出显示。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-