Skip to content

Instantly share code, notes, and snippets.

@markcheno
Created November 18, 2018 04:13
Show Gist options
  • Save markcheno/f776b0bff045c00d90d6a8dcde86d1d1 to your computer and use it in GitHub Desktop.
Save markcheno/f776b0bff045c00d90d6a8dcde86d1d1 to your computer and use it in GitHub Desktop.
D3 Candlestick Chart

D3 Candlestick Chart

Colors borrowed from bitcoin wisdom, but chart engine is custom and using SVG instead of Canvas.

A Pen by kryo on CodePen.

License.

div(id="main")
(function () {
var
utils = window.utilities,
identity = function(v){return v;};
var
PROP_EL = '_el',
PROP_SVG = '_svg',
PROP_DATA = '_data',
PROP_WIDTH = '_width',
PROP_HEIGHT = '_height',
PROP_BAR_SPACING = '_barSpacing',
PROP_DPROP_LOW = '_propertyLow',
PROP_DPROP_HIGH = '_propertyHigh',
PROP_DPROP_VOL = '_propertyVolume',
PROP_DPROP_DATE = '_propertyDate',
PROP_DPROP_CLOSE = '_propertyClose',
PROP_DPROP_OPEN = '_propertyOpen',
PROP_MAR_CANDLE = '_marginCandle',
PROP_MAR_VOLUME = '_marginVolume',
PROP_COL_CANDBG = '_colorCandleBackground',
PROP_COL_BODYDN = '_colorBodyDown',
PROP_COL_BODYUP = '_colorBodyUp',
PROP_COL_STEMDN = '_colorStemDown',
PROP_COL_STEMUP = '_colorStemUp',
PROP_COL_VOLBG = '_colorVolBackground',
PROP_COL_VOLDN = '_colorVolSell',
PROP_COL_VOLUP = '_colorVolBuy',
PROP_COL_VOLDNBORD = '_colorVolSellBorder',
PROP_COL_VOLUPBORD = '_colorVolBuyBorder',
PROP_COL_LABTIMEBG = '_colorLabelTimeBackground',
PROP_COL_LABTIMEFG = '_colorLabelTimeForeground',
PROP_COL_LABCANBG = '_colorLabelCandleBackground',
PROP_COL_LABCANFG = '_colorLabelCandleForeground',
PROP_COL_LABVOLBG = '_colorLabelVolumeBackground',
PROP_COL_LABVOLFG = '_colorLabelVolumeForeground',
PROP_LAB_FAM = '_labelFontFamily',
PROP_LAB_SIZE = '_labelFontSize';
function D3CandleStickChart (el, props) {
this[PROP_WIDTH] = 320;
this[PROP_HEIGHT] = 240;
this[PROP_DATA] = [];
if(window.d3 === undefined) {
throw new Error('Unable to locate the d3 library');
}
props = utils.isPlainObject(props) ? props : {};
// load any data in the props
this.loadData(props.data);
this.width = props.width;
this.height = props.height;
this.parent = el || document.body;
this[PROP_DPROP_LOW] = props.propertyLow || 'low';
this[PROP_DPROP_HIGH] = props.propertyHigh || 'high';
this[PROP_DPROP_VOL] = props.propertyVolume || 'volume';
this[PROP_DPROP_DATE] = props.propertyDate || 'date';
this[PROP_DPROP_CLOSE] = props.propertyClose || 'close';
this[PROP_DPROP_OPEN] = props.propertyOpen || 'open';
this[PROP_BAR_SPACING] = props.barSpacing || 0.35;
this[PROP_MAR_CANDLE] = props.marginCandle || 15;
this[PROP_MAR_VOLUME] = props.marginVolume || 5;
this[PROP_COL_CANDBG] = props.colorCandleBackground || '#111';
this[PROP_COL_BODYDN] = props.colorCandleBodyDown || '#FF2121';
this[PROP_COL_BODYUP] = props.colorCandleBodyUp || this[PROP_COL_CANDBG];
this[PROP_COL_STEMDN] = props.colorCandleStemDown || this[PROP_COL_BODYDN];
this[PROP_COL_STEMUP] = props.colorCandleStemUp || '#1BD31B';
this[PROP_COL_VOLBG] = props.colorVolumeSell || '#222';
this[PROP_COL_VOLDN] = props.colorVolumeBuy || this[PROP_COL_BODYDN];
this[PROP_COL_VOLUP] = props.colorVolumeSellBorder || this[PROP_COL_VOLBG];
this[PROP_COL_VOLDNBORD] = props.colorVolumeBuyBorder || this[PROP_COL_BODYDN];
this[PROP_COL_VOLUPBORD] = props.colorVolumeBackground || this[PROP_COL_STEMUP];
this[PROP_COL_LABTIMEBG] = props.colorLabelTimeBackground || '#333';
this[PROP_COL_LABTIMEFG] = props.colorLabelTimeForeground || '#aaa';
this[PROP_COL_LABCANBG] = props.colorLabelCandleBackground || '#333';
this[PROP_COL_LABCANFG] = props.colorLabelCandleForeground || '#aaa';
this[PROP_COL_LABVOLBG] = props.colorLabelVolumeBackground || '#444';
this[PROP_COL_LABVOLFG] = props.colorLabelVolumeForeground || '#bbb';
this[PROP_LAB_FAM] = props.labelFontFamily || 'Ubuntu';
this[PROP_LAB_SIZE] = props.labelFontSize || 10;
}
function redrawIdent () {
return function (val) {
this.redraw(this.svg, this.data);
return val;
};
}
function redrawGetSetter (prop, allowNull) {
return {
get: utils.getter(prop),
set: utils.setter(prop, allowNull, redrawIdent()),
};
}
Object.defineProperties(D3CandleStickChart.prototype, {
parent: { // auto-install if this gets changed.
get: utils.getter(PROP_EL),
set: utils.setter(PROP_EL, false, function (parent) {
if (parent) {
if (this.parent) { // un-install any previous charts in parent
this.uninstall(this.parent);
}
return this.install(parent);
}
return this.parent;
})
},
svg: {
get: utils.getter(PROP_SVG)
},
width: {
get: utils.getter(PROP_WIDTH),
set: utils.setterInt(PROP_WIDTH, 0)
},
height: {
get: utils.getter(PROP_HEIGHT),
set: utils.setterInt(PROP_HEIGHT, 0)
},
aspectRatio: {
get: function () {
return this.height / this.width;
}
},
data: {
get: utils.getter(PROP_DATA)
},
propertyLow: redrawGetSetter(PROP_DPROP_LOW),
propertyHigh: redrawGetSetter(PROP_DPROP_HIGH),
propertyVolume: redrawGetSetter(PROP_DPROP_VOL),
propertyDate: redrawGetSetter(PROP_DPROP_DATE),
propertyClose: redrawGetSetter(PROP_DPROP_CLOSE),
propertyOpen: redrawGetSetter(PROP_DPROP_OPEN),
barSpacing: redrawGetSetter(PROP_BAR_SPACING),
marginCandle: redrawGetSetter(PROP_MAR_CANDLE),
marginVolume: redrawGetSetter(PROP_MAR_VOLUME),
colorCandleBackground: redrawGetSetter(PROP_COL_CANDBG),
colorCandleBodyDown: redrawGetSetter(PROP_COL_BODYDN),
colorCandleBodyUp: redrawGetSetter(PROP_COL_BODYUP),
colorCandleStemDown: redrawGetSetter(PROP_COL_STEMDN),
colorCandleStemUp: redrawGetSetter(PROP_COL_STEMUP),
colorVolumeSell: redrawGetSetter(PROP_COL_VOLDN),
colorVolumeBuy: redrawGetSetter(PROP_COL_VOLUP),
colorVolumeSellBorder: redrawGetSetter(PROP_COL_VOLDNBORD),
colorVolumeBuyBorder: redrawGetSetter(PROP_COL_VOLUPBORD),
colorVolumeBackground: redrawGetSetter(PROP_COL_VOLBG),
colorLabelTimeBackground: redrawGetSetter(PROP_COL_LABTIMEBG),
colorLabelTimeForeground: redrawGetSetter(PROP_COL_LABTIMEFG),
colorLabelCandleBackground: redrawGetSetter(PROP_COL_LABCANBG),
colorLabelCandleForeground: redrawGetSetter(PROP_COL_LABCANFG),
colorLabelVolumeBackground: redrawGetSetter(PROP_COL_LABVOLBG),
colorLabelVolumeForeground: redrawGetSetter(PROP_COL_LABVOLFG),
labelFontFamily: redrawGetSetter(PROP_LAB_FAM),
labelFontSize: redrawGetSetter(PROP_LAB_SIZE)
});
D3CandleStickChart.low = function (prev, next) {
if(prev === null) return next;
return (next===null) ? prev : Math.min(prev, next);
};
D3CandleStickChart.high = function (prev, next) {
if(prev === null) return next;
return (next===null) ? prev : Math.max(prev, next);
};
D3CandleStickChart.dateMS = function (v) {
if(v instanceof Date) return v.getTime();
if(typeof v === 'string') {
var ms = Date.parse(v);
if(isNaN(ms)) return null;
return ms;
}
if(typeof v === 'number'&&!isNaN(v)) return v;
return null;
};
D3CandleStickChart.lowDate = function (prev, next) {
return D3CandleStickChart.low(prev, D3CandleStickChart.dateMS(next));
};
D3CandleStickChart.highDate = function (prev, next) {
return D3CandleStickChart.high(prev, D3CandleStickChart.dateMS(next));
};
D3CandleStickChart.dateSeriesMinutes = function (ms) {
return moment(ms).format('mm:ss');
};
D3CandleStickChart.dateSeriesHours = function (ms) {
return moment(ms).format('HH:mm');
};
D3CandleStickChart.dateSeriesDays = function (ms) {
return moment(ms).format('MMM, DD');
};
D3CandleStickChart.dateSeriesMonths = function (ms) {
return moment(ms).format('MMM, YYYY');
};
D3CandleStickChart.dateSeriesYears = function (ms) {
return moment(ms).format('MMM, YYYY');
};
D3CandleStickChart.dateSeriesFunctionFromDiff = function (start, end) {
var diff = Math.abs(D3CandleStickChart.dateMS(start) - D3CandleStickChart.dateMS(end));
return (diff >= 3.1104e+10
? D3CandleStickChart.dateSeriesYears : (diff >= 2.592e+9
? D3CandleStickChart.dateSeriesMonths : (diff >= 1.728e+8
? D3CandleStickChart.dateSeriesDays : (diff >= 3.6e+6
? D3CandleStickChart.dateSeriesHours : D3CandleStickChart.dateSeriesMinutes
))));
};
D3CandleStickChart.getScales = function (data, spec) {
spec = spec || {};
var
propLow = spec.propertyLow ||'low',
propHigh = spec.propertyHigh ||'high',
propVol = spec.propertyVolume ||'volume',
propDate = spec.propertyDate ||'date';
return data.reduce(function (p, c) { // find all domains using a single data iteration (optimal)
p.lowestLow = D3CandleStickChart.low(p.lowestLow, c[propLow]);
p.highestHigh = D3CandleStickChart.high(p.highestHigh, c[propHigh]);
p.volumeLow = D3CandleStickChart.low(p.volumeLow, c[propVol]);
p.volumeHigh = D3CandleStickChart.high(p.volumeHigh, c[propVol]);
p.dateLow = D3CandleStickChart.lowDate(p.dateLow, c[propDate]);
p.dateHigh = D3CandleStickChart.highDate(p.dateHigh, c[propDate]);
return p;
}, { // seed the unprocessed domains
lowestLow: null,
highestHigh: null,
volumeLow: null,
volumeHigh: null,
dateLow: null,
dateHigh: null
});
};
D3CandleStickChart.prototype.resize = function (width, height, svg) {
svg = svg || this.svg;
width = width || this.width;
height = height || this.height;
svg.attr('width', width);
svg.attr('height', height);
if(width !== this.width) {
this.width = width;
}
if(height !== this.height) {
this.height = height;
}
return this;
};
D3CandleStickChart.prototype.clearData = function () {
this.data.splice(0, this.data.length);
this.reset();
return true;
};
D3CandleStickChart.prototype.loadData = function (data) {
if(!utils.isArray(data)) return false;
Array.prototype.push.apply(this.data, data);
if(this.parent) this.redraw();
return true;
};
D3CandleStickChart.prototype.reset = function (svg) {
svg = svg || this.svg;
this.resetCandles(svg);
this.resetVolume(svg);
this.resetLabels(svg);
};
D3CandleStickChart.prototype.getScales = function (data) {
return D3CandleStickChart.getScales(data || this.data, {
propertyLow: this.propertyLow,
propertyHigh: this.propertyHigh,
propertyVolume: this.propertyVolume,
propertyDate: this.propertyDate
});
};
D3CandleStickChart.prototype.coordCandles = function () {
var
volcoord = this.coordVolume();
return {
x: volcoord.x,
y: 0,
h: volcoord.y,
w: volcoord.w
};
};
D3CandleStickChart.prototype.resetCandles = function (svg) {
svg.selectAll('g.candles').remove();
return this;
};
D3CandleStickChart.prototype.redrawCandles = function (svg, scales) {
var
propLow = this.propertyLow,
propHigh = this.propertyHigh,
propVol = this.propertyVolume,
propDate = this.propertyDate,
propClose = this.propertyClose,
propOpen = this.propertyOpen,
colorBG = this.colorCandleBackground,
colorBodyDown = this.colorCandleBodyDown,
colorBodyUp = this.colorCandleBodyUp,
colorStemDown = this.colorCandleStemDown,
colorStemUp = this.colorCandleStemUp,
margin = this.marginCandle || 0,
coord = this.coordCandles(),
approxW = (coord.w / this.data.length),
barSpace = approxW * this.barSpacing,
barWidth = approxW - barSpace,
stemWidth = barWidth * 0.15,
group = svg.append('g')
.attr('class', 'candles')
.attr('transform', 'translate('+[coord.x, coord.y].join(' ') +')'),
calcDateX = d3.scale.linear()
.domain([scales.dateLow, scales.dateHigh])
.range([0, coord.w - barWidth]),
calcRY = d3.scale.linear()
.domain([scales.lowestLow, scales.highestHigh])
.range([coord.h - margin, margin]);
if(stemWidth < 1) {
stemWidth = 1;
}
if(barWidth < 1) {
barWidth = 1;
}
group.append('rect')
.attr('class', 'background')
.attr('width', coord.w)
.attr('height', coord.h)
.attr('fill', colorBG);
var
dOpenCloseMin = function (d) { return Math.min(d[propClose], d[propOpen]); },
dOpenCloseMax = function (d) { return Math.max(d[propClose], d[propOpen]); },
dIsDown = function (d) { return d[propClose] < d[propOpen]; },
dStemColor = function (d) { return dIsDown(d) ? colorStemDown : colorStemUp; },
dBodyColor = function (d) { return dIsDown(d) ? colorBodyDown : colorBodyUp; };
if(stemWidth > 0) {
group.selectAll('rect.candle-stem')
.data(this.data)
.enter().append('rect')
.attr('class', 'candle-stem')
.attr('width', stemWidth)
.attr('x', function (d) { return calcDateX(d[propDate]) + ((barWidth/2) - (stemWidth/2)); })
.attr('y', function (d) {
return calcRY(d[propHigh]);
})
.attr('height', function (d) {
return calcRY(d[propLow]) - calcRY(d[propHigh]);
})
.attr('fill', dStemColor);
}
group.selectAll('rect.candle-body')
.data(this.data)
.enter().append('rect')
.attr('class', 'candle-body')
.attr('width', barWidth)
.attr('vmin', dOpenCloseMin)
.attr('vmax', dOpenCloseMax)
.attr('x', function (d) { return calcDateX(d[propDate]); })
.attr('y', function (d) { return calcRY(this.getAttribute('vmax')); })
.attr('height', function (d) {
return calcRY(this.getAttribute('vmin'))-calcRY(this.getAttribute('vmax'));
})
.attr('stroke-width', 0.5)
.attr('stroke-linecap', 'butt')
.attr('stroke-rendering', 'crispEdges')
.attr('stroke', dStemColor)
.attr('fill', dBodyColor);
return svg;
};
D3CandleStickChart.prototype.coordVolume = function () {
var
volheight = 100,
coordlabels = this.coordLabels(),
coordSX = coordlabels.seriesX;
if(volheight / this.height > 0.25) {
volheight = 50;
}
return {
x: coordSX.x,
y: coordSX.y - volheight,
w: coordSX.w,
h: volheight
};
};
D3CandleStickChart.prototype.resetVolume = function (svg) {
svg.selectAll('g.volume').remove();
return this;
};
D3CandleStickChart.prototype.redrawVolume = function (svg, scales) {
var
coord = this.coordVolume(),
propVol = this.propertyVolume,
propDate = this.propertyDate,
propClose = this.propertyClose,
propOpen = this.propertyOpen,
colorVolSell = this.colorVolumeSell,
colorVolBuy = this.colorVolumeBuy,
colorVolSellBorder = this.colorVolumeSellBorder,
colorVolBuyBorder = this.colorVolumeBuyBorder,
colorBG = this.colorVolumeBackground,
approxW = (coord.w / this.data.length),
barSpace = approxW * this.barSpacing,
barWidth = approxW - barSpace,
margin = this.marginVolume || 0;
if(barWidth < 1) {
barWidth = 1;
}
var
group = svg.append('g')
.attr('class', 'volume')
.attr('transform', 'translate('+[coord.x, coord.y].join(' ') +')'),
calcDateX = d3.scale.linear()
.domain([scales.dateLow, scales.dateHigh])
.range([0, coord.w - barWidth]),
calcVolHeight = d3.scale.linear()
.domain([scales.volumeLow, scales.volumeHigh])
.range([2, coord.h - margin]),
dIsDown = function (d) { return d[propClose] < d[propOpen]; },
dColorBars = function (d) { return dIsDown(d) ? colorVolSell : colorVolBuy; },
dColorBarsBord = function (d) { return dIsDown(d) ? colorVolSellBorder : colorVolBuyBorder; };
group.append('rect')
.attr('class', 'background')
.attr('width', coord.w)
.attr('height', coord.h)
.attr('fill', colorBG);
group.selectAll('rect.volume-bar')
.data(this.data)
.enter().append('rect')
.attr('class', 'volume-bar')
.attr('fill', dColorBars)
.attr('stroke', dColorBarsBord)
.attr('width', barWidth)
.attr('stroke-width', 0.5)
.attr('stroke-linecap', 'butt')
.attr('stroke-rendering', 'crispEdges')
.attr('height', function (d) {
return calcVolHeight(d[propVol]);
})
.attr('x', function (d) {
return calcDateX(d[propDate]);
})
.attr('y', function (d) {
return (coord.h - this.getAttribute('height'));
});
return svg;
};
D3CandleStickChart.prototype.coordLabels = function () {
var
xheight = 20,
ywidth = 75;
return {
seriesX: {
x: 0,
y: this.height - xheight,
w: this.width - ywidth,
h: xheight
},
seriesY: {
x: this.width - ywidth,
y: 0,
w: ywidth,
h: this.height
}
};
};
D3CandleStickChart.prototype.resetLabels = function (svg) {
svg.selectAll('g.labels').remove();
return this;
};
D3CandleStickChart.prototype.redrawLabels = function (svg, scales) {
var
coord = this.coordLabels(),
coordvol = this.coordVolume(),
coordcand = this.coordCandles(),
fontFamily = this.labelFontFamily,
fontSize = this.labelFontSize,
colorTimeBG = this.colorLabelTimeBackground,
colorTimeFG = this.colorLabelTimeForeground,
colorCandleBG = this.colorLabelCandleBackground,
colorCandleFG = this.colorLabelCandleForeground,
colorVolumeBG = this.colorLabelVolumeBackground,
colorVolumeFG = this.colorLabelVolumeForeground,
marginCandle = this.marginCandle || 0,
marginVolume = this.marginVolume || 0,
cSX = coord.seriesX,
cSY = coord.seriesY,
groupY = svg.append('g')
.attr('class', 'labels label-y')
.attr('transform', 'translate('+[cSY.x, cSY.y].join(' ') +')'),
groupX = svg.append('g')
.attr('class', 'labels label-x')
.attr('transform', 'translate('+[cSX.x, cSX.y].join(' ') +')'),
// time series data & functions:
timeticks = 10,
dateX = d3.scale.linear()
.domain([scales.dateLow, scales.dateHigh])
.range([cSX.x, cSX.x + cSX.w]),
// candle data & functions:
candticks = 6,
candY = d3.scale.linear()
.domain([scales.lowestLow, scales.highestHigh])
.range([coordcand.y + coordcand.h - marginCandle, coordcand.y + marginCandle]),
// volume data & functions:
volticks = 5,
volY = d3.scale.linear()
.domain([scales.volumeLow, scales.volumeHigh])
.range([coordvol.y + coordvol.h, coordvol.y + fontSize + marginVolume]);
groupX.append('rect') // placeholder for time-series labels
.attr('width', cSX.w)
.attr('height', cSX.h)
.attr('fill', colorTimeBG);
groupY.append('rect') // placeholder for X labels (end-cap)
.attr('y', cSX.y)
.attr('width', cSY.w)
.attr('height', cSX.h)
.attr('fill', colorTimeBG);
groupX.selectAll('text.series-x')
.data(dateX.ticks(timeticks))
.enter().append('svg:text')
.attr('class', 'series-x')
.attr('x', dateX)
.attr('y', 0)
.attr('dy', (cSX.h/2)+(fontSize/2))
.attr('font-family', fontFamily)
.attr('font-size', fontSize)
.attr('text-anchor', 'middle')
.attr('fill', colorTimeFG)
.text(D3CandleStickChart.dateSeriesFunctionFromDiff(scales.dateLow, scales.dateHigh));
// candle labels
groupY.append('rect') // background
.attr('y', coordcand.y)
.attr('width', cSY.w)
.attr('height', coordcand.h)
.attr('fill', colorCandleBG);
groupY.selectAll('text.candle-label') // ticks
.data(candY.ticks(candticks))
.enter().append('text')
.attr('class', 'candle-label')
.attr('x', 0)
.attr('y', candY)
.attr('dx', cSY.w/2)
// .attr('dy', fontSize/2)
.attr('font-family', fontFamily)
.attr('font-size', fontSize)
.attr('text-anchor', 'middle')
.attr('fill', colorCandleFG)
.text(function (d) {
return utils.isNumber(d) ? ( d < 1 ? d.toFixed(4) : d) : d;
});
// volume labels:
groupY.append('rect') // background
.attr('y', coordvol.y)
.attr('width', cSY.w)
.attr('height', coordvol.h)
.attr('fill', colorVolumeBG);
groupY.selectAll('text.vol-label') // ticks
.data(volY.ticks(volticks))
.enter().append('text')
.attr('class', 'vol-label')
.attr('x', 0)
.attr('y', volY)
.attr('dx', cSY.w/2)
.attr('dy', 0)
.attr('fill', colorVolumeFG)
.attr('font-family', fontFamily)
.attr('font-size', fontSize)
.attr('text-anchor', 'middle')
.text(String);
return svg;
};
D3CandleStickChart.prototype.redraw = function (svg, data) {
svg = svg || this.svg;
data = data || this.data;
// clear existing charted data
this.reset(svg);
if(!data.length) { // bail out if no data
return svg;
}
// resize SVG to outer limits
svg.attr('width', this.width);
svg.attr('height', this.height);
svg.attr('viewBox', '0 0 '+ this.width + ' ' + this.height);
var
/***** scales:
* lowestLow
* highestHigh
* volumeLow
* volumeHigh
* dateLow
* dateHigh
*****/
scales = this.getScales(data);
this.redrawCandles(svg, scales);
this.redrawVolume(svg, scales);
this.redrawLabels(svg, scales);
};
D3CandleStickChart.prototype.initView = function (svg) {
this.resize(this.width, this.height, svg);
this.redraw(svg, this.data);
return svg;
};
D3CandleStickChart.prototype.install = function (parent) {
parent = parent || this.parent;
if(!parent) {
return false;
}
// set the parent's svg context
this[PROP_SVG] = this.initView(d3.select(parent).append('svg:svg'));
return parent;
};
D3CandleStickChart.prototype.uninstall = function (parent) {
d3.select(parent||this.parent)
.select('svg')
.remove();
return parent;
};
this.D3CandleStickChart = D3CandleStickChart;
}).call(this);
//
// Test code
//
(function (el) {
if(!el) throw new Error('Unable to find main canvas.');
var
chart = new window.D3CandleStickChart(el, {}),
// data loading stuff
msPerSec = 1000,
pair = 'BTC_BTS',
period = 14400,
num = 100,
epocEnd = Math.round(Date.now()/msPerSec),
epocStart = Math.round(epocEnd - (period * num));
function reloadData() {
$.ajax({ // fetch some chart data
url: 'https://poloniex.com/public?command=returnChartData&currencyPair='+pair+'&start='+epocStart+'&end='+epocEnd+'&period='+period,
json: true,
success: function (data) {
chart.clearData();
chart.loadData(data.map(function (record) {
record.date *= msPerSec;
return record;
}));
}
});
}
reloadData();
setInterval(reloadData, Math.round((period * msPerSec)/2));
(window.onresize = function () {
chart.resize(window.innerWidth, window.innerHeight);
chart.redraw();
})();
})(document.getElementById('main'))
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.3/moment.min.js"></script>
<script src="https://cdn.rawgit.com/kryo2k/utils.js/master/index.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
@import url(https://fonts.googleapis.com/css?family=Ubuntu);
@import url(https://fonts.googleapis.com/css?family=Roboto);
html,
body {
overflow: hidden;
margin: 0;
padding: 0;
height: 100%
}
body {
display: flex;
justify-content: center;
align-items: center;
background: #111;
}
svg {
// outline: 1px dashed white;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment