gpt4 book ai didi

javascript - d3.js:进入-更新-退出模式 - 旧点未删除

转载 作者:行者123 更新时间:2023-11-29 10:58:37 25 4
gpt4 key购买 nike

我正在尝试在下面的这张图中执行进入-更新-退出模式(这是在 SO 的一些非常善良的人的巨大帮助下构建的,但不幸的是我现在再次陷入困境。我无法使模式工作但是我确定我选择了正确的对象(在下面的代码中名为 heatDotsGroup)。

不过,我可以在 Chrome 的开发人员工具中检查该对象是否包含节点(省略号),但该模式不起作用,因此很明显我做错了什么。

有什么想法吗?非常感谢!

function heatmap(dataset) {

var svg = d3.select("#chart")
.select("svg")

var xLabels = [],
yLabels = [];
for (i = 0; i < dataset.length; i++) {
if (i==0){
xLabels.push(dataset[i].xLabel);
var j = 0;
while (dataset[j+1].xLabel == dataset[j].xLabel){
yLabels.push(dataset[j].yLabel);
j++;
}
yLabels.push(dataset[j].yLabel);
} else {
if (dataset[i-1].xLabel == dataset[i].xLabel){
//do nothing
} else {
xLabels.push(dataset[i].xLabel);
}
}
};

var margin = {top: 0, right: 25,
bottom: 60, left: 75};

var width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;

var dotSpacing = 0,
dotWidth = width/(2*(xLabels.length+1)),
dotHeight = height/(2*yLabels.length);

var daysRange = d3.extent(dataset, function (d) {return d.xKey}),
days = daysRange[1] - daysRange[0];

var hoursRange = d3.extent(dataset, function (d) {return d.yKey}),
hours = hoursRange[1] - hoursRange[0];

var tRange = d3.extent(dataset, function (d) {return d.val}),
tMin = tRange[0],
tMax = tRange[1];

var colors = ['#2C7BB6', '#00A6CA', '#00CCBC', '#90EB9D', '#FFFF8C', '#F9D057', '#F29E2E', '#E76818', '#D7191C'];

// the scale
var scale = {
x: d3.scaleLinear()
.range([-1, width]),
y: d3.scaleLinear()
.range([height, 0]),
};

var xBand = d3.scaleBand().domain(xLabels).range([0, width]),
yBand = d3.scaleBand().domain(yLabels).range([height, 0]);

var axis = {
x: d3.axisBottom(scale.x).tickFormat((d, e) => xLabels[d]),
y: d3.axisLeft(scale.y).tickFormat((d, e) => yLabels[d]),
};


function updateScales(data){
scale.x.domain([0, d3.max(data, d => d.xKey)]),
scale.y.domain([ 0, d3.max(data, d => d.yKey)])
}

var colorScale = d3.scaleQuantile()
.domain([0, colors.length - 1, d3.max(dataset, function (d) {return d.val;})])
.range(colors);

var zoom = d3.zoom()
.scaleExtent([1, dotHeight])
.on("zoom", zoomed);

var tooltip = d3.select("body").append("div")
.attr("id", "tooltip")
.style("opacity", 0);

// SVG canvas
svg = d3.select("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.call(zoom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

// Clip path
svg.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height+dotHeight);


// Heatmap dots
var heatDotsGroup = svg.append("g")
.attr("clip-path", "url(#clip)")
.append("g");


//Create X axis
var renderXAxis = svg.append("g")
.attr("class", "x axis")
//.attr("transform", "translate(0," + scale.y(-0.5) + ")")
//.call(axis.x)

//Create Y axis
var renderYAxis = svg.append("g")
.attr("class", "y axis")
.call(axis.y);


function zoomed() {
d3.event.transform.y = 0;
d3.event.transform.x = Math.min(d3.event.transform.x, 5);
d3.event.transform.x = Math.max(d3.event.transform.x, (1 - d3.event.transform.k) * width);
// console.log(d3.event.transform)

// update: rescale x axis
renderXAxis.call(axis.x.scale(d3.event.transform.rescaleX(scale.x)));

// Make sure that only the x axis is zoomed
heatDotsGroup.attr("transform", d3.event.transform.toString().replace(/scale\((.*?)\)/, "scale($1, 1)"));
}

svg.call(renderPlot, dataset)

function renderPlot(selection, dataset){

//Do the axes
updateScales(dataset)
selection.select('.y.axis').call(axis.y)
selection.select('.x.axis')
.attr("transform", "translate(0," + scale.y(-0.5) + ")")
.call(axis.x)


// Do the chart
const update = heatDotsGroup.selectAll("ellipse")
.data(dataset);

update
.enter()
.append("ellipse")
.attr("cx", function (d) {return scale.x(d.xKey) - xBand.bandwidth();})
.attr("cy", function (d) {return scale.y(d.yKey) + yBand.bandwidth();})
.attr("rx", dotWidth)
.attr("ry", dotHeight)
.attr("fill", function (d) {
return colorScale(d.val);}
)
.merge(update).transition().duration(800);

update.exit().remove();

}


};
#clickMe{
height:50px;
width:150px;
background-color:lavender;
}
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8">

