为什么D3线生成器在围绕空值周围的比例使用失败

发布于 2025-02-06 08:34:58 字数 13168 浏览 4 评论 0原文

我试图理解为什么 d3.line 与比例结合时,围绕空值的行为不同。

要详细说明,我的数据集看起来像这样,它将始终包含一些null

const data = [
{ "x": 50, "y": 97.04013083865155 },
{ "x": 100, "y": null },
{ "x": 150, "y": 98.62594214598816 },
{ "x": 200, "y": 76.49419950954189 },
{ "x": 250, "y": 29.30639006661442 },
{ "x": 300, "y": 29.366842697148176 },
{ "x": 350, "y": 51.587600132998325 },
{ "x": 400, "y": null },
{ "x": 450, "y": null },
{ "x": 500, "y": 26.90860254816283 },
{ "x": 550, "y": null },
{ "x": 600, "y": 99.1622268038577 }
]

否级 +null

如果我想生成一条行,并且不使用 比例 ,我得到以下

////////////////////////////////////////////////////////////
//////////////////////// 1 DATA ///////////////////////////
////////////////////////////////////////////////////////////

const data = [
    { "x": 50, "y": 97.04013083865155 },
    { "x": 100, "y": null },
    { "x": 150, "y": 98.62594214598816 },
    { "x": 200, "y": 76.49419950954189 },
    { "x": 250, "y": 29.30639006661442 },
    { "x": 300, "y": 29.366842697148176 },
    { "x": 350, "y": 51.587600132998325 },
    { "x": 400, "y": null },
    { "x": 450, "y": null },
    { "x": 500, "y": 26.90860254816283 },
    { "x": 550, "y": null },
    { "x": 600, "y": 99.1622268038577 }


]
height = 400,
    width = 720;

padding = {
    top: 70,
    bottom: 50,
    left: 70,
    right: 70
}


const boundHeight = height - padding.top - padding.bottom;
const boundWidth = width - padding.right - padding.left;

////////////////////////////////////////////////////////////
//////////////////////// 2 CREATE SCALE ////////////////////
////////////////////////////////////////////////////////////

const scaleX = d3.scaleLinear()
    .range([0, boundWidth])
    .domain(d3.extent(data, d => d.x))

const scaleY = d3.scaleLinear()
    .range([boundHeight, 0])
    .domain(d3.extent(data, d => d.y))

////////////////////////////////////////////////////////////
//////////////////////// 3 SVG// ///////////////////////////
////////////////////////////////////////////////////////////

const svgns = 'http://www.w3.org/2000/svg'
const svg = d3.select('svg')

svg
    .attr('xmlns', svgns)
    .attr('viewBox', `0 0 ${width} ${height}`)

svg.append('rect')
    .attr('class', 'vBoxRect')
    .style("overflow", "visible")
    .attr('width', `${width}`)
    .attr('height', `${height}`)
    .attr('stroke', 'red')
    .attr('fill', 'none')

//create BOUND rect -- to be deleted later
svg.append('rect')
    .attr('class', 'boundRect')
    .attr('x', `${padding.left}`)
    .attr('y', `${padding.top}`)
    .attr('width', `${boundWidth}`)
    .attr('height', `${boundHeight}`)
    .attr('fill', 'none')
    .attr('stroke', 'black')



//create bound element
bound = svg.append('g')
    .attr('class', 'bound')
    .style('transform', `translate(${padding.left}px,${padding.top}px)`)

//constrcuct line generators

////////////////////////////////////////////////////////////
////////////////////////NO SCALE// /////////////////////////
////////////////////////////////////////////////////////////
noScale = d3.line()
    .x(d => d.x)
    .y(d => d.y)
    (data)


//no Scale
bound.append('path')
    .attr('class', 'Black-noScale')
    .attr('d', noScale)
    .attr('fill', 'none')
    .attr('stroke', 'black')
    .attr('stroke-width', '2')

console.log(noScale)
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>

<body>
<svg>
</svg>
    <div id="container" class="svg-container"></div>
    <script src="next.js"></script>
