Javascript 在D3中缩小时,如何在时间刻度中聚集图标?
我有一个D3(使用D3版本3.5.2)时间刻度图,此时仅使用x轴。该图基于日期数组在x轴上绘制一系列svg“rect”元素,同时将y轴上的数据点随机化,以防止出现聚类问题。图表还具有缩放和平移功能 我想做的是缩小比例后,如下所示: 将所有图标聚集在一起,以便在用户缩小时根据时间线图特定垂直部分的“事件或数据点的集合重叠量”缩小时显示新图标 例如,如果2018年5月有七个数据点聚集在一起,那么它将显示一个图标,图标内显示特定垂直时间片(或2018年5月)的事件数,因此在这种情况下,数字七将出现在聚集图标框中。放大到2018年5月,将导致“聚集图标”消失,并显示整个时间范围内实际显示的七个单独的“rect”元素(星期二、23日、星期四、25日等) 这里棘手的部分是获取特定日期的缩小垂直部分的实际元素数量,并在用户缩小时呈现聚集图标,而在用户放大时则相反(隐藏聚集图标并在时间线上呈现单个图标) 这是我当前的代码:Javascript 在D3中缩小时,如何在时间刻度中聚集图标?,javascript,html,css,d3.js,cluster-analysis,Javascript,Html,Css,D3.js,Cluster Analysis,我有一个D3(使用D3版本3.5.2)时间刻度图,此时仅使用x轴。该图基于日期数组在x轴上绘制一系列svg“rect”元素,同时将y轴上的数据点随机化,以防止出现聚类问题。图表还具有缩放和平移功能 我想做的是缩小比例后,如下所示: 将所有图标聚集在一起,以便在用户缩小时根据时间线图特定垂直部分的“事件或数据点的集合重叠量”缩小时显示新图标 例如,如果2018年5月有七个数据点聚集在一起,那么它将显示一个图标,图标内显示特定垂直时间片(或2018年5月)的事件数,因此在这种情况下,数字七将出现在聚
//D3时间刻度演示
常数宽度=1200,
高度=500,
parsedDate=d3.time.format(“%Y-%m-%d”).parse;
常数更改日期=[
'1988-01-01', '1988-01-02', '1988-01-03',
'1988-01-04', '1988-01-05', '1988-01-06',
'1988-01-07', '1989-01-08', '1989-01-09',
'1995-01-10', '1995-01-11', '1998-01-12',
'1998-01-13', '1998-01-14', '2002-01-15',
'2002-01-16', '2002-01-17', '2004-01-18',
'2004-01-19', '2004-01-20', '2004-01-21',
'2004-01-22', '2007-01-23', '2007-01-24',
'2007-01-25', '2007-01-26', '2007-01-27',
'2008-01-28', '2008-01-29', '2008-01-30',
'2008-01-31', '2008-02-01', '2010-02-02',
'2010-02-03', '2010-02-04', '2012-02-05',
'2012-02-06', '2012-02-07', '2012-02-08',
'2014-02-09', '2014-02-10', '2014-02-11',
'2017-02-12', '2017-02-13', '2017-02-14',
'2018-02-15', '2018-02-16', '2018-02-17',
'2018-02-18', '2018-02-19', '2018-02-20'
].map(d=>parsedDate(d));
const svg=d3。选择(“#时间刻度”)
.append('svg')
.attr('preserveAspectRatio','xMinYMin-meet')
.attr('viewBox','0 0${width}${height}`)
.classed('svg-content',true);
//.attr('width',width)
//.attr('高度'),高度;
const clipPath=svg.append('defs')
.append('clipPath'))
.attr('id','clip')
.append('rect')
.attr('宽度',宽度-110)
.attr('高度'),高度;
const xScale=d3.time.scale()
.domain([新日期(Date.parse(d3.min(changedDates,d=>d))),新日期(Date.parse(d3.max(changedDates,d=>d)))]
.范围([10,宽度-110]);
常数yScale=d3.scale.linear()
.domain([200,0])
.范围([0,高度-29]);
const xAxis=d3.svg.axis()
.scale(xScale)
.尺寸(1)
.orient(“底部”);
const yAxis=d3.svg.axis()
.刻度(yScale)
.尺寸(1)
.tickValue([0100200])
.东方(右);
const zoom=d3.behavior.zoom()
.on('zoom',函数(){
select('g.xaxis')。call(xaxis)。selectAll('text')。style('font-size','10px');
updateEvents();
}).x(xScale);
//绘制要在其上进行交互的基础区域
const rect=svg.append('rect')
.attr('x',0)
.attr('y',0)
.attr('宽度',宽度-100)
.attr('height',height)
.attr('opacity',0)
.呼叫(缩放);
append('g')
.attr('class','xaxis')
.attr('transform','translate('+10+','+480+'))
.呼叫(xAxis)
.selectAll('text')
.style('font-size','10px');
append('g')
.attr('class','yaxis')
.attr('transform','translate('+1100+','+10+'))
.呼叫(yAxis)
.selectAll('text')
.style('font-size','10px');
const renderEvents=日期=>{
const events=svg.selectAll('rect')。数据(日期);
events.enter()
.append('rect')
.attr('类','项')
.attr('x',d=>xScale(d))
.attr('y',()=>Math.random()*100)
.attr('宽度',10)
.attr('height',10)
.attr('transform',(d,i)=>(i==changedDates.length-1)?'translate('+0+','+362+'):'translate('+10+','+362+'))
.attr('clip-path','url(#clip'))
.样式(“填充”、“蓝色”);
events.exit()
.remove();
}
常量更新事件=()=>{
//这里的控制台日志是为了尝试并计算出不同数量的反转x比例值,以尝试并破译元素数量的模式
//需要显示在群集图标框中。
svg.selectAll('rect.item').attr('x',d=>xScale(d)).classed('deleteIcon',d=>{console.log('text d:',Math.floor(xScale(d));});
log(`chart上的元素:${svg.selectAll('rect.item').size()}`);
}
渲染(更改日期)代码>
.svg容器{
显示:内联块;
位置:相对位置;
宽度:100%;
垫底:100%;
垂直对齐:顶部;
溢出:隐藏;
顶部:20px;
}
.svg内容{
显示:内联块;
位置:绝对位置;
排名:0;
左:0;
}
D3时间刻度介绍
我已经实现了一个基于
它在解析期间为日期提供了一个固定的y坐标。
它计算日期的月份组。分组后,组的键是以毫秒为单位的时间,因此使用时必须首先将其转换为日期:new date().setTime(parseInt(d.key))
基于X轴上的月份间隔,它是d
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.axis path {
display: none;
}
.axis line {
stroke-opacity: 0.3;
shape-rendering: crispEdges;
}
.view {
fill: none;
stroke:none;
}
button {
position: absolute;
top: 20px;
left: 20px;
}
</style>
<button>Reset</button>
<div id="timescale" class="svg-container"></div>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
//D3 Timescale demo
const width = 1200,
height = 500,
parsedDate = d3.utcParse("%Y-%m-%d");
const changedDates = [
'1988-01-01', '1988-01-02', '1988-01-03',
'1988-01-04', '1988-01-05', '1988-01-06',
'1988-01-07', '1989-01-08', '1989-01-09',
'1995-01-10', '1995-01-11', '1998-01-12',
'1998-01-13', '1998-01-14', '2002-01-15',
'2002-01-16', '2002-01-17', '2004-01-18',
'2004-01-19', '2004-01-20', '2004-01-21',
'2004-01-22', '2007-01-23', '2007-01-24',
'2007-01-25', '2007-01-26', '2007-01-27',
'2008-01-28', '2008-01-29', '2008-01-30',
'2008-01-31', '2008-02-01', '2010-02-02',
'2010-02-03', '2010-02-04', '2012-02-05',
'2012-02-06', '2012-02-07', '2012-02-08',
'2014-02-09', '2014-02-10', '2014-02-11',
'2017-02-12', '2017-02-13', '2017-02-14',
'2018-02-15', '2018-02-16', '2018-02-17',
'2018-02-18', '2018-02-19', '2018-02-20'
].map(d => { return { date: parsedDate(d), y: Math.random() * 100 + 50 }; });
const svg = d3.select('#timescale')
.append('svg')
.attr('preserveAspectRatio', 'xMinYMin meet')
.attr('viewBox', `0 0 ${width} ${height}`)
.classed('svg-content', true);
// .attr('width', width)
// .attr('height', height);
const clipPath = svg.append('defs')
.append('clipPath')
.attr('id', 'clip')
.append('rect')
.attr('width', width - 110)
.attr('height', height);
var minDate = d3.min(changedDates, d => d.date);
var maxDate = d3.max(changedDates, d => d.date);
minDate.setUTCFullYear(minDate.getUTCFullYear()-1);
maxDate.setUTCFullYear(maxDate.getUTCFullYear()+1);
const xScale = d3.scaleTime()
.domain([minDate, maxDate])
.range([10, width - 110]);
const yScale = d3.scaleLinear()
.domain([200, 0])
.range([0, height - 29]);
const xAxis = d3.axisBottom()
.scale(xScale)
.tickSize(1);
const yAxis = d3.axisRight()
.scale(yScale)
.tickSize(1)
.tickValues([0, 100, 200]);
var view = svg.append("rect")
.attr("class", "view")
.attr("x", 0.5)
.attr("y", 0.5)
.attr("width", width - 109)
.attr("height", height - 28);
var gX = svg.append('g')
.attr('class', 'xaxis')
.attr('transform', `translate(10,${height-20})`)
.call(xAxis);
// .selectAll('text')
// .style('font-size', '10px');
svg.append('g')
.attr('class', 'yaxis')
.attr('transform', 'translate(' + 1100 + ',' + 10 + ')')
.call(yAxis);
// .selectAll('text')
// .style('font-size', '10px');
var zoom = d3.zoom()
.scaleExtent([1, 100])
// .translateExtent([[-100, -100], [width + 90, height + 100]])
.on("zoom", zoomed);
d3.select("button")
.on("click", resetted);
svg.call(zoom);
var points = svg.append("g")
.attr('class', 'points');
var monthCount = d3.nest()
.key(function(d) { return Date.UTC(d.date.getUTCFullYear(), d.date.getUTCMonth(), 1); })
.rollup(function(v) { return { count: v.length, y: Math.random() * 100 + 50 }; })
.entries(changedDates);
function drawDates(dates, xScale) {
var points = svg.select(".points");
points.selectAll(".item").remove();
// use domain to group the dates
var minDate = xScale.domain()[0];
var minDatep1m = new Date(minDate.getTime()+30*24*60*60*1000); // + 1 month
var deltaX = xScale(minDatep1m) - xScale(minDate);
if (deltaX > 20) {
points.selectAll('.item')
.data(dates)
.enter()
.append('rect')
.attr('class', 'item')
.attr('x', d => xScale(d.date))
.attr('y', d => d.y)
.attr('width', 10)
.attr('height', 10)
.attr('clip-path', 'url(#clip)')
.style('fill', 'blue');
} else {
var groups = points.selectAll('.item')
.data(monthCount)
.enter()
.append('g')
.attr('class', 'item')
.attr('transform', d => `translate(${xScale(new Date().setTime(parseInt(d.key)))},${d.value.y})`);
groups.append('rect')
.attr('x', -5)
.attr('y', -5)
.attr('width', 10)
.attr('height', 10)
.attr('clip-path', 'url(#clip)')
.style('fill', 'red');
groups.append('text')
.text(d => d.value.count);
}
}
drawDates(changedDates, xScale);
function zoomed() {
var transformedX = d3.event.transform.rescaleX(xScale);
gX.call(xAxis.scale(transformedX));
drawDates(changedDates, transformedX)
}
function resetted() {
svg.transition()
.duration(750)
.call(zoom.transform, d3.zoomIdentity);
}
</script>