当前位置: 首页 > ai >正文

D3.js研发分区柱状图

d3.js研发分区柱状图

特殊要求:分三个区域 0-0.2、0.2-0.3、other三个区域,其中0.3必须标注 

因为坐标轴的要求,其实在这个图中是画了两个x轴,然后隐藏掉了常规x轴,显示了特殊x轴

下面是项目里封装好的方法 可以直接调用,上代码:

import * as d3 from "d3";const CVDistributionBar = (options = {}) => {let originContainer = document.querySelector("#chart-container"),originHeight = originContainer.offsetHeight,originWidth = originContainer.offsetWidth;let height = originHeight * (options.params.height / 100);let width = originWidth * (options.params.width / 100);const container = d3.select(options.container);let svg = container.select("svg");// 获取标签样式function getSvgTextStyle({text = "",fontSize = 14,fontFamily = "Arial",fontWeight = "normal"} = {}) {const svg = d3.select("body").append("svg").attr("class", "get-svg-text-style");const textStyle = svg.append("text").text(text).attr("font-size", fontSize).attr("font-family", fontFamily).attr("font-weight", fontWeight).node().getBBox();svg.remove();return {width: textStyle.width,height: textStyle.height};}// 获取线性坐标轴宽高function getSvgBandAxisStyle({fontSize = 20,orient = "bottom",fontFamily = "Arial",fontWeight = "normal",rotate = 0,domain = ["A", "B", "C"],range = [0, 200]} = {}) {let axis;let svg = d3.select("body").append("svg").attr("width", 200).attr("height", 100).attr("transform", "translate(300, 200)").attr("class", "get-svg-axis-style");let scale = d3.scaleBand().domain(domain).range(range);if (orient === "bottom" || orient === "top") {axis = d3.axisBottom(scale);} else {axis = d3.axisLeft(scale);}let axisStyle = svg.append("g").call(axis).call((g) => {g.selectAll("text").attr("fill", "#555").attr("font-size", fontSize).attr("font-family", fontFamily).attr("font-weight", fontWeight).attr("tmpY",g.select("text").attr("tmpY") || g.select("text").attr("dy")).attr("dy",rotate > 70 && rotate <= 90? "0.35em": rotate >= -90 && rotate < -70? "0.4em": g.select("text").attr("tmpY")).attr("text-anchor",orient === "left"? "end": rotate? rotate > 0? "start": "end": "middle").attr("transform",`translate(0, 0) ${rotate ? `rotate(${rotate} 0 ${g.select("text").attr("y")})` : ""}`);}).node().getBBox();svg.remove();return {width: axisStyle.width,height: axisStyle.height};}const rawData = options.data.barData;const cvLabel = options.data.cvLabel;// 配置参数let {bar_witdh = 25,color1 = "pink",color2 = "#E0D1F1",color3 = "gray",cutoff_color = "red",cutoff_size = 1,x_title = "111",x_title_color = "#000",x_title_font = "Arial",x_title_size = 12,x_text_rotate = 45,x_text_color = "#000000",x_text_size = 14,x_text_font = "Arial",y_title = "222",y_title_color = "#000",y_title_font = "Arial",y_title_size = 14,y_text_color = "#000000",y_text_size = 14,y_text_font = "Arial",main_title = "333",main_title_color = "#000",main_title_font = "Arial",main_title_size = 14} = options.params;x_text_rotate = -x_text_rotate;// 数据预处理const data = rawData.map((d) => {const parts = d.cv.split(/[~]/).map(Number);return {...d,start: parts[0],end: parts[1] || parts[0] + 0.05};});//   let xDomain = [0, 1.2];let xDomain = options.data.xDomain;let yDomain = [0, d3.max(data, (d) => d.density)];const mainTitleH = main_title? getSvgTextStyle({text: main_title,fontSize: main_title_size,fontFamily: main_title_font}).height + 20: 0;const xTitleH = x_title? getSvgTextStyle({text: x_title,fontSize: x_title_size,fontFamily: x_title_font}).height + 20: 0;const xAxisH =getSvgBandAxisStyle({fontSize: x_text_size,fontFamily: x_text_font,rotate: x_text_rotate,domain: xDomain}).height + 20;const yTitleH = y_title? getSvgTextStyle({text: y_title,fontSize: y_title_size,fontFamily: y_title_font}).height + 20: 0;const yAxisW =getSvgBandAxisStyle({fontSize: y_text_size,fontFamily: y_text_font,domain: yDomain,orient: "left"}).width + 20;// 图表参数const margin = {top: 30 + mainTitleH,right: 50,bottom: 30 + xAxisH + xTitleH,left: 30 + yAxisW + yTitleH};// 创建SVG画布if (svg.empty()) {svg = container.append("svg");} else {svg.selectAll("*").remove();}svg.attr("width", width).attr("height", height);// 比例尺const xScale = d3.scaleLinear().domain(xDomain).range([margin.left, width - margin.right]);const yScale = d3.scaleLinear().domain(yDomain).nice().range([height - margin.bottom, margin.top]);// 绘制柱状图svg.selectAll("rect").data(data).join("rect").attr("x", (d) => xScale(d.start)).attr("width",bar_witdh//   (d) => {//     const w = xScale(d.end) - xScale(d.start);//     console.log(w);//     return w > 0 ? w - 1 : 0;//   }).attr("y", (d) => yScale(d.density)).attr("height", (d) => yScale(0) - yScale(d.density)).attr("fill", (d) => {if (d.end <= 0.2) return color1; // 0-0.2区间if (d.start >= 0.2 && d.end <= 0.3) return color2; // 0.2-0.3区间return color3; // 0.3+区间});// 主标题svg.append("text").attr("class", "chart-title").attr("x", width / 2).attr("y", margin.top - 20).text(main_title).attr("text-anchor", "middle").attr("font-family", main_title_font).attr("font-size", main_title_size).attr("fill", main_title_color);// X轴标题svg.append("text").attr("class", "axis-title").attr("x", width / 2).attr("y", height - margin.bottom + xAxisH + xTitleH / 2).text(x_title).attr("text-anchor", "middle").attr("font-family", x_title_font).attr("font-size", x_title_size).attr("fill", x_title_color);// Y轴标题svg.append("text").attr("class", "axis-title").attr("transform", `rotate(-90)`).attr("x", -height / 2).attr("y", margin.left - yAxisW - yTitleH / 2).text(y_title).attr("text-anchor", "middle").attr("font-family", y_title_font).attr("font-size", y_title_size).attr("fill", y_title_color);// 创建X轴const requiredXTicks = [xDomain[0], 0.3, xDomain[1]]; // 必须包含的刻度const generatedXTicks = xScale.ticks(4); // 自动生成的刻度const mergedXTicks = Array.from(new Set([...generatedXTicks, ...requiredXTicks])).sort((a, b) => a - b).filter((t) => t >= xDomain[0] && t <= xDomain[1]);const xAxis = d3.axisBottom(xScale).tickValues(mergedXTicks).tickFormat((d) => d3.format(".1f")(d));svg.append("g").attr("class", "x-axis").attr("transform", `translate(0,${height - margin.bottom})`) // 保持底部定位.call(xAxis).call((g) => {g.selectAll(".tick text").attr("fill", x_text_color).attr("font-size", `${x_text_size}px`).attr("font-family", x_text_font).each(function () {const text = d3.select(this);text.attr("tmpY", text.attr("dy") || "0.71em"); // 默认值处理}).attr("dy", (d, i, nodes) => {const text = d3.select(nodes[i]);const rotate = x_text_rotate;if (rotate > 70 && rotate <= 90) return "0.35em";if (rotate >= -90 && rotate < -70) return "0.4em";return text.attr("tmpY");}).attr("text-anchor", (d) =>x_text_rotate ? (x_text_rotate > 0 ? "start" : "end") : "middle").attr("transform", (d, i, nodes) =>x_text_rotate? `rotate(${x_text_rotate} 0 ${d3.select(nodes[i]).attr("y")})`: "");});// 创建Y轴const yAxis = d3.axisLeft(yScale).tickSize(5).tickPadding(8);svg.append("g").attr("class", "y-axis").attr("transform", `translate(${margin.left},0)`).call(yAxis).selectAll("path") // 坐标轴线样式.attr("stroke", "#333").attr("stroke-width", 1.5).attr("fill", "none").attr("shape-rendering", "crispEdges");// 设置刻度文本样式svg.selectAll(".y-axis .tick text").attr("font-family", y_text_font).style("font-size", y_text_size).style("fill", y_text_color);// 添加卡值线const thresholdX = xScale(0.3);svg.append("line").attr("stroke", cutoff_color).attr("stroke-width", cutoff_size)//   .attr("stroke-dasharray", "4")  //虚线.attr("x1", thresholdX).attr("x2", thresholdX).attr("y1", margin.top).attr("y2", height - margin.bottom);// 添加阈值标注//   const total = d3.sum(data, (d) => d.density);//   const belowSum = d3.sum(//     data.filter((d) => d.end <= 0.3),//     (d) => d.density//   );svg.append("text").attr("fill", "#ff0000").style("font-size", "12px").style("font-weight", "bold").attr("x", thresholdX + 10).attr("y", margin.top + 20).text(cvLabel);// 设置坐标轴刻度线样式svg.selectAll(".tick line").attr("stroke", "#333").attr("stroke-width", 1).attr("shape-rendering", "crispEdges");
};export default CVDistributionBar;