</body>

</html>

使用比例 +null

但是,如果我想生成一条线, ,并且使用 比例 ,生成器失败

////////////////////////////////////////////////////////////
//////////////////////// 1 DATA ///////////////////////////
////////////////////////////////////////////////////////////

const data = [
    { "x": 50, "y": 97.04013083865155 },
    { "x": 100, "y": null },
    { "x": 150, "y": 98.62594214598816 },
    { "x": 200, "y": 76.49419950954189 },
    { "x": 250, "y": 29.30639006661442 },
    { "x": 300, "y": 29.366842697148176 },
    { "x": 350, "y": 51.587600132998325 },
    { "x": 400, "y": null },
    { "x": 450, "y": null },
    { "x": 500, "y": 26.90860254816283 },
    { "x": 550, "y": null },
    { "x": 600, "y": 99.1622268038577 }


]
height = 400,
    width = 720;

padding = {
    top: 70,
    bottom: 50,
    left: 70,
    right: 70
}


const boundHeight = height - padding.top - padding.bottom;
const boundWidth = width - padding.right - padding.left;

////////////////////////////////////////////////////////////
//////////////////////// 2 CREATE SCALE ////////////////////
////////////////////////////////////////////////////////////

const scaleX = d3.scaleLinear()
    .range([0, boundWidth])
    .domain(d3.extent(data, d => d.x))

const scaleY = d3.scaleLinear()
    .range([boundHeight, 0])
    .domain(d3.extent(data, d => d.y))

////////////////////////////////////////////////////////////
//////////////////////// 3 SVG// ///////////////////////////
////////////////////////////////////////////////////////////

const svgns = 'http://www.w3.org/2000/svg'
const svg = d3.select('svg')

svg
    .attr('xmlns', svgns)
    .attr('viewBox', `0 0 ${width} ${height}`)

svg.append('rect')
    .attr('class', 'vBoxRect')
    .style("overflow", "visible")
    .attr('width', `${width}`)
    .attr('height', `${height}`)
    .attr('stroke', 'red')
    .attr('fill', 'none')

//create BOUND rect -- to be deleted later
svg.append('rect')
    .attr('class', 'boundRect')
    .attr('x', `${padding.left}`)
    .attr('y', `${padding.top}`)
    .attr('width', `${boundWidth}`)
    .attr('height', `${boundHeight}`)
    .attr('fill', 'none')
    .attr('stroke', 'black')



//create bound element
bound = svg.append('g')
    .attr('class', 'bound')
    .style('transform', `translate(${padding.left}px,${padding.top}px)`)



//constrcuct line generators

////////////////////////////////////////////////////////////
////////////////////////WITH SCALE// ////////////////////////
////////////////////////////////////////////////////////////

withScale = d3.line()
    .x(d => scaleX(d.x))
    .y(d => scaleY(d.y))
    (data)

bound.append('path')
    .attr('class', 'Orange-withScale')
    .attr('d', withScale)
    .attr('fill', 'none')
    .attr('stroke', 'orange')
    .attr('stroke-width', '2')
    
console.log(withScale);    
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>

<body>
<svg>
</svg>
    <div id="container" class="svg-container"></div>
    <script src="next.js"></script>
</body>

</html>

另一方面,Genrator与定义的效果很好地效果是否使用了

////////////////////////////////////////////////////////////
//////////////////////// 1 DATA ///////////////////////////
////////////////////////////////////////////////////////////

const data = [
    { "x": 50, "y": 97.04013083865155 },
    { "x": 100, "y": null },
    { "x": 150, "y": 98.62594214598816 },
    { "x": 200, "y": 76.49419950954189 },
    { "x": 250, "y": 29.30639006661442 },
    { "x": 300, "y": 29.366842697148176 },
    { "x": 350, "y": 51.587600132998325 },
    { "x": 400, "y": null },
    { "x": 450, "y": null },
    { "x": 500, "y": 26.90860254816283 },
    { "x": 550, "y": null },
    { "x": 600, "y": 99.1622268038577 }


]
height = 400,
    width = 720;

