123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461 |
- <!DOCTYPE html>
- <html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
- <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
- <script>
- angular.module("ngRadialGauge",[]).directive('ngRadialGauge', ['$window', '$timeout',
- function ($window, $timeout) {
- return {
- restrict: 'EAC',
- scope: {
- data: '=',
- lowerLimit: '=',
- upperLimit: '=',
- ranges: '=',
- value: '=',
- valueUnit: '=',
- precision: '=',
- majorGraduationPrecision: '=',
- label: '=',// MODIFIED
- onClick: '&'
- },
- link: function (scope, ele, attrs) {
- var defaultUpperLimit = 100;
- var defaultLowerLimit = 0;
- var initialized = false;
- var renderTimeout;
- var gaugeAngle = parseInt(attrs.angle) || 120;
- //New width variable, now works in conjunction with fixed viewBox sizing
- var _width = attrs.width || "100%";
- /* Colin Bester
- Width and height are not really such an issue with SVG but choose these values as
- width of 300 seems to be pretty baked into code.
- I took the easy path seeing as size is not that relevant and hard coded width and height
- as I was too lazy to dig deep into code.
- May be the wrong call, but seems safe option.
- */
- var view = {
- width : 300,
- height : 225
- };
- var innerRadius = Math.round((view.width * 130) / 300);
- var outerRadius = Math.round((view.width * 145) / 300);
- var majorGraduations = parseInt(attrs.majorGraduations - 1) || 5;
- var minorGraduations = parseInt(attrs.minorGraduations) || 10;
- var majorGraduationLength = Math.round((view.width * 16) / 300);
- var minorGraduationLength = Math.round((view.width * 10) / 300);
- var majorGraduationMarginTop = Math.round((view.width * 7) / 300);
- var majorGraduationColor = attrs.majorGraduationColor || "#B0B0B0";
- var minorGraduationColor = attrs.minorGraduationColor || "#D0D0D0";
- var majorGraduationTextColor = attrs.majorGraduationTextColor || "#6C6C6C";
- var needleColor = attrs.needleColor || "#416094";
- var valueVerticalOffset = Math.round((view.width * 30) / 300);
- var inactiveColor = "#D7D7D7";
- var transitionMs = parseInt(attrs.transitionMs) || 750;
- var majorGraduationTextSize = parseInt(attrs.majorGraduationTextSize);
- var needleValueTextSize = parseInt(attrs.needleValueTextSize);
- var needle = undefined;
- //The scope.data object might contain the data we need, otherwise we fall back on the scope.xyz property
- var extractData = function (prop) {
- if (!scope.data) return scope[prop];
- if (scope.data[prop] === undefined || scope.data[prop] == null) {
- return scope[prop];
- }
- return scope.data[prop];
- };
- var maxLimit;
- var minLimit;
- var value;
- var valueUnit;
- var precision;
- var majorGraduationPrecision;
- var ranges;
- var label;
-
- var updateInternalData = function() {
- maxLimit = extractData('upperLimit') ? extractData('upperLimit') : defaultUpperLimit;
- minLimit = extractData('lowerLimit') ? extractData('lowerLimit') : defaultLowerLimit;
- value = extractData('value');
- valueUnit = extractData('valueUnit');
- precision = extractData('precision');
- majorGraduationPrecision = extractData('majorGraduationPrecision');
- ranges = extractData('ranges');
- label = extractData('label'); // MODIFIED
- };
- updateInternalData();
-
- /* Colin Bester
- Add viewBox and width attributes.
- Used view.width and view.height in case it's decided that hardcoding these values is an issue.
- Width can be specified as %, px etc and will scale image to fit.
- */
- var svg = d3.select(ele[0])
- .append('svg')
- .attr('width', _width)
- .attr('viewBox', '0 0 '+view.width+' '+view.height);
- // .attr('view.width', view.width)
- // .attr('height', view.width * 0.75);
- var renderMajorGraduations = function (majorGraduationsAngles) {
- var centerX = view.width / 2;
- var centerY = view.width / 2;
- //Render Major Graduations
- majorGraduationsAngles.forEach(function (pValue, index) {
- var cos1Adj = Math.round(Math.cos((90 - pValue) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop - majorGraduationLength));
- var sin1Adj = Math.round(Math.sin((90 - pValue) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop - majorGraduationLength));
- var cos2Adj = Math.round(Math.cos((90 - pValue) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop));
- var sin2Adj = Math.round(Math.sin((90 - pValue) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop));
- var x1 = centerX + cos1Adj;
- var y1 = centerY + sin1Adj * -1;
- var x2 = centerX + cos2Adj;
- var y2 = centerY + sin2Adj * -1;
- svg.append("svg:line")
- .attr("x1", x1)
- .attr("y1", y1)
- .attr("x2", x2)
- .attr("y2", y2)
- .style("stroke", majorGraduationColor);
- renderMinorGraduations(majorGraduationsAngles, index);
- });
- };
- var renderMinorGraduations = function (majorGraduationsAngles, indexMajor) {
- var minorGraduationsAngles = [];
- if (indexMajor > 0) {
- var minScale = majorGraduationsAngles[indexMajor - 1];
- var maxScale = majorGraduationsAngles[indexMajor];
- var scaleRange = maxScale - minScale;
- for (var i = 1; i < minorGraduations; i++) {
- var scaleValue = minScale + i * scaleRange / minorGraduations;
- minorGraduationsAngles.push(scaleValue);
- }
- var centerX = view.width / 2;
- var centerY = view.width / 2;
- //Render Minor Graduations
- minorGraduationsAngles.forEach(function (pValue, indexMinor) {
- var cos1Adj = Math.round(Math.cos((90 - pValue) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop - minorGraduationLength));
- var sin1Adj = Math.round(Math.sin((90 - pValue) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop - minorGraduationLength));
- var cos2Adj = Math.round(Math.cos((90 - pValue) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop));
- var sin2Adj = Math.round(Math.sin((90 - pValue) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop));
- var x1 = centerX + cos1Adj;
- var y1 = centerY + sin1Adj * -1;
- var x2 = centerX + cos2Adj;
- var y2 = centerY + sin2Adj * -1;
- svg.append("svg:line")
- .attr("x1", x1)
- .attr("y1", y1)
- .attr("x2", x2)
- .attr("y2", y2)
- .style("stroke", minorGraduationColor);
- });
- }
- };
- var getMajorGraduationValues = function (pMinLimit, pMaxLimit, pPrecision) {
- var scaleRange = pMaxLimit - pMinLimit;
- var majorGraduationValues = [];
- for (var i = 0; i <= majorGraduations; i++) {
- var scaleValue = pMinLimit + i * scaleRange / (majorGraduations);
- majorGraduationValues.push(scaleValue.toFixed(pPrecision));
- }
- return majorGraduationValues;
- };
- var getMajorGraduationAngles = function () {
- var scaleRange = 2 * gaugeAngle;
- var minScale = -1 * gaugeAngle;
- var graduationsAngles = [];
- for (var i = 0; i <= majorGraduations; i++) {
- var scaleValue = minScale + i * scaleRange / (majorGraduations);
- graduationsAngles.push(scaleValue);
- }
- return graduationsAngles;
- };
- var getNewAngle = function(pValue) {
- var scale = d3.scale.linear().range([0, 1]).domain([minLimit, maxLimit]);
- var ratio = scale(pValue);
- var scaleRange = 2 * gaugeAngle;
- var minScale = -1 * gaugeAngle;
- var newAngle = minScale + (ratio * scaleRange);
- return newAngle;
- };
- var renderMajorGraduationTexts = function (majorGraduationsAngles, majorGraduationValues, pValueUnit) {
- if (!ranges) return;
- var centerX = view.width / 2;
- var centerY = view.width / 2;
- var textVerticalPadding = 5;
- var textHorizontalPadding = 5;
- var lastGraduationValue = majorGraduationValues[majorGraduationValues.length - 1];
- var textSize = isNaN(majorGraduationTextSize) ? (view.width * 12) / 300 : majorGraduationTextSize;
- var fontStyle = textSize + "px Courier";
- var dummyText = svg.append("text")
- .attr("x", centerX)
- .attr("y", centerY)
- .attr("fill", "transparent")
- .attr("text-anchor", "middle")
- .style("font", fontStyle)
- .text(lastGraduationValue + pValueUnit);
- var textWidth = dummyText.node().getBBox().width;
- for (var i = 0; i < majorGraduationsAngles.length; i++) {
- var angle = majorGraduationsAngles[i];
- var cos1Adj = Math.round(Math.cos((90 - angle) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop - majorGraduationLength - textHorizontalPadding));
- var sin1Adj = Math.round(Math.sin((90 - angle) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop - majorGraduationLength - textVerticalPadding));
- var sin1Factor = 1;
- if (sin1Adj < 0) sin1Factor = 1.1;
- if (sin1Adj > 0) sin1Factor = 0.9;
- if (cos1Adj > 0) {
- if (angle > 0 && angle < 45) {
- cos1Adj -= textWidth / 2;
- } else {
- cos1Adj -= textWidth;
- }
- }
- if (cos1Adj < 0) {
- if (angle < 0 && angle > -45) {
- cos1Adj -= textWidth / 2;
- }
- }
- if (cos1Adj == 0) {
- cos1Adj -= angle == 0 ? textWidth / 4 : textWidth / 2;
- }
- var x1 = centerX + cos1Adj;
- var y1 = centerY + sin1Adj * sin1Factor * -1;
- svg.append("text")
- .attr("class", "mtt-majorGraduationText")
- .style("font", fontStyle)
- .attr("text-align", "center")
- .attr("x", x1)
- .attr("dy", y1)
- .attr("fill", majorGraduationTextColor)
- .text(majorGraduationValues[i] + pValueUnit);
- }
- };
- var renderGraduationNeedle = function (value, valueUnit, precision, minLimit, maxLimit) {
- svg.selectAll('.mtt-graduation-needle').remove();
- svg.selectAll('.mtt-graduationValueText').remove();
- svg.selectAll('.mtt-graduation-needle-center').remove();
-
- var centerX = view.width / 2;
- var centerY = view.width / 2;
- var centerColor;
- if (typeof value === 'undefined') {
- centerColor = inactiveColor;
- } else {
- centerColor = needleColor;
- var needleAngle = getNewAngle(value);
- var needleLen = innerRadius - majorGraduationLength - majorGraduationMarginTop;
- var needleRadius = (view.width * 2.5) / 300;
- var textSize = isNaN(needleValueTextSize) ? (view.width * 12) / 300 : needleValueTextSize;
- var fontStyle = textSize + "px Courier";
- if (value >= minLimit && value <= maxLimit) {
- var lineData = [
- [needleRadius, 0],
- [0, -needleLen],
- [-needleRadius, 0],
- [needleRadius, 0]
- ];
- var pointerLine = d3.svg.line().interpolate('monotone');
- var pg = svg.append('g').data([lineData])
- .attr('class', 'mtt-graduation-needle')
- .style("fill", needleColor)
- .attr('transform', 'translate(' + centerX + ',' + centerY + ')');
- needle = pg.append('path')
- .attr('d', pointerLine)
- .attr('transform', 'rotate('+needleAngle+')');
- }
- svg.append("text")
- .attr("x", centerX)
- .attr("y", centerY + valueVerticalOffset)
- .attr("class", "mtt-graduationValueText")
- .attr("fill", needleColor)
- .attr("text-anchor", "middle")
- .attr("font-weight", "bold")
- .style("font", fontStyle)
- .text(value.toFixed(precision) + valueUnit);
- // MODIFIED: Added a customizable label
- svg.append("text")
- .attr("x", centerX)
- .attr("y", centerY + valueVerticalOffset + 10)
- .attr("class", "mtt-graduationValueText")
- .attr("fill", needleColor)
- .attr("text-anchor", "middle")
- .attr("font-weight", "bold")
- .style("font", fontStyle)
- .text(label);
- }
- var circleRadius = (view.width * 6) / 300;
- svg.append("circle")
- .attr("r", circleRadius)
- .attr("cy", centerX)
- .attr("cx", centerY)
- .attr("fill", centerColor)
- .attr("class", "mtt-graduation-needle-center");
- };
- $window.onresize = function () {
- scope.$apply();
- };
- scope.$watch(function () {
- return angular.element($window)[0].innerWidth;
- }, function () {
- scope.render();
- });
- /* Colin Bester
- Removed watching of data.value as couldn't see reason for this, plus it's the cause of flicker when using
- data=option mode of using directive.
- I'm assuming that calling render function is not what was intended on every value update.
- */
- // scope.$watchCollection('[ranges, data.ranges, data.value]', function () {
- scope.$watchCollection('[ranges, data.ranges]', function () {
- scope.render();
- }, true);
- scope.render = function () {
- updateInternalData();
- svg.selectAll('*').remove();
- if (renderTimeout) clearTimeout(renderTimeout);
- renderTimeout = $timeout(function () {
- var d3DataSource = [];
- if (typeof ranges === 'undefined') {
- d3DataSource.push([minLimit, maxLimit, inactiveColor]);
- } else {
- //Data Generation
- ranges.forEach(function (pValue, index) {
- d3DataSource.push([pValue.min, pValue.max, pValue.color]);
- });
- }
- //Render Gauge Color Area
- var translate = "translate(" + view.width / 2 + "," + view.width / 2 + ")";
- var cScale = d3.scale.linear().domain([minLimit, maxLimit]).range([-1 * gaugeAngle * (Math.PI / 180), gaugeAngle * (Math.PI / 180)]);
- var arc = d3.svg.arc()
- .innerRadius(innerRadius)
- .outerRadius(outerRadius)
- .startAngle(function (d) { return cScale(d[0]); })
- .endAngle(function (d) { return cScale(d[1]); });
- svg.selectAll("path")
- .data(d3DataSource)
- .enter()
- .append("path")
- .attr("d", arc)
- .style("fill", function (d) { return d[2]; })
- .attr("transform", translate);
- var majorGraduationsAngles = getMajorGraduationAngles();
- var majorGraduationValues = getMajorGraduationValues(minLimit, maxLimit, majorGraduationPrecision);
- renderMajorGraduations(majorGraduationsAngles);
- renderMajorGraduationTexts(majorGraduationsAngles, majorGraduationValues, valueUnit);
- renderGraduationNeedle(value, valueUnit, precision, minLimit, maxLimit);
- initialized = true;
- }, 200);
- };
- var onValueChanged = function(pValue, pPrecision, pValueUnit) {
- if (typeof pValue === 'undefined' || pValue == null) return;
-
- if (needle && pValue >= minLimit && pValue <= maxLimit) {
- var needleAngle = getNewAngle(pValue);
- needle.transition()
- .duration(transitionMs)
- .ease('elastic')
- .attr('transform', 'rotate('+needleAngle+')');
- svg.selectAll('.mtt-graduationValueText')
- .text('[ ' + pValue.toFixed(pPrecision) + pValueUnit + ' ]') ;
- } else {
- svg.selectAll('.mtt-graduation-needle').remove();
- svg.selectAll('.mtt-graduationValueText').remove();
- svg.selectAll('.mtt-graduation-needle-center').attr("fill", inactiveColor);
- }
- };
- scope.$watchCollection('[value, data.value]', function () {
- if (!initialized) return;
- updateInternalData();
- onValueChanged(value, precision, valueUnit);
- }, true);
- }
- };
- }]);
- </script>
- <script src=saveSvgAsPng.js></script>
- <script>
- angular.module('RadialGaugeDemo', [
- 'ngRadialGauge'
- ]);
-
- angular.module('RadialGaugeDemo').controller('RadialGaugeDemoCtrl', ['$scope', '$timeout', function ($scope, $timeout) {
- $scope.value = 1.5;
- $scope.upperLimit = 6;
- $scope.lowerLimit = 0;
- $scope.unit = "kW";
- $scope.precision = 2;
- $scope.ranges = [
- {
- min: 0,
- max: 1.5,
- color: '#DEDEDE'
- },
- {
- min: 1.5,
- max: 2.5,
- color: '#8DCA2F'
- },
- {
- min: 2.5,
- max: 3.5,
- color: '#FDC702'
- },
- {
- min: 3.5,
- max: 4.5,
- color: '#FF7700'
- },
- {
- min: 4.5,
- max: 6.0,
- color: '#C50200'
- }
- ];
- $scope.OnClick = function() {
- console.log("click");
- svgAsDataUri(document.getElementsByTagName("svg")[0], null, function (uri) {
- var img = '<img class="img-thumbnail" src="' + uri + '">';
- d3.select("#svgpreview").html(img);
- });
- }
- }]);
- </script>
- </head>
- <body ng-app="RadialGaugeDemo">
- <div ng-controller="RadialGaugeDemoCtrl">
- <div width="10%" ng-radial-gauge ranges="ranges" value="value" value-unit="unit" precision="precision" lower-limit="lowerLimit" upper-limit="upperLimit"></div>
- <a href="" ng-click="OnClick()">Click Here to show image Preview</a>
- <div id="svgpreview"></div>
- </div>
- </body>
- </html>
|