问题
I am looking to plot on a timeline a series of events with a start and end date. It seems like the google chart library offers that specific functionality through the Timeline chart type.
I also need to plot single, punctual events (not only events that happen over a begin/end timespan) and represent them with symbols like circles, triangles or other.
I was wondering if it is possible to add another layer on top of the timeline to do that? Or perhaps to add custom shapes to represent events that have matching being/end dates?
Any insight would be great!
回答1:
there are no standard options you can use to add another layer / custom shapes.
but the chart's svg can be modified manually, once the chart's 'ready'
event fires.
see following working snippet,
function addMarkers
will add custom shapes for a given date and row.
pass an array of "events" to be added to the chart.
addMarkers([
{row: 0, date: new Date(currentYear, 1, 11), name: 'Event A', type: 'triangle', color: 'yellow'},
{row: 2, date: new Date(currentYear, 10, 15), name: 'Event B', type: 'circle', color: 'lime'}
]);
google.charts.load('current', {
packages:['timeline']
}).then(function () {
var container = document.getElementById('timeline');
var chart = new google.visualization.Timeline(container);
var dataTable = new google.visualization.DataTable();
dataTable.addColumn({type: 'string', id: 'Row'});
dataTable.addColumn({type: 'string', id: 'Bar'});
dataTable.addColumn({type: 'date', id: 'Start'});
dataTable.addColumn({type: 'date', id: 'End'});
var currentYear = (new Date()).getFullYear();
dataTable.addRows([
['Row 1', 'A-1', new Date(currentYear, 0, 1), new Date(currentYear, 2, 31)],
['Row 1', 'A-2', new Date(currentYear, 3, 1), new Date(currentYear, 5, 30)],
['Row 2', 'B-1', new Date(currentYear, 6, 1), new Date(currentYear, 8, 31)],
['Row 2', 'B-2', new Date(currentYear, 9, 1), new Date(currentYear, 11, 31)]
]);
var dataTableGroup = google.visualization.data.group(dataTable, [0]);
var dateRangeStart = dataTable.getColumnRange(2);
var dateRangeEnd = dataTable.getColumnRange(3);
var rowHeight = 44;
var options = {
height: (dataTableGroup.getNumberOfRows() * rowHeight) + rowHeight
};
function drawChart() {
chart.draw(dataTable, options);
}
// add custom marker
function addMarkers(events) {
var baseline;
var baselineBounds;
var chartElements;
var labelFound;
var labelText;
var marker;
var markerLabel;
var markerSpan;
var rowLabel;
var svg;
var svgNS;
var timeline;
var timelineUnit;
var timelineWidth;
var timespan;
var xCoord;
var yCoord;
// initialize chart elements
baseline = null;
svg = null;
svgNS = null;
timeline = null;
chartElements = container.getElementsByTagName('svg');
if (chartElements.length > 0) {
svg = chartElements[0];
svgNS = svg.namespaceURI;
}
chartElements = container.getElementsByTagName('rect');
if (chartElements.length > 0) {
timeline = chartElements[0];
}
chartElements = container.getElementsByTagName('path');
if (chartElements.length > 0) {
baseline = chartElements[0];
}
if ((svg === null) || (timeline === null) || (baseline === null)) {
return;
}
timelineWidth = parseFloat(timeline.getAttribute('width'));
baselineBounds = baseline.getBBox();
timespan = dateRangeEnd.max.getTime() - dateRangeStart.min.getTime();
timelineUnit = (timelineWidth - baselineBounds.x) / timespan;
// add events
events.forEach(function (event) {
// find row label
rowLabel = dataTable.getValue(event.row, 0);
chartElements = container.getElementsByTagName('text');
if (chartElements.length > 0) {
Array.prototype.forEach.call(chartElements, function(label) {
if (label.textContent.indexOf('…') > -1) {
labelText = label.textContent.replace('…', '');
} else {
labelText = label.textContent;
}
if (rowLabel.indexOf(labelText) > -1) {
markerLabel = label.cloneNode(true);
}
});
}
// calculate placement
markerSpan = event.date.getTime() - dateRangeStart.min.getTime();
// add label
markerLabel.setAttribute('text-anchor', 'start');
markerLabel.setAttribute('fill', event.color);
markerLabel.setAttribute('x', (baselineBounds.x + (timelineUnit * markerSpan) + 6));
markerLabel.textContent = event.name;
svg.appendChild(markerLabel);
// add marker
xCoord = (baselineBounds.x + (timelineUnit * markerSpan) - 4);
yCoord = parseFloat(markerLabel.getAttribute('y'));
switch (event.type) {
case 'triangle':
marker = document.createElementNS(svgNS, 'polygon');
marker.setAttribute('fill', 'transparent');
marker.setAttribute('stroke', event.color);
marker.setAttribute('stroke-width', '3');
marker.setAttribute('points', xCoord + ',' + (yCoord - 10) + ' ' + (xCoord - 5) + ',' + yCoord + ' ' + (xCoord + 5) + ',' + yCoord);
svg.appendChild(marker);
break;
case 'circle':
marker = document.createElementNS(svgNS, 'circle');
marker.setAttribute('cx', xCoord);
marker.setAttribute('cy', yCoord - 5);
marker.setAttribute('r', '6');
marker.setAttribute('stroke', event.color);
marker.setAttribute('stroke-width', '3');
marker.setAttribute('fill', 'transparent');
svg.appendChild(marker);
break;
}
});
}
google.visualization.events.addListener(chart, 'ready', function () {
addMarkers([
{row: 0, date: new Date(currentYear, 1, 11), name: 'Event A', type: 'triangle', color: 'yellow'},
{row: 2, date: new Date(currentYear, 10, 15), name: 'Event B', type: 'circle', color: 'lime'}
]);
});
window.addEventListener('resize', drawChart, false);
drawChart();
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="timeline"></div>
来源:https://stackoverflow.com/questions/54614813/google-charts-add-layer-on-top-of-timeline