Skip to content

Instantly share code, notes, and snippets.

@JonathanDn
Last active June 26, 2017 10:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JonathanDn/e31270582e461f36b8ccd519bcfe15f7 to your computer and use it in GitHub Desktop.
Save JonathanDn/e31270582e461f36b8ccd519bcfe15f7 to your computer and use it in GitHub Desktop.
Vanilla JS - Horizontal Scrollable Timeline, with mock date data circles re-rendering when zoom activated & when side-scrolling - change colors according to input v1.0.5.1
<div class="chart"></div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
// Data
let logsMoq = [{
CreationDateTime: "2017-02-14T07:00:44.033Z",
Type: "DUWorkFlowLog",
name: 'Workflow'
},
{
CreationDateTime: "2017-02-14T10:00:44.033Z",
Type: "DUEventDoc",
name: 'Alert'
},
{
CreationDateTime: "2017-02-14T04:00:44.033Z",
Type: "DUToolRunsLog",
name: 'Tool'
},
{
CreationDateTime: "2017-02-14T01:00:44.033Z",
Type: "DUEventDoc",
name: 'Alert'
},
{
CreationDateTime: "2017-02-14T08:30:44.033Z",
Type: "attachment",
name: 'Attachment'
}
];
let colorMap = {
DUEventDoc: 'red',
DUToolRunsLog: 'orange',
DUWorkFlowLog: '#00c2d6',
attachment: 'green'
}
// TODO - make the initial zoom to be maximum zoom in.
// Time Formats
let ts = Math.round(new Date().getTime() / 1000);
let tsYesterday = ts - (24 * 3600);
let tsMonthAgo = ts - (24 * 3000 * 30);
let tsWeekAgo = ts - (24 * 3000 * 7);
let dateNow = new Date(ts * 1000);
let dateTwentyFourHoursAgo = new Date(tsYesterday * 1000);
let dateMonthAgo = new Date(tsMonthAgo * 1000);
let dateWeekAgo = new Date(tsWeekAgo * 1000);
// Domain in Timestamps:
let tsDateNow = Date.parse(dateNow);
let tsDateTwentyFourHoursAgo = Date.parse(dateTwentyFourHoursAgo);
let tsDateMonthAgo = Date.parse(dateMonthAgo);
let tsDateWeekAgo = Date.parse(dateWeekAgo);
let margin = {top: 0, right: 20, bottom: 30, left: 20};
let width = 790 - margin.left - margin.right;
let height = 100 - margin.top - margin.bottom;
// Bar + scale + axis container
let svg = d3.select('.chart')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('class', 'main-container')
.attr('transform', `translate(${margin.left}, ${margin.top})`);
// Bar above scale/axis
let viewBox = svg.append('rect')
.attr('width', width)
.attr('height', height)
.style('fill', 'lightgrey')
.style('cursor', 'move');
// x Scale & Axis - domain in timestamps
let xScale = d3
.scaleTime()
// .scaleUtc()
// .domain([tsDateTwentyFourHoursAgo, tsDateNow])
.domain([tsDateMonthAgo, tsDateNow])
.range([0, width]);
let xAxis = d3.axisBottom(xScale)
//.tickFormat(d3.timeFormat("%I %M %p"));
// Bottom x scale
let gX = svg.append('g')
.attr('transform', `translate(0, ${height})`)
.attr('class', 'x axis')
.call(xAxis);
let circles;
// Moq timestamps of Data - presented in places
// Update these on zoom:
function render(data) {
// JOIN
circles = svg.selectAll("circle")
.data(data);
// UPDATE - add circles
circles
.enter()
.append('g')
.attr('class', 'dot')
.append("circle")
.attr("r", 5)
.attr("cy", height)
.attr("cx", function(d) {
return ( xScale(new Date(d.CreationDateTime).getTime()));
})
.style('fill', (d) => colorMap[d.Type])
.style('stroke', 'black')
.style('stroke-width', 2);
// EXIT
circles.exit().remove();
}
render(logsMoq);
// Zoom Behavior
let zoom = d3.zoom()
// translateExtent - width - in charge of how many alerts can stack inside
.translateExtent([[0, +30], [width * 2, height]])
// scaleExtent - how much zoom scales.
.scaleExtent([1, 100])
.on ("zoom", function() {
// Zoom to a deeper Resolution:
zoomed();
// Re-Render
reRender()
})
function reRender() {
// Preserve logsMoq data
let oldLogsMoq = logsMoq;
// Remove
logsMoq = [];
// Update Original;
logsMoq = oldLogsMoq;
// Get logs back and Re-Render
render(oldLogsMoq);
}
svg.call(zoom);
function zoomed() {
// New Position:
let tX = d3.event.transform.x;
let k = d3.event.transform.k
// Zoom on bar
render(logsMoq);
// Zoom / side-scroll on xAxis
gX.call(xAxis.scale(d3.event.transform.rescaleX(xScale)));
// Draw the CIRCLES in their new position:
circles.attr('cx', _.partial(rePosition, tX, k))
// Draw the circle LINES in their new position:
d3.selectAll('.dot-line')
.attr('x1', _.partial(rePosition, tX, k))
.attr('x2', _.partial(rePosition, tX, k));
// Draw the circle RECTS in their new position:
d3.selectAll('.item-content').attr('x', _.partial(rePosition, tX, k));
// Draw the RECTS - Text in new position
d3.selectAll('.flag-text').attr('x', _.partial(rePosition, tX, k));
}
function rePosition(transitionX, kFactor, data) {
let oldX = xScale(new Date(data.CreationDateTime).getTime());
let newXPosition = (transitionX + kFactor * oldX);
return newXPosition;
}
.chart {
min-width: 790px;
min-height: 100px;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment