同时具有鼠标事件和栏转换
Having both mouse events and bar transitions
我的意图是创建一个图表,每次绘制/重绘时都有一个条形高度转换,然后一旦显示条形图,就可以显示鼠标事件(mouseenter、mouseleave 和 mousemove)一个工具提示,其中包含您悬停的栏的信息。
我已经做到了鼠标事件可以正常工作,但是我应该在哪里添加过渡?如果我在“.attr("height")”上方添加过渡,则过渡有效,但不会绘制 X 轴和 Y 轴,并且出现错误 "unknown type: mouseenter." 如果我将过渡放在栏选择,我做了 'bar.transition()...,' 过渡不显示,但绘制了 X 轴和 Y 轴并且鼠标事件有效。
我错过了什么吗?我对 D3/ javascript 嵌套函数如何工作的理解是粗制滥造的,因为我们从未在 class 中讨论过它们,所以我很可能忽略了如何应用 D3 属性等的基本原则。
编辑:看看它,我是否应该在
之上声明转换
.attr('height', function (d)...
我应该将点击事件移到底部,这样
bar.on('mouseenter', function (d)...
当前代码:
function drawHistogram(type = 3, title = 'Total Count by Name - Alphabetically') {
var url = "histogramData.tsv";
var margin = 100;
var width = 1000 - (margin * 2);
var height = 500 - (margin * 2);
var x = d3.scaleBand()
.range([0, width])
.padding(0.1); // space between bars
var y = d3.scaleLinear()
.range([height, 0]);
var svg = d3.select('#graphBox').append('svg')
.attr('width', width + margin + margin)
.attr('height', height + margin + margin)
.append('g')
.attr('transform',
'translate(' + margin + ',' + margin + ')');
// add graph title and x/y labels
svg.append('text')
...
var gradient = d3.scaleLinear()
.domain([0, 50])
.range(['rgb(253,203,90)', 'rgb(253,56,170)']);
d3.tsv(url, function (error, data) {
if (error) throw error;
// sort data based on type
if (type == 0) { // name Z to A
data.sort(function (a, b) {
return d3.descending(a.name, b.name)
});
} else if (type == 1) { // count lowest to highest
data.sort(function (a, b) {
return a.count - b.count;
});
} else if (type == 2) { // count highest to lowest
data.sort(function (a, b) {
return b.count - a.count;
});
} // name A to Z
data.forEach(function (d) {
d.count = +d.count;
});
x.domain(data.map(function (d) {
return d.name;
}));
y.domain([0, d3.max(data, function (d) {
return d.count;
})]);
var bar = svg.selectAll('.bar')
.data(data)
.enter()
.append('rect')
.attr('class', 'bar')
.attr('x', function (d) {
return x(d.name);
})
.attr('y', function (d) {
return y(d.count);
})
.attr('width', x.bandwidth())
// This is the transition code, this transition will allow the bars to dropdown.
// If you uncomment this, the bar effect will work as intended, however, my axis
// are no longer drawn and the mouse events do not work. But the transition works.
/*
.transition()
.duration(5000)
.delay(function (d, i) {
return i * 20;
})
*/
.attr('height', function (d) {
return height - y(d.count);
})
// give bars horizontal gradient fill
.style('fill', function (d, i) {
return gradient(i);
})
.on('mouseenter', function (d) {
bar = d3.select(this)
.attr('opacity', 0.5)
tooltip.style('display', null)
.raise()
})
.on('mouseleave', function (d) {
d3.select(this).attr('opacity', 1)
tooltip.style('display', 'none')
})
.on('mousemove', function (d) {
var xPos = d3.mouse(this)[0] + 5;
var yPos = d3.mouse(this)[1] - 40;
tooltip.attr('transform', 'translate(' + xPos + ',' + yPos + ')');
tooltip.select('#name').text('Name: ' + d.name);
tooltip.select('#count').text('Count: ' + d.count);
});
// I tried to add the transition outside of the bar's creation.
// and call it here while adding the transition.
// The transition here does not do anything but the axis are drawn
// and mouse events work properly.
/*
bar.transition()
.duration(5000)
.delay(function (d, i) {
return i * 20;
})
*/
// add x and y axis legends
svg.append('g')
...
// add a clickable line over the x axis to sort chart alphabetically
svg.append('line')
...
// add a clickable line over the y axis to sort chart by value
svg.append('line')
....
});
// tooltip item
var tooltip = svg.append('g')
.attr('class', 'tooltip')
.style('display', 'none');
tooltip.append('rect')
.attr('width', 80)
.attr('height', 35)
.attr('fill', 'white')
.style('opacity', 1);
tooltip.append('text')
.attr('id', 'name')
.attr('x', '0.5em')
.attr('dy', '1.3em')
.style('text-anchor', 'left')
tooltip.append('text')
.attr('id', 'count')
.attr('x', '0.5em')
.attr('dy', '2.3em')
.style('text-anchor', 'left')
}
您不能订阅过渡中的鼠标事件,因此您应该在 .transition 之前添加 .on。
尝试这样的事情:
var bar = svg.selectAll('.bar')
.data(data)
.enter()
.append('rect')
.attr('class', 'bar')
.attr('x', function (d) {
return x(d.name);
})
.attr('y', function (d) {
return y(d.count);
})
.attr('width', x.bandwidth())
.on('mouseenter', function (d) {
bar = d3.select(this)
.attr('opacity', 0.5)
tooltip.style('display', null)
.raise()
})
.on('mouseleave', function (d) {
d3.select(this).attr('opacity', 1)
tooltip.style('display', 'none')
})
.on('mousemove', function (d) {
var xPos = d3.mouse(this)[0] + 5;
var yPos = d3.mouse(this)[1] - 40;
tooltip.attr('transform', 'translate(' + xPos + ',' + yPos + ')');
tooltip.select('#name').text('Name: ' + d.name);
tooltip.select('#count').text('Count: ' + d.count);
})
.transition()
.duration(5000)
.delay(function (d, i) {
return i * 20;
})
.attr('height', function (d) {
return height - y(d.count);
})
// give bars horizontal gradient fill
.style('fill', function (d, i) {
return gradient(i);
});
中有一个小误会:您可以订阅过渡中的鼠标事件,这不是问题。
问题只是方法名称的问题。无论出于何种原因,D3 创建者决定为两种不同的方法赋予相同的名称。因此,这里的on()
:
selection.on
是不是不是这里on()
的方法一样:
transition.on
它们是不同的方法,但名称相同。也就是说,transition.on
不用于鼠标事件(而是 selection.on
),它只接受 4 个类型名称:
"start"
"end"
"interrupt"
"cancel"
这里有一个演示,显示您可以在过渡期间订阅鼠标事件,前提是您使用 selection.on
(在本例中为 d3.select(this).on...
)。将鼠标悬停在黑色矩形上,没有鼠标事件侦听器。转场开始后,鼠标事件监听器起作用:
const rect = d3.select("rect");
rect.transition()
.delay(1000)
.duration(5000)
.attr("width", 300)
.on("start", function() {
d3.select(this).on("mousemove", function() {
console.log("The mouse is over!");
})
})
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg>
<rect width="100" height="150"></rect>
</svg>
或者您也可以使用鲜为人知的 transition.selection()
,其中 returns 对应于过渡的选择。与上面代码片段的不同之处在于,这里会立即附加监听器:
const rect = d3.select("rect");
rect.transition()
.delay(1000)
.duration(5000)
.attr("width", 300)
.selection()
.on("mousemove", function() {
console.log("The mouse is over!");
})
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg>
<rect width="100" height="150"></rect>
</svg>
我的意图是创建一个图表,每次绘制/重绘时都有一个条形高度转换,然后一旦显示条形图,就可以显示鼠标事件(mouseenter、mouseleave 和 mousemove)一个工具提示,其中包含您悬停的栏的信息。
我已经做到了鼠标事件可以正常工作,但是我应该在哪里添加过渡?如果我在“.attr("height")”上方添加过渡,则过渡有效,但不会绘制 X 轴和 Y 轴,并且出现错误 "unknown type: mouseenter." 如果我将过渡放在栏选择,我做了 'bar.transition()...,' 过渡不显示,但绘制了 X 轴和 Y 轴并且鼠标事件有效。
我错过了什么吗?我对 D3/ javascript 嵌套函数如何工作的理解是粗制滥造的,因为我们从未在 class 中讨论过它们,所以我很可能忽略了如何应用 D3 属性等的基本原则。
编辑:看看它,我是否应该在
之上声明转换.attr('height', function (d)...
我应该将点击事件移到底部,这样
bar.on('mouseenter', function (d)...
当前代码:
function drawHistogram(type = 3, title = 'Total Count by Name - Alphabetically') {
var url = "histogramData.tsv";
var margin = 100;
var width = 1000 - (margin * 2);
var height = 500 - (margin * 2);
var x = d3.scaleBand()
.range([0, width])
.padding(0.1); // space between bars
var y = d3.scaleLinear()
.range([height, 0]);
var svg = d3.select('#graphBox').append('svg')
.attr('width', width + margin + margin)
.attr('height', height + margin + margin)
.append('g')
.attr('transform',
'translate(' + margin + ',' + margin + ')');
// add graph title and x/y labels
svg.append('text')
...
var gradient = d3.scaleLinear()
.domain([0, 50])
.range(['rgb(253,203,90)', 'rgb(253,56,170)']);
d3.tsv(url, function (error, data) {
if (error) throw error;
// sort data based on type
if (type == 0) { // name Z to A
data.sort(function (a, b) {
return d3.descending(a.name, b.name)
});
} else if (type == 1) { // count lowest to highest
data.sort(function (a, b) {
return a.count - b.count;
});
} else if (type == 2) { // count highest to lowest
data.sort(function (a, b) {
return b.count - a.count;
});
} // name A to Z
data.forEach(function (d) {
d.count = +d.count;
});
x.domain(data.map(function (d) {
return d.name;
}));
y.domain([0, d3.max(data, function (d) {
return d.count;
})]);
var bar = svg.selectAll('.bar')
.data(data)
.enter()
.append('rect')
.attr('class', 'bar')
.attr('x', function (d) {
return x(d.name);
})
.attr('y', function (d) {
return y(d.count);
})
.attr('width', x.bandwidth())
// This is the transition code, this transition will allow the bars to dropdown.
// If you uncomment this, the bar effect will work as intended, however, my axis
// are no longer drawn and the mouse events do not work. But the transition works.
/*
.transition()
.duration(5000)
.delay(function (d, i) {
return i * 20;
})
*/
.attr('height', function (d) {
return height - y(d.count);
})
// give bars horizontal gradient fill
.style('fill', function (d, i) {
return gradient(i);
})
.on('mouseenter', function (d) {
bar = d3.select(this)
.attr('opacity', 0.5)
tooltip.style('display', null)
.raise()
})
.on('mouseleave', function (d) {
d3.select(this).attr('opacity', 1)
tooltip.style('display', 'none')
})
.on('mousemove', function (d) {
var xPos = d3.mouse(this)[0] + 5;
var yPos = d3.mouse(this)[1] - 40;
tooltip.attr('transform', 'translate(' + xPos + ',' + yPos + ')');
tooltip.select('#name').text('Name: ' + d.name);
tooltip.select('#count').text('Count: ' + d.count);
});
// I tried to add the transition outside of the bar's creation.
// and call it here while adding the transition.
// The transition here does not do anything but the axis are drawn
// and mouse events work properly.
/*
bar.transition()
.duration(5000)
.delay(function (d, i) {
return i * 20;
})
*/
// add x and y axis legends
svg.append('g')
...
// add a clickable line over the x axis to sort chart alphabetically
svg.append('line')
...
// add a clickable line over the y axis to sort chart by value
svg.append('line')
....
});
// tooltip item
var tooltip = svg.append('g')
.attr('class', 'tooltip')
.style('display', 'none');
tooltip.append('rect')
.attr('width', 80)
.attr('height', 35)
.attr('fill', 'white')
.style('opacity', 1);
tooltip.append('text')
.attr('id', 'name')
.attr('x', '0.5em')
.attr('dy', '1.3em')
.style('text-anchor', 'left')
tooltip.append('text')
.attr('id', 'count')
.attr('x', '0.5em')
.attr('dy', '2.3em')
.style('text-anchor', 'left')
}
您不能订阅过渡中的鼠标事件,因此您应该在 .transition 之前添加 .on。
尝试这样的事情:
var bar = svg.selectAll('.bar')
.data(data)
.enter()
.append('rect')
.attr('class', 'bar')
.attr('x', function (d) {
return x(d.name);
})
.attr('y', function (d) {
return y(d.count);
})
.attr('width', x.bandwidth())
.on('mouseenter', function (d) {
bar = d3.select(this)
.attr('opacity', 0.5)
tooltip.style('display', null)
.raise()
})
.on('mouseleave', function (d) {
d3.select(this).attr('opacity', 1)
tooltip.style('display', 'none')
})
.on('mousemove', function (d) {
var xPos = d3.mouse(this)[0] + 5;
var yPos = d3.mouse(this)[1] - 40;
tooltip.attr('transform', 'translate(' + xPos + ',' + yPos + ')');
tooltip.select('#name').text('Name: ' + d.name);
tooltip.select('#count').text('Count: ' + d.count);
})
.transition()
.duration(5000)
.delay(function (d, i) {
return i * 20;
})
.attr('height', function (d) {
return height - y(d.count);
})
// give bars horizontal gradient fill
.style('fill', function (d, i) {
return gradient(i);
});
问题只是方法名称的问题。无论出于何种原因,D3 创建者决定为两种不同的方法赋予相同的名称。因此,这里的on()
:
selection.on
是不是不是这里on()
的方法一样:
transition.on
它们是不同的方法,但名称相同。也就是说,transition.on
不用于鼠标事件(而是 selection.on
),它只接受 4 个类型名称:
"start"
"end"
"interrupt"
"cancel"
这里有一个演示,显示您可以在过渡期间订阅鼠标事件,前提是您使用 selection.on
(在本例中为 d3.select(this).on...
)。将鼠标悬停在黑色矩形上,没有鼠标事件侦听器。转场开始后,鼠标事件监听器起作用:
const rect = d3.select("rect");
rect.transition()
.delay(1000)
.duration(5000)
.attr("width", 300)
.on("start", function() {
d3.select(this).on("mousemove", function() {
console.log("The mouse is over!");
})
})
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg>
<rect width="100" height="150"></rect>
</svg>
或者您也可以使用鲜为人知的 transition.selection()
,其中 returns 对应于过渡的选择。与上面代码片段的不同之处在于,这里会立即附加监听器:
const rect = d3.select("rect");
rect.transition()
.delay(1000)
.duration(5000)
.attr("width", 300)
.selection()
.on("mousemove", function() {
console.log("The mouse is over!");
})
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg>
<rect width="100" height="150"></rect>
</svg>