Last active
June 26, 2017 10:53
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.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