padding = {
    top: 70,
    bottom: 50,
    left: 70,
    right: 70
}


const boundHeight = height - padding.top - padding.bottom;
const boundWidth = width - padding.right - padding.left;

////////////////////////////////////////////////////////////
//////////////////////// 2 CREATE SCALE ////////////////////
////////////////////////////////////////////////////////////

const scaleX = d3.scaleLinear()
    .range([0, boundWidth])
    .domain(d3.extent(data, d => d.x))

const scaleY = d3.scaleLinear()
    .range([boundHeight, 0])
    .domain(d3.extent(data, d => d.y))

////////////////////////////////////////////////////////////
//////////////////////// 3 SVG// ///////////////////////////
////////////////////////////////////////////////////////////

const svgns = 'http://www.w3.org/2000/svg'
const svg = d3.select('svg')

svg
    .attr('xmlns', svgns)
    .attr('viewBox', `0 0 ${width} ${height}`)

svg.append('rect')
    .attr('class', 'vBoxRect')
    .style("overflow", "visible")
    .attr('width', `${width}`)
    .attr('height', `${height}`)
    .attr('stroke', 'red')
    .attr('fill', 'none')

//create BOUND rect -- to be deleted later
svg.append('rect')
    .attr('class', 'boundRect')
    .attr('x', `${padding.left}`)
    .attr('y', `${padding.top}`)
    .attr('width', `${boundWidth}`)
    .attr('height', `${boundHeight}`)
    .attr('fill', 'none')
    .attr('stroke', 'black')



//create bound element
bound = svg.append('g')
    .attr('class', 'bound')
    .style('transform', `translate(${padding.left}px,${padding.top}px)`)

//constrcuct line generators

////////////////////////////////////////////////////////////
////////////////////////NO SCALE// /////////////////////////
////////////////////////////////////////////////////////////

noScaleWithDefinedFilter = d3.line()
    .x(d => d.x)
    .y(d => d.y)
    .defined(d => d.y)
    (data.filter((a) => a.y !== null))

noScaleWithDefined = d3.line()
    .x(d => d.x)
    .y(d => d.y)
    .defined(d => d.y)
    (data)

//shows complete line
bound.append('path')
    .attr('class', 'Violet-noScale+defined+filter')
    .attr('d', noScaleWithDefinedFilter)
    .attr('fill', 'none')
    .attr('stroke', 'violet')
    .attr('stroke-width', '2')
    .style('transform', 'translateY(50px)')

//does not show null Y
bound.append('path')
    .attr('class', 'Red-noScale+defined')
    .attr('d', noScaleWithDefined)
    .attr('fill', 'none')
    .attr('stroke', 'red')
    .attr('stroke-width', '2')
    .style('transform', 'translateY(75px)')
    

////////////////////////////////////////////////////////////
////////////////////////WITH SCALE// /////////////////////////
////////////////////////////////////////////////////////////

withScaleWithDefined = d3.line()
    .x(d => scaleX(d.x))
    .y(d => scaleY(d.y))
    .defined(d => d.y)
    (data)

withScaleWithDefinedFilter = d3.line()
    .x(d => scaleX(d.x))
    .y(d => scaleY(d.y))
    .defined(d => d.y)
    (data.filter((a) => a.y !== null))

bound.append('path')
    .attr('class', 'Salmon-withScale+Defined+Filter')
    .attr('d', withScaleWithDefinedFilter)
    .attr('fill', 'none')
    .attr('stroke', 'salmon')
    .attr('stroke-width', '2')

bound.append('path')
    .attr('class', 'Blue-withScale+Defined')
    .attr('d', withScaleWithDefined)
    .attr('fill', 'none')
    .attr('stroke', 'blue')
    .attr('stroke-width', '2')
    .style('transform', 'translateY(-25px)')

console.log(noScaleWithDefinedFilter);
console.log(noScaleWithDefined);
console.log(withScaleWithDefined);
console.log(withScaleWithDefinedFilter);
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>

