transcend-charts
Version:
Transcend is a charting and graph library for NUVI
431 lines (377 loc) • 14.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _Numbers = require('../helpers/Numbers');
var _Numbers2 = _interopRequireDefault(_Numbers);
var _Charts = require('../helpers/Charts');
var _Charts2 = _interopRequireDefault(_Charts);
var _Color = require('./Color');
var _Color2 = _interopRequireDefault(_Color);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var GroupedBarLine = function GroupedBarLine(htmlContainer, graphData, graphOptions, percentOfWidth) {
var self = this;
var htmlCanvas = null;
var data = null;
var plotVars = {
graphArea: { top: 0, right: 0, bottom: 0, left: 0, width: 0, height: 0 } }; // this var is updated regularly in the render function to ensure it always contains correct data about the size and state of the graph
var DEBUG = false;
var fullScreenChangeListener;
var webkitFullScreenChangeListener;
var mozFullScreenChangeListener;
var msFullScreenChangeListener;
var resizeListener;
var isDestroyed = false;
var resizeTimer = null;
var options = graphOptions;
// background and padding
if (!options.backgroundColor) {
options.backgroundColor = new _Color2.default.Color('transparent');
} else {
options.backgroundColor = new _Color2.default.Color(options.backgroundColor);
}
if (!options.padding) {
options.padding = 0;
} else {
options.padding = parseFloat(options.padding);
}
// segment spacing
if (!options.barSpacing && options.barSpacing !== 0) {
options.barSpacing = 1; // as a percent of the width of each bar
} else {
options.barSpacing = _Numbers2.default.makeNumber(options.barSpacing);
}
// label font
if (!options.barLabelFontColor) {
options.barLabelFontColor = new _Color2.default.Color('#ffffff');
} else {
options.barLabelFontColor = new _Color2.default.Color(options.barLabelFontColor);
}
if (!options.barLabelFontFamily) {
options.barLabelFontFamily = 'Arial';
}
// values floating on top of each bar
if (options.showValues === undefined || options.showValues === false) {
options.showValues = false;
} else {
options.showValues = true;
}
if (!options.valueFontColor) {
options.valueFontColor = new _Color2.default.Color('#444444');
}
if (!options.valueFontFamily) {
options.valueFontFamily = 'Arial';
}
if (!options.valueFontWeight) {
options.valueFontWeight = '400';
}
// stripes on the bars
if (!options.barStripeSpacing) {
options.barStripeSpacing = 10;
} else {
options.barStripeSpacing = parseFloat(options.barStripeSpacing);
}
if (!options.barStripeWidth) {
options.barStripeWidth = 2;
} else {
options.barStripeWidth = parseFloat(options.barStripeWidth);
}
if (!options.barStripeType) {
options.barStripeType = 'none';
}
if (['none', 'horizontal', 'vertical', 'diagonal'].indexOf(options.barStripeType) === -1) {
options.barStripeType = 'none';
}
if (!options.barStripeColor) {
options.barStripeColor = new _Color2.default.Color('#666666');
} else {
options.barStripeColor = new _Color2.default.Color(options.barStripeColor);
}
if (!options.barFillColor) {
options.barFillColor = new _Color2.default.Color('#666666');
} else {
options.barFillColor = new _Color2.default.Color(options.barFillColor);
}
if (!percentOfWidth) {
percentOfWidth = 1;
} else if (typeof percentOfWidth === 'string' && percentOfWidth.indexOf('%') !== -1) {
percentOfWidth = parseFloat(percentOfWidth) / 100;
} else {
percentOfWidth = parseFloat(percentOfWidth);
}
var barLabelSpacing = 8; // the spacing between the labels and the left of the bar
var barValueSpacing = 4; // the spacing
var pxRatio = window.devicePixelRatio || 1;
this.render = function () {
if (!data) {
return false;
}
var ctx = htmlCanvas.getContext('2d');
// upscale this thang if the device pixel ratio is higher than 1
var backingStoreRatio = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1;
ctx.save();
if (pxRatio > 1) {
ctx.scale(pxRatio / backingStoreRatio, pxRatio / backingStoreRatio);
}
var canvasWidth = htmlCanvas.width / (pxRatio / backingStoreRatio);
var canvasHeight = htmlCanvas.height / (pxRatio / backingStoreRatio);
// fill background
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
if (options.backgroundColor.toString() !== 'transparent') {
ctx.fillStyle = options.backgroundColor.toString();
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
}
// calculate grid area
plotVars.graphArea = {
left: options.padding,
top: options.padding,
right: options.padding,
bottom: options.padding
};
plotVars.graphArea.height = canvasHeight - plotVars.graphArea.top - plotVars.graphArea.bottom;
var allowedBarHeight = plotVars.graphArea.height / data.length;
var barHeight = allowedBarHeight / (1 + options.barSpacing);
var barGap = allowedBarHeight - barHeight;
var fontSize = barHeight;
ctx.font = fontSize + 'px ' + options.barLabelFontFamily;
// calculate the width of widest label
var widestLabel = _lodash2.default.max(data, function (segment) {
return ctx.measureText(segment.name).width;
}).name;
var widestLabelWidth = ctx.measureText(widestLabel).width;
plotVars.graphArea.left = options.padding + widestLabelWidth + barLabelSpacing;
plotVars.graphArea.width = canvasWidth - plotVars.graphArea.left - plotVars.graphArea.right;
var total = _lodash2.default.sum(data, function (datum) {
return datum.value;
});
var maxValue = _lodash2.default.max(data, function (datum) {
return datum.value;
}).value;
var maxWidth = plotVars.graphArea.width * percentOfWidth;
ctx.textBaseline = 'top';
var somethingIsHighlighted = data.some(function (segment) {
return segment.isHighlighted;
});
// draw bars
for (var i = 0; i < data.length; i++) {
// bar
var fillColor = options.barFillColor;
if (data[i].barFillColor) {
fillColor = data[i].barFillColor;
}
if (somethingIsHighlighted && !data[i].isHighlighted) {
fillColor = new _Color2.default.Color(fillColor).fade(0.7);
}
ctx.fillStyle = fillColor.toString();
var barWidth = data[i].value / maxValue * maxWidth;
if (!barWidth) {
barWidth = 0.5;
}
var bar_x = plotVars.graphArea.left;
var bar_y = plotVars.graphArea.top + i * allowedBarHeight + barGap / 2;
ctx.textAlign = 'left';
data[i].boundingBox = { x: bar_x, y: bar_y, width: barWidth, height: barHeight };
ctx.fillRect(bar_x, bar_y, barWidth, barHeight);
// bar value
if (options.showValues) {
var displayValue = data[i].prefix + data[i].value + data[i].suffix;
if (data[i].displayValue) {
displayValue = data[i].displayValue;
}
ctx.fillStyle = options.valueFontColor;
var segmentValueWidth = ctx.measureText(displayValue).width;
if (segmentValueWidth < barWidth - barValueSpacing * 2) {
ctx.textAlign = 'right';
var value_x = plotVars.graphArea.left + barWidth - barValueSpacing;
ctx.fillText(displayValue, value_x, bar_y);
} else {
ctx.fillStyle = new _Color2.default.Color(options.barLabelFontColor).fade(0.3);
ctx.textAlign = 'left';
var value_x = plotVars.graphArea.left + barWidth + barValueSpacing;
ctx.fillText(displayValue, value_x, bar_y);
}
}
// label on left
ctx.font = '300 ' + fontSize + 'px ' + options.barLabelFontFamily;
ctx.fillStyle = options.barLabelFontColor.toString();
ctx.textAlign = 'right';
var label_x = plotVars.graphArea.left - barLabelSpacing;
ctx.fillText(data[i].name, label_x, bar_y);
}
ctx.restore();
};
/***
* Initialize some basic vars including the html canvas to render on
***/
function _init() {
var _this = this;
htmlCanvas = document.createElement('CANVAS');
htmlContainer.appendChild(htmlCanvas);
self.setData(graphData);
fillParent.call(this);
htmlCanvas.addEventListener('mouseout', function (event) {
handleMouseOut.call(_this, event);
});
htmlCanvas.addEventListener('mousemove', function (event) {
var rect = htmlCanvas.getBoundingClientRect();
var pt = {
x: event.clientX - rect.left,
y: event.clientY - rect.top
};
handleMouseMove.call(_this, event, pt);
});
htmlCanvas.addEventListener('mouseup', function (event) {
var rect = htmlCanvas.getBoundingClientRect();
var pt = {
x: event.clientX - rect.left,
y: event.clientY - rect.top
};
handleMouseUp.call(_this, event, pt);
});
resizeListener = window.addEventListener('resize', function () {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(function () {
fillParent.call(_this);
}, 50);
});
fullScreenChangeListener = window.addEventListener('webkitfullscreenchange', fullscreenChange);
webkitFullScreenChangeListener = window.addEventListener('fullscreenchange', fullscreenChange);
mozFullScreenChangeListener = window.addEventListener('mozfullscreenchange', fullscreenChange);
msFullScreenChangeListener = window.addEventListener('msfullscreenchange', fullscreenChange);
function fullscreenChange(event) {
var fullscreenElement = document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement;
if (!fullscreenElement) {
// exiting full screen
var targetElement = event.target;
// if this element contains a canvas, make it tiny so we don't mess up the size of its parent
// don't worry, the window.resize event that fires next will make it fill its parent just fine
var canvases = targetElement.getElementsByTagName('CANVAS');
for (var c = 0; c < canvases.length; c++) {
if (canvases[c] === htmlCanvas) {
canvases[c].width = 1;
canvases[c].height = 1;
canvases[c].style.width = '1px';
canvases[c].style.height = '1px';
break;
}
}
}
}
}
function handleMouseMove(event, pt) {
var oldHighlightedBar = data.find(function (bar) {
return bar.isHighlighted;
});
var highlightedBar = hitTestBars(pt, function (bar) {
bar.isHighlighted = true;
}, function (bar) {
bar.isHighlighted = false;
});
if (highlightedBar !== oldHighlightedBar) {
this.render();
}
}
function handleMouseOut() {
var wasSomethingHighlighted = data.some(function (bar) {
return bar.isHighlighted;
});
data.forEach(function (bar) {
bar.isHighlighted = false;
});
if (wasSomethingHighlighted) {
this.render();
}
}
function handleMouseUp(event, pt) {
var highlightedBar = hitTestBars(pt, null, null);
if (highlightedBar && highlightedBar.onClick) {
highlightedBar.onClick(event);
}
}
function hitTestBars(pt, onHit, onMiss) {
var hoveredBar = null;
for (var i = 0; i < data.length; i++) {
if (pt.x >= data[i].boundingBox.x && pt.x < data[i].boundingBox.x + data[i].boundingBox.width && pt.y >= data[i].boundingBox.y && pt.y < data[i].boundingBox.y + data[i].boundingBox.height) {
hoveredBar = data[i];
if (onHit) {
onHit(data[i]);
}
} else {
if (onMiss) {
onMiss(data[i]);
}
}
}
return hoveredBar;
}
/***
* Fills the parent container with this canvas element
***/
function fillParent() {
if (htmlCanvas && htmlCanvas.parentNode) {
var style = window.getComputedStyle(htmlCanvas.parentNode);
var width = htmlCanvas.parentNode.offsetWidth - parseFloat(style.paddingLeft) - parseFloat(style.paddingRight);
var height = htmlCanvas.parentNode.offsetHeight - parseFloat(style.paddingTop) - parseFloat(style.paddingBottom);
// upscale this thang if the device pixel ratio is higher than 1
htmlCanvas.width = width * pxRatio;
htmlCanvas.height = height * pxRatio;
htmlCanvas.style.width = width + 'px';
htmlCanvas.style.height = height + 'px';
this.render();
}
}
/***
* This function sets/resets the data for the graph
***/
this.setData = function (someData) {
data = someData.slice();
if (data && data.length) {
for (var i = 0; i < data.length; i++) {
var parts = _Numbers2.default.separateNumberUnits(data[i].value);
if (data[i].prefix === undefined) {
data[i].prefix = parts.prefix;
}
if (data[i].suffix === undefined) {
data[i].suffix = parts.suffix;
}
data[i].value = parts.value;
if (data[i].barFillColor) {
data[i].barFillColor = new _Color2.default.Color(data[i].barFillColor);
}
if (data[i].barBorderColor) {
data[i].barBorderColor = new _Color2.default.Color(data[i].barBorderColor);
}
if (data[i].barStripeColor) {
data[i].barStripeColor = new _Color2.default.Color(data[i].barStripeColor);
}
}
//console.log(data);
}
};
this.destroy = function () {
if (fullScreenChangeListener) {
window.removeEventListener('fullscreenchange', fullScreenChangeListener);
}
if (webkitFullScreenChangeListener) {
window.removeEventListener('webkitfullscreenchange', webkitFullScreenChangeListener);
}
if (mozFullScreenChangeListener) {
window.removeEventListener('mozfullscreenchange', mozFullScreenChangeListener);
}
if (msFullScreenChangeListener) {
window.removeEventListener('msfullscreenchange', msFullScreenChangeListener);
}
if (resizeListener) {
window.removeEventListener('resize', resizeListener);
}
if (htmlCanvas && htmlCanvas.parentNode) {
htmlCanvas.parentNode.removeChild(htmlCanvas);
}
isDestroyed = true;
};
// Initialize
_init.call(this);
};
exports.default = GroupedBarLine;