157 lines
5.4 KiB
JavaScript
157 lines
5.4 KiB
JavaScript
import * as d3 from "d3";
|
||
import { useEffect, useRef, useState } from "react";
|
||
|
||
|
||
function drawGraph(data, keyX, drawMin, drawMax, graphtype) {
|
||
// значения по оси ОХ
|
||
|
||
// создаем массив для построения графика
|
||
// console.log(keyX)
|
||
if(keyX=="release" || keyX=="size"){
|
||
data = d3.sort(data, (x,y)=>Number(x["labelX"])-Number(y["labelX"]));
|
||
}
|
||
const svg = d3.select("svg")
|
||
svg.selectAll('*').remove();
|
||
|
||
// создаем словарь с атрибутами области вывода графика
|
||
const attr_area = {
|
||
width: parseFloat(svg.style('width')),
|
||
height: parseFloat(svg.style('height')),
|
||
marginX: 50,
|
||
marginY: 50
|
||
}
|
||
|
||
// создаем шкалы преобразования и выводим оси
|
||
const [scX, scY] = createAxis(svg, data, attr_area,[drawMin,drawMax]);
|
||
|
||
// рисуем график
|
||
|
||
const scaleYDomain = d3.extent(data.map(d => d.values[1]));
|
||
if (drawMin && drawMax){
|
||
createChart(svg, data, scX, scY, attr_area, "blue", 0,graphtype,0,scaleYDomain)
|
||
createChart(svg, data, scX, scY, attr_area, "red", 1,graphtype,0,scaleYDomain)
|
||
}
|
||
else if (drawMin) {
|
||
createChart(svg, data, scX, scY, attr_area, "blue", 0,graphtype,1,scaleYDomain)
|
||
}
|
||
else if (drawMax) {
|
||
createChart(svg, data, scX, scY, attr_area, "red", 1,graphtype,1,scaleYDomain)
|
||
}
|
||
}
|
||
|
||
function createAxis(svg, data, attr_area, selections) {
|
||
// находим интервал значений, которые нужно отложить по оси OY
|
||
// максимальное и минимальное значение и максимальных высот по каждой стране
|
||
const max = d3.max(data,d => d.values[Number(selections[1])]);
|
||
const min = d3.min(data,d => d.values[Number(1-selections[0])]);
|
||
// console.log(max,min,data)
|
||
// функция интерполяции значений на оси
|
||
// по оси ОХ текстовые значения
|
||
const scaleX = d3.scaleBand()
|
||
.domain(data.map(d => d.labelX))
|
||
.range([0, attr_area.width - 2 * attr_area.marginX]);
|
||
|
||
const scaleY = d3.scaleLinear()
|
||
.domain([min * 0.85, max * 1.1])
|
||
.range([attr_area.height - 2 * attr_area.marginY, 0]);
|
||
|
||
// создание осей
|
||
const axisX = d3.axisBottom(scaleX); // горизонтальная
|
||
const axisY = d3.axisLeft(scaleY); // вертикальная
|
||
|
||
// отрисовка осей в SVG-элементе
|
||
svg.append("g")
|
||
.attr("transform", `translate(${attr_area.marginX},
|
||
${attr_area.height - attr_area.marginY})`)
|
||
.call(axisX)
|
||
.selectAll("text") // подписи на оси - наклонные
|
||
.style("text-anchor", "end")
|
||
.attr("dx", "-.8em")
|
||
.attr("dy", ".15em")
|
||
.attr("transform", d => "rotate(-45)");
|
||
|
||
svg.append("g")
|
||
.attr("transform", `translate(${attr_area.marginX}, ${attr_area.marginY})`)
|
||
.call(axisY);
|
||
|
||
return [scaleX, scaleY]
|
||
}
|
||
|
||
function createChart(svg, data, scaleX, scaleY, attr_area, color, valueIdx,isHistogram,horisontalScale,scaleYDomain) {
|
||
if (isHistogram){
|
||
svg.selectAll(".dot")
|
||
.data(data)
|
||
.enter()
|
||
.append("rect")
|
||
.attr("x", d => scaleX(d.labelX)+valueIdx*6)
|
||
.attr("y", d => scaleY(d.values[valueIdx]))
|
||
.attr("width",6*(horisontalScale+1))
|
||
.attr("height",d => scaleY(scaleYDomain[0]*0.85)-scaleY(d.values[valueIdx]))
|
||
.attr("transform", `translate(${attr_area.marginX}, ${attr_area.marginY})`)
|
||
.style("fill", color)
|
||
|
||
|
||
}
|
||
else{
|
||
const r = 4;
|
||
|
||
svg.selectAll(".dot")
|
||
.data(data)
|
||
.enter()
|
||
.append("circle")
|
||
.attr("r", r)
|
||
.attr("cx", d => scaleX(d.labelX) + scaleX.bandwidth() / 2+valueIdx*4)
|
||
.attr("cy", d => scaleY(d.values[valueIdx]))
|
||
.attr("transform", `translate(${attr_area.marginX}, ${attr_area.marginY})`)
|
||
.style("fill", color)
|
||
}
|
||
}
|
||
|
||
|
||
const ChartDraw = (props) => {
|
||
const chartRef = useRef(null);
|
||
|
||
const [width, setWidth] = useState(0);
|
||
const [height, setHeight] = useState(0);
|
||
|
||
// заносим в состояния ширину и высоту svg-элемента
|
||
useEffect(() => {
|
||
const svg = d3.select(chartRef.current);
|
||
setWidth(parseFloat(svg.style('width')));
|
||
setHeight(parseFloat(svg.style('height')));
|
||
});
|
||
// задаем отступы в svg-элементе
|
||
const margin = {
|
||
top:10,
|
||
bottom:60,
|
||
left:40,
|
||
right:10
|
||
};
|
||
|
||
// вычисляем ширину и высоту области для вывода графиков
|
||
const boundsWidth = width - margin.left - margin.right;
|
||
const boundsHeight = height - margin.top - margin.bottom;
|
||
|
||
useEffect(() => {
|
||
if(boundsWidth<0||boundsHeight<0){
|
||
return;
|
||
}
|
||
const svg = d3.select(chartRef.current);
|
||
// выводим прямоугольник,
|
||
svg
|
||
.append("rect")
|
||
.attr("x", margin.left)
|
||
.attr("y", margin.top)
|
||
.attr("width", boundsWidth)
|
||
.attr("height", boundsHeight)
|
||
.style("fill", "lightgrey");
|
||
// console.log(props)
|
||
drawGraph(props.data,props.ox,props.minMax[0],props.minMax[1],Number(props.graphType))
|
||
});
|
||
|
||
return (
|
||
<svg ref={chartRef} > </svg>
|
||
)
|
||
}
|
||
|
||
export default ChartDraw; |