<body>
<svg>
</svg>
    <div id="container" class="svg-container"></div>
    <script src="next.js"></script>
</body>

</html>

我该如何实现,以下是什么

const noScale = d3.line()
    .x(d => d.x)
    .y(d => d.y)
    (data)

,但是通过扩展

const withScale = d3.line()
    .x(d => scaleX(d.x))
    .y(d => scaleY(d.y))
    (data)

可以实现这一目标而不破坏数据?

I am trying to understand why d3.line behaves differently around null values when combined with scale.

To elaborate, my dataset looks like this and it will always contain some null

const data = [
{ "x": 50, "y": 97.04013083865155 },
{ "x": 100, "y": null },
{ "x": 150, "y": 98.62594214598816 },
{ "x": 200, "y": 76.49419950954189 },
{ "x": 250, "y": 29.30639006661442 },
{ "x": 300, "y": 29.366842697148176 },
{ "x": 350, "y": 51.587600132998325 },
{ "x": 400, "y": null },
{ "x": 450, "y": null },
{ "x": 500, "y": 26.90860254816283 },
{ "x": 550, "y": null },
{ "x": 600, "y": 99.1622268038577 }
]

No Scale +null

If I want to generate a line, and not use scale, I get the following

////////////////////////////////////////////////////////////
//////////////////////// 1 DATA ///////////////////////////
////////////////////////////////////////////////////////////

const data = [
    { "x": 50, "y": 97.04013083865155 },
    { "x": 100, "y": null },
    { "x": 150, "y": 98.62594214598816 },
    { "x": 200, "y": 76.49419950954189 },
    { "x": 250, "y": 29.30639006661442 },
    { "x": 300, "y": 29.366842697148176 },
    { "x": 350, "y": 51.587600132998325 },
    { "x": 400, "y": null },
    { "x": 450, "y": null },
    { "x": 500, "y": 26.90860254816283 },
    { "x": 550, "y": null },
    { "x": 600, "y": 99.1622268038577 }


]
height = 400,
    width = 720;

padding = {
    top: 70,
    bottom: 50,
    left: 70,
    right: 70
}


const boundHeight = height - padding.top - padding.bottom;
const boundWidth = width - padding.right - padding.left;

////////////////////////////////////////////////////////////
//////////////////////// 2 CREATE SCALE ////////////////////
////////////////////////////////////////////////////////////

const scaleX = d3.scaleLinear()
    .range([0, boundWidth])
    .domain(d3.extent(data, d => d.x))

const scaleY = d3.scaleLinear()
    .range([boundHeight, 0])
    .domain(d3.extent(data, d => d.y))

////////////////////////////////////////////////////////////
//////////////////////// 3 SVG// ///////////////////////////
////////////////////////////////////////////////////////////

const svgns = 'http://www.w3.org/2000/svg'
const svg = d3.select('svg')

svg
    .attr('xmlns', svgns)
    .attr('viewBox', `0 0 ${width} ${height}`)

svg.append('rect')
    .attr('class', 'vBoxRect')
    .style("overflow", "visible")
    .attr('width', `${width}`)
    .attr('height', `${height}`)
    .attr('stroke', 'red')
    .attr('fill', 'none')

//create BOUND rect -- to be deleted later
svg.append('rect')
    .attr('class', 'boundRect')
    .attr('x', `${padding.left}`)
    .attr('y', `${padding.top}`)
    .attr('width', `${boundWidth}`)
    .attr('height', `${boundHeight}`)
    .attr('fill', 'none')
    .attr('stroke', 'black')



//create bound element
bound = svg.append('g')
    .attr('class', 'bound')
    .style('transform', `translate(${padding.left}px,${padding.top}px)`)

//constrcuct line generators

////////////////////////////////////////////////////////////
////////////////////////NO SCALE// /////////////////////////
////////////////////////////////////////////////////////////
noScale = d3.line()
    .x(d => d.x)
    .y(d => d.y)
    (data)