<title>Heatmap Chart</title>

<!-- Reference style.css -->
<!-- <link rel="stylesheet" type="text/css" href="style.css">-->

<!-- Reference minified version of D3 -->
<script src='https://d3js.org/d3.v4.min.js' type='text/javascript'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js'></script>
<script src='heatmap_v4.js' type='text/javascript'></script>
</head>

<body>
<input id="clickMe" type="button" value="click me to push new data" onclick="run();" />

<div id='chart'>
<svg width="700" height="500">
<g class="focus">
<g class="xaxis"></g>
<g class="yaxis"></g>
</g>
</svg>
</div>


<script>
function run() {
var dataset = [];
for (let i = 1; i < 360; i++) { //360
for (j = 1; j < 7; j++) { //75
dataset.push({
xKey: i,
xLabel: "xMark " + i,
yKey: j,
yLabel: "yMark " + j,
val: Math.random() * 25,
})
}
};

heatmap(dataset)
}

$(document).ready(function() {});
</script>
</body>

</html>

最佳答案

问题是您每次运行进入/退出/更新周期时都没有使用相同的选择。当按钮被按下时你:

  1. 生成新数据
  2. 运行热图函数
  3. 热图函数选择 svg 并附加一个名为 heatDotsGroup 的新 g
  4. 调用更新函数并将新创建的g作为选择传递
  5. enter 循环追加所有内容,因为新的 g 是空的。

因此,exit 和 udpate 循环都是空的。尝试:

console.log(update.size(),update.exit().size()) // *Without any merge* 

你应该看到每次更新都是空的。这是因为每次都输入了所有元素,这就是为什么每次更新都会增加省略号的数量。

我已经从热图函数中取出了一堆变量声明和追加语句,这些东西只需要运行一次(我可以走得更远,但我只做了最少的)。我还合并了您的更新并在设置属性之前输入选择(因为我们希望在更新时设置新属性)。下面的代码片段应该演示此更改。

在代码片段中,按下按钮会发生以下情况:

  1. 生成新数据
  2. 运行热图函数
  3. 热图函数选择现有的选择并且不附加任何新内容
  4. 调用更新函数并传递包含任何现有节点的先前更新/进入/退出周期使用的选择。
  5. 更新功能根据现有节点根据需要进入/退出/更新元素。

这是一个基于以上内容的工作版本:

// Things to set/append once:
var svg = d3.select("#chart")
.select("svg")

var margin = {top: 0, right: 25,bottom: 60, left: 75};
var width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;

svg = svg.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var clip = svg.append("clipPath")
.attr("id", "clip")
.append("rect")


var heatDotsGroup = svg.append("g")
.attr("clip-path", "url(#clip)")
.append("g");

var xAxis = svg.append("g").attr("class", "x axis");
var yAxis = svg.append("g").attr("class", "y axis")



