zui
Version:
一个基于 Bootstrap 深度定制开源前端实践方案,帮助你快速构建现代跨屏应用。
441 lines (368 loc) • 18.2 kB
JavaScript
/* ========================================================================
* 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 -----