//no Scale
bound.append('path')
    .attr('class', 'Black-noScale')
    .attr('d', noScale)
    .attr('fill', 'none')
    .attr('stroke', 'black')
    .attr('stroke-width', '2')

console.log(noScale)
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>

<body>
<svg>
</svg>
    <div id="container" class="svg-container"></div>
    <script src="next.js"></script>
</body>

</html>

With Scale +null

However, if I want to generate a line, and use scale, the generator fails

////////////////////////////////////////////////////////////
//////////////////////// 1 DATA ///////////////////////////
////////////////////////////////////////////////////////////

const data = [
    { "x": 50, "y": 97.04013083865155 },
    { "x": 100, "y": null },
    { "x": 150, "y": 98.62594214598816 },
    { "x": 200, "y": 76.49419950954189 },
    { "x": 250, "y": 29.30639006661442 },
    { "x": 300, "y": 29.366842697148176 },
    { "x": 350, "y": 51.587600132998325 },
    { "x": 400, "y": null },
    { "x": 450, "y": null },
    { "x": 500, "y": 26.90860254816283 },
    { "x": 550, "y": null },
    { "x": 600, "y": 99.1622268038577 }


]
height = 400,
    width = 720;

padding = {
    top: 70,
    bottom: 50,
    left: 70,
    right: 70
}


const boundHeight = height - padding.top - padding.bottom;
const boundWidth = width - padding.right - padding.left;

////////////////////////////////////////////////////////////
//////////////////////// 2 CREATE SCALE ////////////////////
////////////////////////////////////////////////////////////

const scaleX = d3.scaleLinear()
    .range([0, boundWidth])
    .domain(d3.extent(data, d => d.x))

const scaleY = d3.scaleLinear()
    .range([boundHeight, 0])
    .domain(d3.extent(data, d => d.y))

////////////////////////////////////////////////////////////
//////////////////////// 3 SVG// ///////////////////////////
////////////////////////////////////////////////////////////

const svgns = 'http://www.w3.org/2000/svg'
const svg = d3.select('svg')

svg
    .attr('xmlns', svgns)
    .attr('viewBox', `0 0 ${width} ${height}`)

svg.append('rect')
    .attr('class', 'vBoxRect')
    .style("overflow", "visible")
    .attr('width', `${width}`)
    .attr('height', `${height}`)
    .attr('stroke', 'red')
    .attr('fill', 'none')

//create BOUND rect -- to be deleted later
svg.append('rect')
    .attr('class', 'boundRect')
    .attr('x', `${padding.left}`)
    .attr('y', `${padding.top}`)
    .attr('width', `${boundWidth}`)
    .attr('height', `${boundHeight}`)
    .attr('fill', 'none')
    .attr('stroke', 'black')



//create bound element
bound = svg.append('g')
    .attr('class', 'bound')
    .style('transform', `translate(${padding.left}px,${padding.top}px)`)



//constrcuct line generators

////////////////////////////////////////////////////////////
////////////////////////WITH SCALE// ////////////////////////
////////////////////////////////////////////////////////////

withScale = d3.line()
    .x(d => scaleX(d.x))
    .y(d => scaleY(d.y))
    (data)

bound.append('path')
    .attr('class', 'Orange-withScale')
    .attr('d', withScale)
    .attr('fill', 'none')
    .attr('stroke', 'orange')
    .attr('stroke-width', '2')
    
console.log(withScale);    
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>

<body>
<svg>
</svg>
    <div id="container" class="svg-container"></div>
    <script src="next.js"></script>
</body>

</html>

On the other hand, the genrator works well with defined whether scale used or not

////////////////////////////////////////////////////////////
//////////////////////// 1 DATA ///////////////////////////
////////////////////////////////////////////////////////////