function heatmap(dataset) {


var xLabels = [],
yLabels = [];
for (i = 0; i < dataset.length; i++) {
if (i==0){
xLabels.push(dataset[i].xLabel);
var j = 0;
while (dataset[j+1].xLabel == dataset[j].xLabel){
yLabels.push(dataset[j].yLabel);
j++;
}
yLabels.push(dataset[j].yLabel);
} else {
if (dataset[i-1].xLabel == dataset[i].xLabel){
//do nothing
} else {
xLabels.push(dataset[i].xLabel);
}
}
};



var dotSpacing = 0,
dotWidth = width/(2*(xLabels.length+1)),
dotHeight = height/(2*yLabels.length);

var daysRange = d3.extent(dataset, function (d) {return d.xKey}),
days = daysRange[1] - daysRange[0];

var hoursRange = d3.extent(dataset, function (d) {return d.yKey}),
hours = hoursRange[1] - hoursRange[0];

var tRange = d3.extent(dataset, function (d) {return d.val}),
tMin = tRange[0],
tMax = tRange[1];

var colors = ['#2C7BB6', '#00A6CA', '#00CCBC', '#90EB9D', '#FFFF8C', '#F9D057', '#F29E2E', '#E76818', '#D7191C'];

// the scale
var scale = {
x: d3.scaleLinear()
.range([-1, width]),
y: d3.scaleLinear()
.range([height, 0]),
};

var xBand = d3.scaleBand().domain(xLabels).range([0, width]),
yBand = d3.scaleBand().domain(yLabels).range([height, 0]);

var axis = {
x: d3.axisBottom(scale.x).tickFormat((d, e) => xLabels[d]),
y: d3.axisLeft(scale.y).tickFormat((d, e) => yLabels[d]),
};


function updateScales(data){
scale.x.domain([0, d3.max(data, d => d.xKey)]),
scale.y.domain([ 0, d3.max(data, d => d.yKey)])
}

var colorScale = d3.scaleQuantile()
.domain([0, colors.length - 1, d3.max(dataset, function (d) {return d.val;})])
.range(colors);

var zoom = d3.zoom()
.scaleExtent([1, dotHeight])
.on("zoom", zoomed);

var tooltip = d3.select("body").append("div")
.attr("id", "tooltip")
.style("opacity", 0);

// SVG canvas
svg.call(zoom);


// Clip path
clip.attr("width", width)
.attr("height", height+dotHeight);





//Create X axis
var renderXAxis = xAxis
//.attr("transform", "translate(0," + scale.y(-0.5) + ")")
//.call(axis.x)

//Create Y axis
var renderYAxis = yAxis.call(axis.y);


function zoomed() {
d3.event.transform.y = 0;
d3.event.transform.x = Math.min(d3.event.transform.x, 5);
d3.event.transform.x = Math.max(d3.event.transform.x, (1 - d3.event.transform.k) * width);
// console.log(d3.event.transform)

// update: rescale x axis
renderXAxis.call(axis.x.scale(d3.event.transform.rescaleX(scale.x)));

// Make sure that only the x axis is zoomed
heatDotsGroup.attr("transform", d3.event.transform.toString().replace(/scale\((.*?)\)/, "scale($1, 1)"));
}

svg.call(renderPlot, dataset)

function renderPlot(selection, dataset){

//Do the axes
updateScales(dataset)
selection.select('.y.axis').call(axis.y)
selection.select('.x.axis')
.attr("transform", "translate(0," + scale.y(-0.5) + ")")
.call(axis.x)


// Do the chart
const update = heatDotsGroup.selectAll("ellipse")
.data(dataset);

update
.enter()
.append("ellipse")
.merge(update)
.attr("cx", function (d) {return scale.x(d.xKey) - xBand.bandwidth();})
.attr("cy", function (d) {return scale.y(d.yKey) + yBand.bandwidth();})
.attr("rx", dotWidth)
.attr("ry", dotHeight)
.attr("fill", function (d) {
return colorScale(d.val);}
)


update.exit().remove();

}


};
#clickMe{
height:50px;
width:150px;
background-color:lavender;
}
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8">

<title>Heatmap Chart</title>

<!-- Reference style.css -->
<!-- <link rel="stylesheet" type="text/css" href="style.css">-->

<!-- Reference minified version of D3 -->
<script src='https://d3js.org/d3.v4.min.js' type='text/javascript'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js'></script>
<script src='heatmap_v4.js' type='text/javascript'></script>
</head>

<body>
<input id="clickMe" type="button" value="click me to push new data" onclick="run();" />

<div id='chart'>
<svg width="700" height="500">
<g class="focus">
<g class="xaxis"></g>
<g class="yaxis"></g>
</g>
</svg>
</div>


<script>
function run() {
var dataset = [];
for (let i = 1; i < 360; i++) { //360
for (j = 1; j < 7; j++) { //75
dataset.push({
xKey: i,
xLabel: "xMark " + i,
yKey: j,
yLabel: "yMark " + j,
val: Math.random() * 25,
})
}
};

heatmap(dataset)
}

$(document).ready(function() {});
</script>
</body>

</html>

这里的导出选择仍然是空的,因为数据数组的大小是固定的。 D3 假定新数据替换旧数据,但它不知道新数据应该表示为新元素,除非我们当然指定一个关键函数,如现在已删除的注释中所述。这可能是也可能不是您想要的功能。

关于javascript - d3.js:进入-更新-退出模式 - 旧点未删除,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51623912/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com