调用:

 CVDistributionBar({data: plots,params: chartParam,container: "#bar-container"});

 

献上研发初期没封装的html版的吧!可直接运行

<!DOCTYPE html>
<html><head><title>CV Distribution</title><script src="https://d3js.org/d3.v7.min.js"></script></head><body><div id="chart"></div></body><script>// 获取标签样式function getSvgTextStyle({text = "",fontSize = 14,fontFamily = "Arial",fontWeight = "normal"} = {}) {const svg = d3.select("body").append("svg").attr("class", "get-svg-text-style");const textStyle = svg.append("text").text(text).attr("font-size", fontSize).attr("font-family", fontFamily).attr("font-weight", fontWeight).node().getBBox();svg.remove();return {width: textStyle.width,height: textStyle.height};}// 获取线性坐标轴宽高function getSvgBandAxisStyle({fontSize = 20,orient = "bottom",fontFamily = "Arial",fontWeight = "normal",rotate = 0,domain = ["A", "B", "C"],range = [0, 200]} = {}) {let axis;let svg = d3.select("body").append("svg").attr("width", 200).attr("height", 100).attr("transform", "translate(300, 200)").attr("class", "get-svg-axis-style");let scale = d3.scaleBand().domain(domain).range(range);if (orient === "bottom" || orient === "top") {axis = d3.axisBottom(scale);} else {axis = d3.axisLeft(scale);}let axisStyle = svg.append("g").call(axis).call((g) => {g.selectAll("text").attr("fill", "#555").attr("font-size", fontSize).attr("font-family", fontFamily).attr("font-weight", fontWeight).attr("tmpY",g.select("text").attr("tmpY") || g.select("text").attr("dy")).attr("dy",rotate > 70 && rotate <= 90? "0.35em": rotate >= -90 && rotate < -70? "0.4em": g.select("text").attr("tmpY")).attr("text-anchor",orient === "left"? "end": rotate? rotate > 0? "start": "end": "middle").attr("transform",`translate(0, 0) ${rotate? `rotate(${rotate} 0 ${g.select("text").attr("y")})`: ""}`);}).node().getBBox();svg.remove();return {width: axisStyle.width,height: axisStyle.height};}// 原始数据const rawData = [{ cv: "0~0.05", density: 3082 },{ cv: "0.05~0.1", density: 2109 },{ cv: "0.1~0.15", density: 1100 },{ cv: "0.15~0.2", density: 621 },{ cv: "0.2~0.25", density: 430 },{ cv: "0.25~0.3", density: 256 },{ cv: "0.3~0.35", density: 177 },{ cv: "0.35~0.4", density: 127 },{ cv: "0.4~0.45", density: 87 },{ cv: "0.45~0.5", density: 58 },{ cv: "0.5~0.55", density: 60 },{ cv: "0.55~0.6", density: 33 },{ cv: "0.6~0.65", density: 21 },{ cv: "0.65~0.7", density: 22 },{ cv: "0.7~0.75", density: 14 },{ cv: "0.75~0.8", density: 7 },{ cv: "0.8~0.85", density: 12 },{ cv: "0.85~0.9", density: 9 },{ cv: "0.9~0.95", density: 3 },{ cv: "0.95~1", density: 2 },{ cv: "1~1.05", density: 2 },{ cv: "1.05~1.1", density: 1 }];const cvLabel = "cv<0.3 73.16%";// 配置参数let // {bar_witdh = 35,color1 = "pink",color2 = "#E0D1F1",color3 = "gray",cutoff_color = "red",cutoff_size = 1,x_title = "111",x_title_color = "#000",x_title_font = "Arial",x_title_size = 14,x_text_rotate = -45,x_text_color = "#000000",x_text_size = 14,x_text_font = "Arial",y_title = "222",y_title_color = "#000",y_title_font = "Arial",y_title_size = 14,y_text_color = "#000000",y_text_size = 14,y_text_font = "Arial",main_title = "333",main_title_color = "#000",main_title_font = "Arial",main_title_size = 14;// } = options.params;// 数据预处理const data = rawData.map((d) => {const parts = d.cv.split(/[~]/).map(Number);return {...d,start: parts[0],end: parts[1] || parts[0] + 0.05};});let xDomain = [0, 1.2];let yDomain = [0, d3.max(data, (d) => d.density)];const mainTitleH = main_title? getSvgTextStyle({text: main_title,fontSize: main_title_size,fontFamily: main_title_font}).height + 20: 0;const xTitleH = x_title? getSvgTextStyle({text: x_title,fontSize: x_title_size,fontFamily: x_title_font}).height + 20: 0;const xAxisH =getSvgBandAxisStyle({fontSize: x_text_size,fontFamily: x_text_font,rotate: x_text_rotate,domain: xDomain}).height + 20;const yTitleH = y_title? getSvgTextStyle({text: y_title,fontSize: y_title_size,fontFamily: y_title_font}).height + 20: 0;const yAxisW =getSvgBandAxisStyle({fontSize: y_text_size,fontFamily: y_text_font,domain: yDomain,orient: "left"}).width + 20;// 图表参数const margin = {top: mainTitleH,right: 50,bottom: xAxisH + xTitleH,left: yAxisW + yTitleH};const width = 1000;const height = 500;const innerHeight = height - margin.top - margin.bottom;const innerWidth = width - margin.left - margin.right;// 创建SVG画布const svg = d3.select("#chart").append("svg").attr("width", width).attr("height", height);// 比例尺const xScale = d3.scaleLinear().domain(xDomain).range([margin.left, width - margin.right]);const yScale = d3.scaleLinear().domain(yDomain).nice().range([height - margin.bottom, margin.top]);// 绘制柱状图svg.selectAll("rect").data(data).join("rect").attr("x", (d) => xScale(d.start)).attr("width",bar_witdh//   (d) => {//     const w = xScale(d.end) - xScale(d.start);//     console.log(w);//     return w > 0 ? w - 1 : 0;//   }).attr("y", (d) => yScale(d.density)).attr("height", (d) => yScale(0) - yScale(d.density)).attr("fill", (d) => {if (d.end <= 0.2) return color1; // 0-0.2区间if (d.start >= 0.2 && d.end <= 0.3) return color2; // 0.2-0.3区间return color3; // 0.3+区间});// 主标题svg.append("text").attr("class", "chart-title").attr("x", width / 2).attr("y", margin.top - 20).text(main_title).attr("text-anchor", "middle").attr("font-family", main_title_font).attr("font-size", main_title_size).attr("fill", main_title_color);// X轴标题svg.append("text").attr("class", "axis-title").attr("x", width / 2).attr("y", height - margin.bottom + xAxisH + xTitleH / 2).text(x_title).attr("text-anchor", "middle").attr("font-family", x_title_font).attr("font-size", x_title_size).attr("fill", x_title_color);// Y轴标题svg.append("text").attr("class", "axis-title").attr("transform", `rotate(-90)`).attr("x", -height / 2).attr("y", margin.left - yAxisW - yTitleH / 2).text(y_title).attr("text-anchor", "middle").attr("font-family", y_title_font).attr("font-size", y_title_size).attr("fill", y_title_color);// 创建X轴const requiredXTicks = [xDomain[0], 0.3, xDomain[1]]; // 必须包含的刻度const generatedXTicks = xScale.ticks(4); // 自动生成的刻度const mergedXTicks = Array.from(new Set([...generatedXTicks, ...requiredXTicks])).sort((a, b) => a - b).filter((t) => t >= xDomain[0] && t <= xDomain[1]);const xAxis = d3.axisBottom(xScale).tickValues(mergedXTicks).tickFormat((d) => d3.format(".1f")(d));svg.append("g").attr("class", "x-axis").attr("transform", `translate(0,${height - margin.bottom})`) // 保持底部定位.call(xAxis).call((g) => {g.selectAll(".tick text").attr("fill", x_text_color).attr("font-size", x_text_size).attr("font-family", x_text_font).each(function () {const text = d3.select(this);text.attr("tmpY", text.attr("dy") || "0.71em"); // 默认值处理}).attr("dy", (d, i, nodes) => {const text = d3.select(nodes[i]);const rotate = x_text_rotate;if (rotate > 70 && rotate <= 90) return "0.35em";if (rotate >= -90 && rotate < -70) return "0.4em";return text.attr("tmpY");}).attr("text-anchor", (d) =>x_text_rotate ? (x_text_rotate > 0 ? "start" : "end") : "middle").attr("transform", (d, i, nodes) =>x_text_rotate? `rotate(${x_text_rotate} 0 ${d3.select(nodes[i]).attr("y")})`: "");});// 创建Y轴const yAxis = d3.axisLeft(yScale).tickSize(5).tickPadding(8);svg.append("g").attr("transform", `translate(${margin.left},0)`).call(yAxis).selectAll("path") // 坐标轴线样式.attr("stroke", "#333").attr("stroke-width", 1.5).attr("fill", "none").attr("shape-rendering", "crispEdges");// 设置刻度文本样式svg.selectAll(".tick text").attr("font-family", y_text_font).style("font-size", y_text_size).style("fill", y_text_color);// 添加卡值线const thresholdX = xScale(0.3);svg.append("line").attr("stroke", cutoff_color).attr("stroke-width", cutoff_size)//   .attr("stroke-dasharray", "4")  //虚线.attr("x1", thresholdX).attr("x2", thresholdX).attr("y1", margin.top).attr("y2", height - margin.bottom);// 添加阈值标注const total = d3.sum(data, (d) => d.density);const belowSum = d3.sum(data.filter((d) => d.end <= 0.3),(d) => d.density);svg.append("text").attr("fill", "#ff0000").style("font-size", "12px").style("font-weight", "bold").attr("x", thresholdX + 10).attr("y", margin.top + 20).text(cvLabel);// 设置坐标轴刻度线样式svg.selectAll(".tick line").attr("stroke", "#333").attr("stroke-width", 1).attr("shape-rendering", "crispEdges");</script>
</html>