const data = [
    { "x": 50, "y": 97.04013083865155 },
    { "x": 100, "y": null },
    { "x": 150, "y": 98.62594214598816 },
    { "x": 200, "y": 76.49419950954189 },
    { "x": 250, "y": 29.30639006661442 },
    { "x": 300, "y": 29.366842697148176 },
    { "x": 350, "y": 51.587600132998325 },
    { "x": 400, "y": null },
    { "x": 450, "y": null },
    { "x": 500, "y": 26.90860254816283 },
    { "x": 550, "y": null },
    { "x": 600, "y": 99.1622268038577 }


]
height = 400,
    width = 720;

padding = {
    top: 70,
    bottom: 50,
    left: 70,
    right: 70
}


const boundHeight = height - padding.top - padding.bottom;
const boundWidth = width - padding.right - padding.left;

////////////////////////////////////////////////////////////
//////////////////////// 2 CREATE SCALE ////////////////////
////////////////////////////////////////////////////////////

const scaleX = d3.scaleLinear()
    .range([0, boundWidth])
    .domain(d3.extent(data, d => d.x))

const scaleY = d3.scaleLinear()
    .range([boundHeight, 0])
    .domain(d3.extent(data, d => d.y))

////////////////////////////////////////////////////////////
//////////////////////// 3 SVG// ///////////////////////////
////////////////////////////////////////////////////////////

const svgns = 'http://www.w3.org/2000/svg'
const svg = d3.select('svg')

svg
    .attr('xmlns', svgns)
    .attr('viewBox', `0 0 ${width} ${height}`)

svg.append('rect')
    .attr('class', 'vBoxRect')
    .style("overflow", "visible")
    .attr('width', `${width}`)
    .attr('height', `${height}`)
    .attr('stroke', 'red')
    .attr('fill', 'none')

//create BOUND rect -- to be deleted later
svg.append('rect')
    .attr('class', 'boundRect')
    .attr('x', `${padding.left}`)
    .attr('y', `${padding.top}`)
    .attr('width', `${boundWidth}`)
    .attr('height', `${boundHeight}`)
    .attr('fill', 'none')
    .attr('stroke', 'black')



//create bound element
bound = svg.append('g')
    .attr('class', 'bound')
    .style('transform', `translate(${padding.left}px,${padding.top}px)`)

//constrcuct line generators

////////////////////////////////////////////////////////////
////////////////////////NO SCALE// /////////////////////////
////////////////////////////////////////////////////////////

noScaleWithDefinedFilter = d3.line()
    .x(d => d.x)
    .y(d => d.y)
    .defined(d => d.y)
    (data.filter((a) => a.y !== null))

noScaleWithDefined = d3.line()
    .x(d => d.x)
    .y(d => d.y)
    .defined(d => d.y)
    (data)

//shows complete line
bound.append('path')
    .attr('class', 'Violet-noScale+defined+filter')
    .attr('d', noScaleWithDefinedFilter)
    .attr('fill', 'none')
    .attr('stroke', 'violet')
    .attr('stroke-width', '2')
    .style('transform', 'translateY(50px)')

//does not show null Y
bound.append('path')
    .attr('class', 'Red-noScale+defined')
    .attr('d', noScaleWithDefined)
    .attr('fill', 'none')
    .attr('stroke', 'red')
    .attr('stroke-width', '2')
    .style('transform', 'translateY(75px)')
    

////////////////////////////////////////////////////////////
////////////////////////WITH SCALE// /////////////////////////
////////////////////////////////////////////////////////////

withScaleWithDefined = d3.line()
    .x(d => scaleX(d.x))
    .y(d => scaleY(d.y))
    .defined(d => d.y)
    (data)

withScaleWithDefinedFilter = d3.line()
    .x(d => scaleX(d.x))
    .y(d => scaleY(d.y))
    .defined(d => d.y)
    (data.filter((a) => a.y !== null))

bound.append('path')
    .attr('class', 'Salmon-withScale+Defined+Filter')
    .attr('d', withScaleWithDefinedFilter)
    .attr('fill', 'none')
    .attr('stroke', 'salmon')
    .attr('stroke-width', '2')

