UNPKG

zui

Version:

一个基于 Bootstrap 深度定制开源前端实践方案,帮助你快速构建现代跨屏应用。

441 lines (368 loc) 18.2 kB
/* ======================================================================== * Chart.js: Chart.line.js [Version: 1.0.2] * http://chartjs.org/ * * ZUI: The file has been changed in ZUI. It will not keep update with the * official version in the future. * http://zui.sexy * ======================================================================== * Copyright 2015 Nick Downie, Released under the MIT license * https://github.com/nnnick/Chart.js/blob/master/LICENSE.md * ======================================================================== */ /// ----- ZUI change begin ----- /// Add jquery object to namespace /// (function(){ // Old code (function($) { /// ----- ZUI change end ----- "use strict"; /// ----- ZUI change begin ----- /// Change root to zui shared object /// /// var root = this, // old code var root = $ && $.zui ? $.zui : this, /// ----- ZUI change end ----- Chart = root.Chart, helpers = Chart.helpers; var defaultConfig = { ///Boolean - Whether grid lines are shown across the chart scaleShowGridLines: true, //String - Colour of the grid lines scaleGridLineColor: "rgba(0,0,0,.05)", //Number - Width of the grid lines scaleGridLineWidth: 1, //Boolean - Whether to show horizontal lines (except X axis) scaleShowHorizontalLines: true, /// ZUI change end //Boolean - Whether to show beyond lines scaleShowBeyondLine: true, /// ZUI change end /// //Boolean - Whether to show vertical lines (except Y axis) scaleShowVerticalLines: true, //Boolean - Whether the line is curved between points bezierCurve: true, //Number - Tension of the bezier curve between points bezierCurveTension: 0.4, //Boolean - Whether to show a dot for each point pointDot: true, //Number - Radius of each point dot in pixels pointDotRadius: 4, //Number - Pixel width of point dot stroke pointDotStrokeWidth: 1, //Number - amount extra to add to the radius to cater for hit detection outside the drawn point pointHitDetectionRadius: 20, //Boolean - Whether to show a stroke for datasets datasetStroke: true, //Number - Pixel width of dataset stroke datasetStrokeWidth: 2, //Boolean - Whether to fill the dataset with a colour datasetFill: true, //String - A legend template legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].strokeColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>" }; Chart.Type.extend({ name: "Line", defaults: defaultConfig, initialize: function(data) { //Declare the extension of the default point, to cater for the options passed in to the constructor this.PointClass = Chart.Point.extend({ strokeWidth: this.options.pointDotStrokeWidth, radius: this.options.pointDotRadius, display: this.options.pointDot, hitDetectionRadius: this.options.pointHitDetectionRadius, ctx: this.chart.ctx, inRange: function(mouseX) { return(Math.pow(mouseX - this.x, 2) < Math.pow(this.radius + this.hitDetectionRadius, 2)); } }); this.datasets = []; //Set up tooltip events on the chart if(this.options.showTooltips) { helpers.bindEvents(this, this.options.tooltipEvents, function(evt) { var activePoints = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : []; this.eachPoints(function(point) { point.restore(['fillColor', 'strokeColor']); }); helpers.each(activePoints, function(activePoint) { activePoint.fillColor = activePoint.highlightFill; activePoint.strokeColor = activePoint.highlightStroke; }); this.showTooltip(activePoints); }); } //Iterate through each of the datasets, and build this into a property of the chart helpers.each(data.datasets, function(dataset) { /// ----- ZUI change begin ----- // add color theme if($.zui && $.zui.Color && $.zui.Color.get) { var accentColor = $.zui.Color.get(dataset.color); var accentColorValue = accentColor.toCssStr(); if(!dataset.fillColor) dataset.fillColor = accentColor.clone().fade(20).toCssStr(); if(!dataset.strokeColor) dataset.strokeColor = accentColorValue; if(!dataset.pointColor) dataset.pointColor = accentColorValue; if(!dataset.pointStrokeColor) dataset.pointStrokeColor = '#fff'; if(!dataset.pointHighlightFill) dataset.pointHighlightFill = '#fff'; if(!dataset.pointHighlightStroke) dataset.pointHighlightStroke = accentColorValue; } /// ----- ZUI change begin ----- var datasetObject = { label: dataset.label || null, fillColor: dataset.fillColor, strokeColor: dataset.strokeColor, pointColor: dataset.pointColor, pointStrokeColor: dataset.pointStrokeColor, /// ZUI change begin showTooltips: dataset.showTooltips !== false, /// ZUI change end points: [] }; this.datasets.push(datasetObject); helpers.each(dataset.data, function(dataPoint, index) { //Add a new point for each piece of data, passing any required data to draw. datasetObject.points.push(new this.PointClass({ value: dataPoint, label: data.labels[index], datasetLabel: dataset.label, strokeColor: dataset.pointStrokeColor, fillColor: dataset.pointColor, highlightFill: dataset.pointHighlightFill || dataset.pointColor, highlightStroke: dataset.pointHighlightStroke || dataset.pointStrokeColor })); }, this); this.buildScale(data.labels); this.eachPoints(function(point, index) { helpers.extend(point, { x: this.scale.calculateX(index), y: this.scale.endPoint }); point.save(); }, this); }, this); this.render(); }, update: function() { this.scale.update(); // Reset any highlight colours before updating. helpers.each(this.activeElements, function(activeElement) { activeElement.restore(['fillColor', 'strokeColor']); }); this.eachPoints(function(point) { point.save(); }); this.render(); }, eachPoints: function(callback) { helpers.each(this.datasets, function(dataset) { helpers.each(dataset.points, callback, this); }, this); }, getPointsAtEvent: function(e) { var pointsArray = [], eventPosition = helpers.getRelativePosition(e); helpers.each(this.datasets, function(dataset) { helpers.each(dataset.points, function(point) { if(point.inRange(eventPosition.x, eventPosition.y)) pointsArray.push(point); }); }, this); return pointsArray; }, buildScale: function(labels) { var self = this; var dataTotal = function() { var values = []; self.eachPoints(function(point) { values.push(point.value); }); return values; }; var scaleOptions = { templateString: this.options.scaleLabel, height: this.chart.height, width: this.chart.width, ctx: this.chart.ctx, textColor: this.options.scaleFontColor, fontSize: this.options.scaleFontSize, fontStyle: this.options.scaleFontStyle, fontFamily: this.options.scaleFontFamily, valuesCount: labels.length, beginAtZero: this.options.scaleBeginAtZero, integersOnly: this.options.scaleIntegersOnly, calculateYRange: function(currentHeight) { var updatedRanges = helpers.calculateScaleRange( dataTotal(), currentHeight, this.fontSize, this.beginAtZero, this.integersOnly ); helpers.extend(this, updatedRanges); }, xLabels: labels, font: helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily), lineWidth: this.options.scaleLineWidth, lineColor: this.options.scaleLineColor, showHorizontalLines: this.options.scaleShowHorizontalLines, showVerticalLines: this.options.scaleShowVerticalLines, /// ZUI change begin showBeyondLine: this.options.scaleShowBeyondLine, /// ZUI change end gridLineWidth: (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0, gridLineColor: (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)", padding: (this.options.showScale) ? 0 : this.options.pointDotRadius + this.options.pointDotStrokeWidth, showLabels: this.options.scaleShowLabels, display: this.options.showScale }; if(this.options.scaleOverride) { helpers.extend(scaleOptions, { calculateYRange: helpers.noop, steps: this.options.scaleSteps, stepValue: this.options.scaleStepWidth, min: this.options.scaleStartValue, max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth) }); } this.scale = new Chart.Scale(scaleOptions); }, addData: function(valuesArray, label) { //Map the values array for each of the datasets helpers.each(valuesArray,function(value,datasetIndex){ //Add a new point for each piece of data, passing any required data to draw. this.datasets[datasetIndex].points.push(new this.PointClass({ value : value, label : label, datasetLabel: this.datasets[datasetIndex].label, x: this.scale.calculateX(this.scale.valuesCount+1), y: this.scale.endPoint, strokeColor : this.datasets[datasetIndex].pointStrokeColor, fillColor : this.datasets[datasetIndex].pointColor })); },this); this.scale.addXLabel(label); //Then re-render the chart. this.update(); }, removeData: function() { this.scale.removeXLabel(); //Then re-render the chart. helpers.each(this.datasets, function(dataset) { dataset.points.shift(); }, this); this.update(); }, reflow: function() { var newScaleProps = helpers.extend({ height: this.chart.height, width: this.chart.width }); this.scale.update(newScaleProps); }, draw: function(ease) { var easingDecimal = ease || 1; this.clear(); var ctx = this.chart.ctx; // Some helper methods for getting the next/prev points var hasValue = function(item) { return item.value !== null; }, nextPoint = function(point, collection, index) { return helpers.findNextWhere(collection, hasValue, index) || point; }, previousPoint = function(point, collection, index) { return helpers.findPreviousWhere(collection, hasValue, index) || point; }; this.scale.draw(easingDecimal); helpers.each(this.datasets, function(dataset) { var pointsWithValues = helpers.where(dataset.points, hasValue); //Transition each point first so that the line and point drawing isn't out of sync //We can use this extra loop to calculate the control points of this dataset also in this loop helpers.each(dataset.points, function(point, index) { if(point.hasValue()) { point.transition({ y: this.scale.calculateY(point.value), x: this.scale.calculateX(index) }, easingDecimal); } }, this); // Control points need to be calculated in a seperate loop, because we need to know the current x/y of the point // This would cause issues when there is no animation, because the y of the next point would be 0, so beziers would be skewed if(this.options.bezierCurve) { helpers.each(pointsWithValues, function(point, index) { var tension = (index > 0 && index < pointsWithValues.length - 1) ? this.options.bezierCurveTension : 0; point.controlPoints = helpers.splineCurve( previousPoint(point, pointsWithValues, index), point, nextPoint(point, pointsWithValues, index), tension ); // Prevent the bezier going outside of the bounds of the graph // Cap puter bezier handles to the upper/lower scale bounds if(point.controlPoints.outer.y > this.scale.endPoint) { point.controlPoints.outer.y = this.scale.endPoint; } else if(point.controlPoints.outer.y < this.scale.startPoint) { point.controlPoints.outer.y = this.scale.startPoint; } // Cap inner bezier handles to the upper/lower scale bounds if(point.controlPoints.inner.y > this.scale.endPoint) { point.controlPoints.inner.y = this.scale.endPoint; } else if(point.controlPoints.inner.y < this.scale.startPoint) { point.controlPoints.inner.y = this.scale.startPoint; } }, this); } //Draw the line between all the points ctx.lineWidth = this.options.datasetStrokeWidth; ctx.strokeStyle = dataset.strokeColor; ctx.beginPath(); helpers.each(pointsWithValues, function(point, index) { if(index === 0) { ctx.moveTo(point.x, point.y); } else { if(this.options.bezierCurve) { var previous = previousPoint(point, pointsWithValues, index); ctx.bezierCurveTo( previous.controlPoints.outer.x, previous.controlPoints.outer.y, point.controlPoints.inner.x, point.controlPoints.inner.y, point.x, point.y ); } else { ctx.lineTo(point.x, point.y); } } }, this); ctx.stroke(); if(this.options.datasetFill && pointsWithValues.length > 0) { //Round off the line by going to the base of the chart, back to the start, then fill. ctx.lineTo(pointsWithValues[pointsWithValues.length - 1].x, this.scale.endPoint); ctx.lineTo(pointsWithValues[0].x, this.scale.endPoint); ctx.fillStyle = dataset.fillColor; ctx.closePath(); ctx.fill(); } //Now draw the points over the line //A little inefficient double looping, but better than the line //lagging behind the point positions helpers.each(pointsWithValues, function(point) { point.draw(); }); }, this); } }); /// ----- ZUI change begin ----- /// Use jquery object to create Chart object $.fn.lineChart = function(data, options) { var lineCharts = []; this.each(function() { var $this = $(this); lineCharts.push(new Chart(this.getContext("2d")).Line(data, $.extend($this.data(), options))); }); return lineCharts.length === 1 ? lineCharts[0] : lineCharts; } /// ----- ZUI change end ----- /// ----- ZUI change begin ----- /// Add jquery object to namespace /// }).call(this); // Old code }).call(this, jQuery); /// ----- ZUI change end -----