效果图:

什么时候寒暑假也能带上打工人阿!!!要不上四休三也行阿!!!要不多发几倍工资吧!!!好想去玩 不想早起!!!

http://www.xdnf.cn/news/13786.html

相关文章:

  • 电子垃圾之涂鸦控制板
  • 题解:CF2093B Expensive Number
  • C++面试(8)-----求链表中环的入口节点
  • C++面试(6)-----调整数组顺序使奇数位于偶数前面
  • CodeForces 1453C. Triangles
  • QOpenGLWidget 中能同时显示 .step 的结构树和渲染图吗
  • 快递鸟电商退换货技术全解析:构建智能化逆向物流管理体系
  • IT运维的365天--028 批处理自行检测并以管理员权限运行
  • vue3 常见引用
  • 伊吖学C笔记(6、数、求和、排列)
  • 模拟电路的知识
  • 如何通过插件系统打造个性化效率工作流
  • go部分语法记录
  • 【Fifty Project - D36】
  • 2025pmx文件怎么打开blender和虚幻
  • 林业资源多元监测技术守护绿水青山
  • 说一下Java里面线程池的拒绝策略
  • 从实验室到实践:无人机固件越权提取技术解析
  • DNS常用的域名记录
  • 品融电商:头部全域电商代运营,助品牌决胜多平台时代
  • supervisorctr命令简介
  • 翻译核心词汇
  • React中修改 state 时必须返回一个新对象 (immutable update)
  • Windows环境变量原理(用户变量与系统变量)(用户环境变量、系统环境变量)
  • 解锁 AI 短视频创作密码,开启你的创意之旅
  • DOcplex用法锦集(持续更新)
  • CKA考试知识点分享(12)---configmap
  • 【Android Studio】新建项目及问题解决
  • python3如何使用QT编写基础的对话框程序
  • 【开发常用命令】:服务器与本地之间的数据传输