bound.append('path')
    .attr('class', 'Blue-withScale+Defined')
    .attr('d', withScaleWithDefined)
    .attr('fill', 'none')
    .attr('stroke', 'blue')
    .attr('stroke-width', '2')
    .style('transform', 'translateY(-25px)')

console.log(noScaleWithDefinedFilter);
console.log(noScaleWithDefined);
console.log(withScaleWithDefined);
console.log(withScaleWithDefinedFilter);
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>

<body>
<svg>
</svg>
    <div id="container" class="svg-container"></div>
    <script src="next.js"></script>
</body>

</html>

How can I achieve, what the following does

const noScale = d3.line()
    .x(d => d.x)
    .y(d => d.y)
    (data)

but with scaling

const withScale = d3.line()
    .x(d => scaleX(d.x))
    .y(d => scaleY(d.y))
    (data)

Can this be achieved without destroying the data?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

深海夜未眠 2025-02-13 08:34:58

这是设计和记录:如果您将null值传递给该线性刻度,则它将返回undefined,显然您无法使用undefined 在d属性中。

该比例本身的解决方案是使用未知,该将未知返回的值设置为您想要的任何值,例如零:

const scaleY = d3.scaleLinear()
    .unknown(0)
    etc...

如果,另一方面,您要 skip 零值,您必须使用定义而不是比例更改行发生器本身。

这是您的代码,未知(0)

////////////////////////////////////////////////////////////
//////////////////////// 1 DATA ///////////////////////////
////////////////////////////////////////////////////////////

const data = [
    { "x": 50, "y": 97.04013083865155 },
    { "x": 100, "y": null },
    { "x": 150, "y": 98.62594214598816 },
    { "x": 200, "y": 76.49419950954189 },
    { "x": 250, "y": 29.30639006661442 },
    { "x": 300, "y": 29.366842697148176 },
    { "x": 350, "y": 51.587600132998325 },
    { "x": 400, "y": null },
    { "x": 450, "y": null },
    { "x": 500, "y": 26.90860254816283 },
    { "x": 550, "y": null },
    { "x": 600, "y": 99.1622268038577 }


]
height = 400,
    width = 720;

padding = {
    top: 70,
    bottom: 50,
    left: 70,
    right: 70
}


const boundHeight = height - padding.top - padding.bottom;
const boundWidth = width - padding.right - padding.left;

////////////////////////////////////////////////////////////
//////////////////////// 2 CREATE SCALE ////////////////////
////////////////////////////////////////////////////////////

const scaleX = d3.scaleLinear()
    .range([0, boundWidth])
    .domain(d3.extent(data, d => d.x))

const scaleY = d3.scaleLinear()
    .unknown(0)
    .range([boundHeight, 0])
    .domain(d3.extent(data, d => d.y))

////////////////////////////////////////////////////////////
//////////////////////// 3 SVG// ///////////////////////////
////////////////////////////////////////////////////////////

const svgns = 'http://www.w3.org/2000/svg'
const svg = d3.select('svg')

svg
    .attr('xmlns', svgns)
    .attr('viewBox', `0 0 ${width} ${height}`)

svg.append('rect')
    .attr('class', 'vBoxRect')
    .style("overflow", "visible")
    .attr('width', `${width}`)
    .attr('height', `${height}`)
    .attr('stroke', 'red')
    .attr('fill', 'none')

//create BOUND rect -- to be deleted later
svg.append('rect')
    .attr('class', 'boundRect')
    .attr('x', `${padding.left}`)
    .attr('y', `${padding.top}`)
    .attr('width', `${boundWidth}`)
    .attr('height', `${boundHeight}`)
    .attr('fill', 'none')
    .attr('stroke', 'black')



//create bound element
bound = svg.append('g')
    .attr('class', 'bound')
    .style('transform', `translate(${padding.left}px,${padding.top}px)`)



//constrcuct line generators

////////////////////////////////////////////////////////////
////////////////////////WITH SCALE// ////////////////////////
////////////////////////////////////////////////////////////

