英文:
Passing on condition to d3.dispatch
问题
以下是使用 d3.dispatch
的部分代码:
//with dispatch
const dispatch = d3.dispatch('one', 'two');
dispatch.on('one', function() {
this.attr('opacity', '0.5');
});
dispatch.on('two', function(val) {
this.attr('opacity', (() => {
return val;
}))
});
listener.on('mouseover', function() {
dispatch.call('one', rect)
})
.on('mouseout', function() {
dispatch.call('one', rect)
})
.on('mousemove', function(event) {
const points = d3.pointer(event);
const pointY = points[1];
const selOne = [rect.nodes()][0].filter((a) => {
const y = a.y.baseVal.value <= pointY && pointY <= a.height.baseVal.value + a.y.baseVal.value;
return y;
});
const currData = (selOne.length === 1) ? selOne[0].__data__['id'] : null;
const val = (d, i) => (d.id === currData) ? '1' : '0.5';
dispatch.call('two', d3.selectAll('.rectContainer>rect'))
});
这是使用 d3.dispatch
实现的交互部分的代码。
英文:
I am trying to see how can I author an interaction to d3.dispatch
.
Without dispatch, the interaction is following
<!-- begin snippet: js hide: true console: true babel: false -->
<!-- language: lang-js -->
const data = [
{ "id": 1 },
{ "id": 2 },
{ "id": 3 },
{ "id": 4 },
{ "id": 5 },
{ "id": 6 },
{ "id": 7 },
{ "id": 8 }
];
const svgns = 'http://www.w3.org/2000/svg';
const width = 600;
const height = 450;
const posX = 40;
const posY = 20;
const heightRect = 40;
const widthRect = 40;
const padding = 10;
const svg = d3.select('body')
.append('svg')
.attr('xmlns', svgns)
.attr('viewBox', `0 0 ${width} ${height}`);
const bgRect = svg.append('rect')
.classed('boundary', true)
.attr('x', '0')
.attr('y', '0')
.attr('width', `${width}`)
.attr('height', `${height}`)
.attr('fill', `#E6E6E6`);
//-------------BUILD COLOR SCALE---------------------------//
const interpolatorSequentialMultiHueName = [
'd3.interpolateViridis'
]
const xColorScale = d3.scaleSequential().domain(d3.extent(data, d => d.id))
.interpolator(eval(`${interpolatorSequentialMultiHueName[0]}`));
//-------------BUILD RECT---------------------------//
const rect = svg.append('g')
.classed('rectContainer', true)
.selectAll('rect')
.data(data)
.join('rect')
.attr('x', `${posX}`)
.attr('y', ((d, i) => posY + heightRect * i + padding * i))
.attr('width', `${widthRect}`)
.attr('height', `${heightRect}`)
.attr('fill', (d => xColorScale(d.id)))
.attr('opacity', '0.5');
//listener
const listener =
d3.select('rect')
.clone()
.raise()
.attr('x', `${posX}`)
.attr('y', ((d, i) => { return posY }))
.attr('width', `${widthRect}`)
.attr('height', ((d, i) => { const node = rect.nodes(); const len = node.length - 1; return node[len].y.baseVal.value + node[len].height.baseVal.value - node[0].y.baseVal.value }))
.attr('fill', 'none')
.attr('class', 'listener')
.style("pointer-events", "all");
// interaction without dispatch
listener.on('mouseover', function() {
rect.attr('opacity', '0.5')
})
.on('mouseout', function(event) {
rect.attr('opacity', '0.5');
})
.on('mousemove', function(event) {
const points = d3.pointer(event);
const pointY = points[1];
// what is the current selection
const selOne = [rect.nodes()][0].filter((a) => {
const y = a.y.baseVal.value <= pointY && pointY <= a.height.baseVal.value + a.y.baseVal.value;
return y;
});
//what is the data of the curent selection
const currData = (selOne.length === 1) ? selOne[0].__data__['id'] : null;
//conditional statement to control opacity based on current selection
d3.selectAll('.rectContainer>rect')
.attr('opacity', ((d, i) => (d.id === currData) ? '1' : '0.5'))
});
<!-- language: lang-html -->
<!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>
</body>
<script src="min.js">
</script>
</html>
<!-- end snippet -->
I tried to convert the above to d3.dispatch
and this is what I tried which did not work
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
const data = [
{ "id": 1 },
{ "id": 2 },
{ "id": 3 },
{ "id": 4 },
{ "id": 5 },
{ "id": 6 },
{ "id": 7 },
{ "id": 8 }
];
const svgns = 'http://www.w3.org/2000/svg';
const width = 600;
const height = 450;
const posX = 40;
const posY = 20;
const heightRect = 40;
const widthRect = 40;
const padding = 10;
const svg = d3.select('body')
.append('svg')
.attr('xmlns', svgns)
.attr('viewBox', `0 0 ${width} ${height}`);
const bgRect = svg.append('rect')
.classed('boundary', true)
.attr('x', '0')
.attr('y', '0')
.attr('width', `${width}`)
.attr('height', `${height}`)
.attr('fill', `#E6E6E6`);
//-------------BUILD COLOR SCALE---------------------------//
const interpolatorSequentialMultiHueName = [
'd3.interpolateViridis'
]
const xColorScale = d3.scaleSequential().domain(d3.extent(data, d => d.id))
.interpolator(eval(`${interpolatorSequentialMultiHueName[0]}`));
//-------------BUILD RECT---------------------------//
const rect = svg.append('g')
.classed('rectContainer', true)
.selectAll('rect')
.data(data)
.join('rect')
.attr('x', `${posX}`)
.attr('y', ((d, i) => posY + heightRect * i + padding * i))
.attr('width', `${widthRect}`)
.attr('height', `${heightRect}`)
.attr('fill', (d => xColorScale(d.id)))
.attr('opacity', '0.5');
//listener
const listener =
d3.select('rect')
.clone()
.raise()
.attr('x', `${posX}`)
.attr('y', ((d, i) => { return posY }))
.attr('width', `${widthRect}`)
.attr('height', ((d, i) => { const node = rect.nodes(); const len = node.length - 1; return node[len].y.baseVal.value + node[len].height.baseVal.value - node[0].y.baseVal.value }))
.attr('fill', 'none')
.attr('class', 'listener')
.style("pointer-events", "all");
//with dispatch
const dispatch = d3.dispatch('one', 'two');
dispatch.on('one', function() {
this.attr('opacity', '0.5');
});
dispatch.on('two', function(val) {
this.attr('opacity', (() => {
return val;
}))
});
listener.on('mouseover', function() {
dispatch.call('one', rect)
})
.on('mouseout', function() {
dispatch.call('one', rect)
})
.on('mousemove', function(event) {
const points = d3.pointer(event);
const pointY = points[1];
const selOne = [rect.nodes()][0].filter((a) => {
const y = a.y.baseVal.value <= pointY && pointY <= a.height.baseVal.value + a.y.baseVal.value;
return y;
});
const currData = (selOne.length === 1) ? selOne[0].__data__['id'] : null;
const val = (d, i) => (d.id === currData) ? '1' : '0.5';
dispatch.call('two', d3.selectAll('.rectContainer>rect'))
})
<!-- language: lang-html -->
<!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>
</body>
<script src="min.js">
</script>
</html>
<!-- end snippet -->
答案1
得分: 1
希望这只是一个练习,因为老实说,这段代码对于生产来说过于繁琐,这里有很多东西需要重构。
关于你的问题,问题在于你忘记了值,这是dispatch.call
中的第三个参数。但即使你传递了val
,也不会起作用,因为整个选择中的值都是相同的。
因此,传递currData
:
dispatch.call('two', d3.selectAll('.rectContainer>rect'), currData)
然后,在你的dispatch中:
dispatch.on('two', function(currData) {
this.attr('opacity', (d, i) => (d.id === currData) ? '1' : '0.5')
});
以下是经过这些更改的代码:
const data = [{
"id": 1
},
{
"id": 2
},
{
"id": 3
},
{
"id": 4
},
{
"id": 5
},
{
"id": 6
},
{
"id": 7
},
{
"id": 8
}
];
const svgns = 'http://www.w3.org/2000/svg';
const width = 600;
const height = 450;
const posX = 40;
const posY = 20;
const heightRect = 40;
const widthRect = 40;
const padding = 10;
const svg = d3.select('body')
.append('svg')
.attr('xmlns', svgns)
.attr('viewBox', `0 0 ${width} ${height}`);
const bgRect = svg.append('rect')
.classed('boundary', true)
.attr('x', '0')
.attr('y', '0')
.attr('width', `${width}`)
.attr('height', `${height}`)
.attr('fill', `#E6E6E6`);
//-------------构建颜色比例---------------------------//
const interpolatorSequentialMultiHueName = [
'd3.interpolateViridis'
];
const xColorScale = d3.scaleSequential().domain(d3.extent(data, d => d.id))
.interpolator(eval(`${interpolatorSequentialMultiHueName[0]}`));
//-------------构建矩形---------------------------//
const rect = svg.append('g')
.classed('rectContainer', true)
.selectAll('rect')
.data(data)
.join('rect')
.attr('x', `${posX}`)
.attr('y', ((d, i) => posY + heightRect * i + padding * i))
.attr('width', `${widthRect}`)
.attr('height', `${heightRect}`)
.attr('fill', (d => xColorScale(d.id)))
.attr('opacity', '0.5');
//listener
const listener =
d3.select('rect')
.clone()
.raise()
.attr('x', `${posX}`)
.attr('y', ((d, i) => {
return posY
}))
.attr('width', `${widthRect}`)
.attr('height', ((d, i) => {
const node = rect.nodes();
const len = node.length - 1;
return node[len].y.baseVal.value + node[len].height.baseVal.value - node[0].y.baseVal.value
}))
.attr('fill', 'none')
.attr('class', 'listener')
.style("pointer-events", "all");
//带有dispatch
const dispatch = d3.dispatch('one', 'two');
dispatch.on('one', function() {
this.attr('opacity', '0.5');
});
dispatch.on('two', function(currData) {
this.attr('opacity', (d, i) => (d.id === currData) ? '1' : '0.5')
});
listener.on('mouseover', function() {
dispatch.call('one', rect)
})
.on('mouseout', function() {
dispatch.call('one', rect)
})
.on('mousemove', function(event) {
const points = d3.pointer(event);
const pointY = points[1];
const selOne = [rect.nodes()][0].filter((a) => {
const y = a.y.baseVal.value <= pointY && pointY <= a.height.baseVal.value + a.y.baseVal.value;
return y;
});
const currData = (selOne.length === 1) ? selOne[0].__data__['id'] : null;
dispatch.call('two', d3.selectAll('.rectContainer>rect'), currData)
});
我已经为你翻译好了。如果你有任何其他问题,请随时提问。
英文:
I hope this is just an exercise, because honestly this code is way too cumbersome for production, lots of things here should be refactored.
Regarding your question, the issue is that you're forgetting the value, which is the third argument in dispatch.call
. But even if you passed val
that will not work, because you would have the same value for the entire selection.
Thus, pass the currData
:
dispatch.call('two', d3.selectAll('.rectContainer>rect'), currData)
And then, in your dispatch:
dispatch.on('two', function(currData) {
this.attr('opacity', (d, i) => (d.id === currData) ? '1' : '0.5')
});
Here's your code with those changes:
<!-- begin snippet: js hide: true console: true babel: false -->
<!-- language: lang-js -->
const data = [{
"id": 1
},
{
"id": 2
},
{
"id": 3
},
{
"id": 4
},
{
"id": 5
},
{
"id": 6
},
{
"id": 7
},
{
"id": 8
}
];
const svgns = 'http://www.w3.org/2000/svg';
const width = 600;
const height = 450;
const posX = 40;
const posY = 20;
const heightRect = 40;
const widthRect = 40;
const padding = 10;
const svg = d3.select('body')
.append('svg')
.attr('xmlns', svgns)
.attr('viewBox', `0 0 ${width} ${height}`);
const bgRect = svg.append('rect')
.classed('boundary', true)
.attr('x', '0')
.attr('y', '0')
.attr('width', `${width}`)
.attr('height', `${height}`)
.attr('fill', `#E6E6E6`);
//-------------BUILD COLOR SCALE---------------------------//
const interpolatorSequentialMultiHueName = [
'd3.interpolateViridis'
]
const xColorScale = d3.scaleSequential().domain(d3.extent(data, d => d.id))
.interpolator(eval(`${interpolatorSequentialMultiHueName[0]}`));
//-------------BUILD RECT---------------------------//
const rect = svg.append('g')
.classed('rectContainer', true)
.selectAll('rect')
.data(data)
.join('rect')
.attr('x', `${posX}`)
.attr('y', ((d, i) => posY + heightRect * i + padding * i))
.attr('width', `${widthRect}`)
.attr('height', `${heightRect}`)
.attr('fill', (d => xColorScale(d.id)))
.attr('opacity', '0.5');
//listener
const listener =
d3.select('rect')
.clone()
.raise()
.attr('x', `${posX}`)
.attr('y', ((d, i) => {
return posY
}))
.attr('width', `${widthRect}`)
.attr('height', ((d, i) => {
const node = rect.nodes();
const len = node.length - 1;
return node[len].y.baseVal.value + node[len].height.baseVal.value - node[0].y.baseVal.value
}))
.attr('fill', 'none')
.attr('class', 'listener')
.style("pointer-events", "all");
//with dispatch
const dispatch = d3.dispatch('one', 'two');
dispatch.on('one', function() {
this.attr('opacity', '0.5');
});
dispatch.on('two', function(currData) {
this.attr('opacity', (d, i) => (d.id === currData) ? '1' : '0.5')
});
listener.on('mouseover', function() {
dispatch.call('one', rect)
})
.on('mouseout', function() {
dispatch.call('one', rect)
})
.on('mousemove', function(event) {
const points = d3.pointer(event);
const pointY = points[1];
const selOne = [rect.nodes()][0].filter((a) => {
const y = a.y.baseVal.value <= pointY && pointY <= a.height.baseVal.value + a.y.baseVal.value;
return y;
});
const currData = (selOne.length === 1) ? selOne[0].__data__['id'] : null;
dispatch.call('two', d3.selectAll('.rectContainer>rect'), currData)
})
<!-- language: lang-html -->
<!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>
</body>
<script src="min.js">
</script>
</html>
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论