Reputation: 621
Attached is my minimum working example in JS Fiddle. I am able to only show the initial data, but when I change the slider, the data does not update. Where am I going wrong in my code here? Apologies for the poor structure - I am still a beginner in D3.
https://jsfiddle.net/pv02z8em/1/
chartGroup
.selectAll('.line-series')
.data(data, d=> d.x)
.join(
enter => {
enter.append('path')
.attr('class', d => `line-series x_${d.x}`)
.attr("d", drawLine(data))
.style('stroke', 'dodgerblue')
.style('stroke-width', 2)
.style('fill', 'none')
.style('opacity', 1)
},
update => { update.transition().duration(500) },
exit => { exit.remove() }
)
}
Upvotes: 1
Views: 941
Reputation: 102174
You have several issues in that code:
You are not selecting the slider by its ID. It should be d3.select('#slider-x-range')
;
In the listener, you're not calling buildLine(data)
;
Remove everything inside buildLine
that isn't related to the path itself, otherwise you'll create different SVGs every time the user moves the slider;
Your join
structure is currently appending several paths, one above the other. This is certainly not what you want. It could be just:
let path = chartGroup.selectAll('.line-series')
.data([data]);
path = path.enter()
.append('path')
.attr('class', "line-series")
.attr("d", d => drawLine(d))
.style('stroke', 'dodgerblue')
.style('stroke-width', 2)
.style('fill', 'none')
.style('opacity', 1)
.merge(path)
path.transition().duration(500).attr("d", d => drawLine(d))
Here is your code with these and other minor changes:
<!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">
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/skeleton.css">
<link rel="stylesheet" href="css/skeleton_override.css">
<style>
svg {
display: inline-block;
position: relative;
vertical-align: top;
overflow: hidden;
}
.x-axis,
.y-axis {
font: 16px sans-serif;
}
.axis-label {
font: 18px sans-serif;
}
.chart-title {
font: 24px sans-serif;
}
.x-axis .tick:first-of-type text {
fill: none;
}
.body {
display: flex;
}
.chart-group-container {
margin: 10px 20px 20px 20px;
}
.controls-container {
display: flex;
flex-direction: column;
}
.controls-header {
color: black;
padding: 0.5rem 2rem;
text-align: left;
}
.controls-body {
overflow: auto;
font-size: 0.8em;
cursor: default;
}
.slidecontainer {
text-align: left;
margin: 10px;
font-family: sans-serif;
font-size: 14px;
}
#slider-x-range {
vertical-align: bottom;
}
</style>
<title>Document</title>
</head>
<body>
<div class="chart-group-container">
<div class="row">
<div class="six columns">
<div class="chart-container">
<div class="viz">
</div>
</div>
</div>
<div class="six columns"></div>
<div class="controls-container">
<div class="controls-header">UI Controls</div>
<div class="controls-body">
<div class="slider-label">Adjust x axis</div>
<div class="slidecontainer">
<span>10</span>
<input type="range" min="10" max="100" value="1" id="slider-x-range">
<span>100</span>
</div>
</div>
</div>
</div>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
//let sinWave = Math.sin(x)
let range = function(start, stop, step) {
step = step || 1;
let arr = []
for (let i = start; i < stop; i += step) {
arr.push(i);
}
return arr;
}
let generateSinWave = function(x) {
let y = []
x.forEach(function(i) {
y.push(Math.sin(i))
});
return y;
}
const generateData = (n) => {
x = range(0, n, 1)
y = generateSinWave(x)
let labels = ['x', 'y']
let data = []
for (let i = 0; i < x.length; i++) {
data.push({
x: x[i],
y: y[i]
})
}
return data;
}
</script>
<script>
let margin = {
top: 50,
right: 30,
bottom: 30,
left: 100
},
width = 800 - margin.left - margin.right
height = 400 - margin.top - margin.bottom;
let xScale = d3.scaleLinear()
.range([0, width])
let yScale = d3.scaleLinear()
.range([height, 0])
.nice()
let drawLine = d3.line()
.x(d => xScale(d.x))
.y(d => yScale(d.y))
.curve(d3.curveBasis);
let svg = d3.select('.viz')
.append('svg')
.attr("viewBox", `0 0 ${width + margin.left + margin.right} ${height + margin.top + margin.bottom}`)
.attr("preserveAspectRatio", "xMinYMin meet")
//.attr('width', `${width + margin.left + margin.right}px`)
//.attr('height', `${height + margin.top + margin.bottom}px`)
//.classed("svg-content", true);
.append('g')
.attr('class', 'line-chart-container')
.attr('transform', `translate(${margin.left}, ${margin.top})`);
/* d3.select(".line-chart-container")
.attr("style", "outline: thin solid black;")
.attr("margin-right", "102px") */
const chartGroup = svg.append('g').attr('class', 'line-chart')
// Draw x axis
const xAxis = d3.axisBottom(xScale).tickSizeOuter(0);
const xAxisDraw = svg
.append('g')
.attr('class', 'x-axis')
//.style('font', '14px sans-serif')
.attr('transform', `translate(0, ${height / 2})`)
.call(xAxis);
const yAxis = d3
.axisLeft(yScale)
.ticks(10)
//.tickSizeInner(-width);
const yAxisDraw = svg
.append('g')
.attr('class', 'y-axis')
.call(yAxis);
// x axis label
svg.append('text')
.attr('class', 'axis-label')
.attr('text-anchor', 'end')
.text('X axis')
.attr('x', width)
.attr('y', height - margin.bottom + 50)
// y axis label
svg.append('text')
.attr('class', 'axis-label')
.attr('text-anchor', 'end')
.attr('transform', 'rotate(-90)')
.attr('x', margin.top + 50 - (height / 2))
.attr('y', margin.left - 160)
.text('Y axis')
// Draw Header
const header = svg
.append('g')
.attr('class', 'chart-title')
.attr('transform', `translate(${width / 2 - 75}, ${margin.top - 75})`)
.append('text')
header.append('tspan').text('Sine wave')
function buildLine(data) {
xScale.domain([d3.min(data, d => d.x), d3.max(data, d => d.x)])
yScale.domain([d3.min(data, d => d.y), d3.max(data, d => d.y)])
let path = chartGroup
.selectAll('.line-series')
.data([data]);
path = path.enter().append('path')
.attr('class', "line-series")
.attr("d", d => drawLine(d))
.style('stroke', 'dodgerblue')
.style('stroke-width', 2)
.style('fill', 'none')
.style('opacity', 1)
.merge(path)
path.transition().duration(500).attr("d", d => drawLine(d))
}
let xRangeSlider = document.getElementById('slider-x-range');
xRangeSlider.min = 10;
xRangeSlider.max = 100;
let data = generateData(xRangeSlider.value)
buildLine(data)
d3.select('#slider-x-range')
.on("change", d => {
data = generateData(xRangeSlider.value)
buildLine(data)
});
</script>
</body>
</html>
As you'll find out, the transition is not what you probably expect: that's an unrelated issue, the interpolation of the d
property string (you can find more info here).
Upvotes: 1