withScale = d3.line()
    .x(d => scaleX(d.x))
    .y(d => scaleY(d.y))
    (data)

bound.append('path')
    .attr('class', 'Orange-withScale')
    .attr('d', withScale)
    .attr('fill', 'none')
    .attr('stroke', 'orange')
    .attr('stroke-width', '2')
    
console.log(withScale);
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>

<body>
<svg>
</svg>
    <div id="container" class="svg-container"></div>
    <script src="next.js"></script>
</body>

</html>

This is by design and documented: if you pass a null value to that linear scale it will return undefined, and obviously you cannot do anything with undefined inside the d attribute.

The solution for the scale itself is using unknown, which sets the unknown returned value to any value you want, for instance zero:

const scaleY = d3.scaleLinear()
    .unknown(0)
    etc...

If, on the other hand, you want to skip the null values, you have to change the line generator itself with defined, not the scale.

Here's your code with unknown(0):

////////////////////////////////////////////////////////////
//////////////////////// 1 DATA ///////////////////////////
////////////////////////////////////////////////////////////

const data = [
    { "x": 50, "y": 97.04013083865155 },
    { "x": 100, "y": null },
    { "x": 150, "y": 98.62594214598816 },
    { "x": 200, "y": 76.49419950954189 },
    { "x": 250, "y": 29.30639006661442 },
    { "x": 300, "y": 29.366842697148176 },
    { "x": 350, "y": 51.587600132998325 },
    { "x": 400, "y": null },
    { "x": 450, "y": null },
    { "x": 500, "y": 26.90860254816283 },
    { "x": 550, "y": null },
    { "x": 600, "y": 99.1622268038577 }


]
height = 400,
    width = 720;

padding = {
    top: 70,
    bottom: 50,
    left: 70,
    right: 70
}


const boundHeight = height - padding.top - padding.bottom;
const boundWidth = width - padding.right - padding.left;

////////////////////////////////////////////////////////////
//////////////////////// 2 CREATE SCALE ////////////////////
////////////////////////////////////////////////////////////

const scaleX = d3.scaleLinear()
    .range([0, boundWidth])
    .domain(d3.extent(data, d => d.x))

const scaleY = d3.scaleLinear()
    .unknown(0)
    .range([boundHeight, 0])
    .domain(d3.extent(data, d => d.y))

////////////////////////////////////////////////////////////
//////////////////////// 3 SVG// ///////////////////////////
////////////////////////////////////////////////////////////

const svgns = 'http://www.w3.org/2000/svg'
const svg = d3.select('svg')

svg
    .attr('xmlns', svgns)
    .attr('viewBox', `0 0 ${width} ${height}`)

svg.append('rect')
    .attr('class', 'vBoxRect')
    .style("overflow", "visible")
    .attr('width', `${width}`)
    .attr('height', `${height}`)
    .attr('stroke', 'red')
    .attr('fill', 'none')

//create BOUND rect -- to be deleted later
svg.append('rect')
    .attr('class', 'boundRect')
    .attr('x', `${padding.left}`)
    .attr('y', `${padding.top}`)
    .attr('width', `${boundWidth}`)
    .attr('height', `${boundHeight}`)
    .attr('fill', 'none')
    .attr('stroke', 'black')



//create bound element
bound = svg.append('g')
    .attr('class', 'bound')
    .style('transform', `translate(${padding.left}px,${padding.top}px)`)



//constrcuct line generators

////////////////////////////////////////////////////////////
////////////////////////WITH SCALE// ////////////////////////
////////////////////////////////////////////////////////////

withScale = d3.line()
    .x(d => scaleX(d.x))
    .y(d => scaleY(d.y))
    (data)

bound.append('path')
    .attr('class', 'Orange-withScale')
    .attr('d', withScale)
    .attr('fill', 'none')
    .attr('stroke', 'orange')
    .attr('stroke-width', '2')
    
console.log(withScale);
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>

<body>
<svg>
</svg>
    <div id="container" class="svg-container"></div>
    <script src="next.js"></script>
</body>

</html>

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文