├── D3RadialGauge ├── app.js ├── Index.html ├── radial-gauge-ctrl.js ├── d3-serv.js ├── LICENSE.txt └── ng-radial-gauge-dir.js ├── D3RadialGauge.png └── README.md /D3RadialGauge/app.js: -------------------------------------------------------------------------------- 1 | var app = angular.module('myApp', []); 2 | -------------------------------------------------------------------------------- /D3RadialGauge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stherrienaspnet/D3-Radial-Gauge/HEAD/D3RadialGauge.png -------------------------------------------------------------------------------- /D3RadialGauge/Index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ngRadialGauge Demo 5 | 6 | 7 |
8 |
9 |
10 | 11 |
12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /D3RadialGauge/radial-gauge-ctrl.js: -------------------------------------------------------------------------------- 1 | app.controller('RadialGaugeCtrl', ['$scope', function ($scope) { 2 | $scope.value = 1.5; 3 | $scope.upperLimit = 6; 4 | $scope.lowerLimit = 0; 5 | $scope.unit = "kW"; 6 | $scope.precision = 2; 7 | $scope.ranges = [ 8 | { 9 | min: 0, 10 | max: 1.5, 11 | color: '#DEDEDE' 12 | }, 13 | { 14 | min: 1.5, 15 | max: 2.5, 16 | color: '#8DCA2F' 17 | }, 18 | { 19 | min: 2.5, 20 | max: 3.5, 21 | color: '#FDC702' 22 | }, 23 | { 24 | min: 3.5, 25 | max: 4.5, 26 | color: '#FF7700' 27 | }, 28 | { 29 | min: 4.5, 30 | max: 6.0, 31 | color: '#C50200' 32 | } 33 | ]; 34 | 35 | $scope.increase = function() { 36 | $scope.value = $scope.value * 1.1; 37 | } 38 | }]); 39 | -------------------------------------------------------------------------------- /D3RadialGauge/d3-serv.js: -------------------------------------------------------------------------------- 1 | app.factory('d3Service', ['$document', '$window', '$q', '$rootScope', 2 | function ($document, $window, $q, $rootScope) { 3 | var d = $q.defer(), 4 | d3service = { 5 | d3: function () { return d.promise; } 6 | }; 7 | function onScriptLoad() { 8 | // Load client in the browser 9 | $rootScope.$apply(function () { d.resolve($window.d3); }); 10 | } 11 | var scriptTag = $document[0].createElement('script'); 12 | scriptTag.type = 'text/javascript'; 13 | scriptTag.async = true; 14 | scriptTag.src = '//cdnjs.cloudflare.com/ajax/libs/d3/3.4.1/d3.min.js'; 15 | scriptTag.onreadystatechange = function () { 16 | if (this.readyState == 'complete') onScriptLoad(); 17 | }; 18 | scriptTag.onload = onScriptLoad; 19 | 20 | var s = $document[0].getElementsByTagName('body')[0]; 21 | s.appendChild(scriptTag); 22 | 23 | return d3service; 24 | }]); 25 | -------------------------------------------------------------------------------- /D3RadialGauge/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Stephane Therrien 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * The name Michael Bostock may not be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT, 21 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 26 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | D3-Radial-Gauge 2 | =============== 3 | 4 | D3.JS Radial Gauge 5 | 6 | ![alt tag](https://raw.github.com/stherrienaspnet/D3-Radial-Gauge/master/D3RadialGauge.png) 7 | 8 | Here is the list of properties with their default value that can be changed according to your needs: 9 | 10 | var settings = $.extend({ 11 | width: 300, 12 | innerRadius: 130, 13 | outterRadius: 145, 14 | majorGraduations: 6, 15 | minorGraduations: 10, 16 | majorGraduationLenght: 16, 17 | minorGraduationLenght: 10, 18 | majorGraduationMarginTop: 7, 19 | majorGraduationColor: "#EAEAEA", 20 | minorGraduationColor: "#EAEAEA", 21 | majorGraduationTextColor: "#6C6C6C", 22 | majorGraduationDecimals: 2, 23 | needleColor: "#2DABC1", 24 | valueVerticalOffset:40, 25 | data: [], 26 | value:0 27 | }, options); 28 | 29 | Here is a usage sample: 30 | 31 |
32 |
33 |
34 | 59 | 60 | http://plnkr.co/edit/sP0dHuGoCpaIvCiqoNxF?p=preview 61 | -------------------------------------------------------------------------------- /D3RadialGauge/ng-radial-gauge-dir.js: -------------------------------------------------------------------------------- 1 | app.directive('ngRadialGauge', ['$window', '$timeout', 'd3Service', 2 | function ($window, $timeout, d3Service) { 3 | return { 4 | restrict: 'A', 5 | scope: { 6 | lowerLimit: '=', 7 | upperLimit: '=', 8 | ranges: '=', 9 | value: '=', 10 | valueUnit: '=', 11 | precision: '=', 12 | label: '@', 13 | onClick: '&' 14 | }, 15 | link: function (scope, ele, attrs) { 16 | "use strict"; 17 | d3Service.d3().then(function (d3) { 18 | var initialized = false; 19 | var renderTimeout; 20 | var width = parseInt(attrs.width) || 300; 21 | var innerRadius = Math.round((width * 130) / 300); 22 | var outterRadius = Math.round((width * 145) / 300); 23 | var majorGraduations = parseInt(attrs.majorGraduations - 1) || 5; 24 | var minorGraduations = parseInt(attrs.minorGraduations) || 10; 25 | var majorGraduationLenght = Math.round((width * 16) / 300); 26 | var minorGraduationLenght = Math.round((width * 10) / 300); 27 | var majorGraduationMarginTop = Math.round((width * 7) / 300); 28 | var majorGraduationColor = attrs.majorGraduationColor || "#B0B0B0"; 29 | var minorGraduationColor = attrs.minorGraduationColor || "#D0D0D0"; 30 | var majorGraduationTextColor = attrs.majorGraduationTextColor || "#6C6C6C"; 31 | var needleColor = attrs.needleColor || "#416094"; 32 | var valueVerticalOffset = Math.round((width * 30) / 300); 33 | var unactiveColor = "#D7D7D7"; 34 | var majorGraduationTextSize = parseInt(attrs.majorGraduationTextSize); 35 | var needleValueTextSize = parseInt(attrs.needleValueTextSize); 36 | 37 | var maxLimit = scope.upperLimit ? scope.upperLimit : 100; 38 | var minLimit = scope.lowerLimit ? scope.lowerLimit : 0; 39 | 40 | var svg = d3.select(ele[0]) 41 | .append('svg') 42 | .attr('width', width) 43 | .attr('height', width * 0.75); 44 | var renderMajorGraduations = function (majorGraduationsAngles) { 45 | var centerX = width / 2; 46 | var centerY = width / 2; 47 | //Render Major Graduations 48 | $.each(majorGraduationsAngles, function (index, value) { 49 | var cos1Adj = Math.round(Math.cos((90 - value) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop - majorGraduationLenght)); 50 | var sin1Adj = Math.round(Math.sin((90 - value) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop - majorGraduationLenght)); 51 | var cos2Adj = Math.round(Math.cos((90 - value) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop)); 52 | var sin2Adj = Math.round(Math.sin((90 - value) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop)); 53 | var x1 = centerX + cos1Adj; 54 | var y1 = centerY + sin1Adj * -1; 55 | var x2 = centerX + cos2Adj; 56 | var y2 = centerY + sin2Adj * -1; 57 | svg.append("svg:line") 58 | .attr("x1", x1) 59 | .attr("y1", y1) 60 | .attr("x2", x2) 61 | .attr("y2", y2) 62 | .style("stroke", majorGraduationColor); 63 | 64 | renderMinorGraduations(majorGraduationsAngles, index); 65 | }); 66 | }; 67 | var renderMinorGraduations = function (majorGraduationsAngles, indexMajor) { 68 | var minorGraduationsAngles = []; 69 | 70 | if (indexMajor > 0) { 71 | var minScale = majorGraduationsAngles[indexMajor - 1]; 72 | var maxScale = majorGraduationsAngles[indexMajor]; 73 | var scaleRange = maxScale - minScale; 74 | 75 | for (var i = 1; i < minorGraduations; i++) { 76 | var scaleValue = minScale + i * scaleRange / minorGraduations; 77 | minorGraduationsAngles.push(scaleValue); 78 | } 79 | 80 | var centerX = width / 2; 81 | var centerY = width / 2; 82 | //Render Minor Graduations 83 | $.each(minorGraduationsAngles, function (indexMinor, value) { 84 | var cos1Adj = Math.round(Math.cos((90 - value) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop - minorGraduationLenght)); 85 | var sin1Adj = Math.round(Math.sin((90 - value) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop - minorGraduationLenght)); 86 | var cos2Adj = Math.round(Math.cos((90 - value) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop)); 87 | var sin2Adj = Math.round(Math.sin((90 - value) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop)); 88 | var x1 = centerX + cos1Adj; 89 | var y1 = centerY + sin1Adj * -1; 90 | var x2 = centerX + cos2Adj; 91 | var y2 = centerY + sin2Adj * -1; 92 | svg.append("svg:line") 93 | .attr("x1", x1) 94 | .attr("y1", y1) 95 | .attr("x2", x2) 96 | .attr("y2", y2) 97 | .style("stroke", minorGraduationColor); 98 | }); 99 | } 100 | }; 101 | var getMajorGraduationValues = function (minLimit, maxLimit) { 102 | var scaleRange = maxLimit - minLimit; 103 | var majorGraduationValues = []; 104 | for (var i = 0; i <= majorGraduations; i++) { 105 | var scaleValue = minLimit + i * scaleRange / (majorGraduations); 106 | majorGraduationValues.push(scaleValue.toFixed(scope.precision)); 107 | } 108 | 109 | return majorGraduationValues; 110 | }; 111 | var getMajorGraduationAngles = function () { 112 | var scaleRange = 240; 113 | var minScale = -120; 114 | var graduationsAngles = []; 115 | for (var i = 0; i <= majorGraduations; i++) { 116 | var scaleValue = minScale + i * scaleRange / (majorGraduations); 117 | graduationsAngles.push(scaleValue); 118 | } 119 | 120 | return graduationsAngles; 121 | }; 122 | var renderMajorGraduationTexts = function (majorGraduationsAngles, majorGraduationValues) { 123 | if (!scope.ranges) return; 124 | 125 | var centerX = width / 2; 126 | var centerY = width / 2; 127 | var textVerticalPadding = 5; 128 | var textHorizontalPadding = 5; 129 | 130 | var lastGraduationValue = majorGraduationValues[majorGraduationValues.length - 1]; 131 | var textSize = isNaN(majorGraduationTextSize) ? (width * 12) / 300 : majorGraduationTextSize; 132 | var fontStyle = textSize + "px Courier"; 133 | 134 | var dummyText = svg.append("text") 135 | .attr("x", centerX) 136 | .attr("y", centerY) 137 | .attr("fill", "transparent") 138 | .attr("text-anchor", "middle") 139 | .style("font", fontStyle) 140 | .text(lastGraduationValue + scope.valueUnit); 141 | 142 | var textWidth = dummyText.node().getBBox().width; 143 | 144 | for (var i = 0; i < majorGraduationsAngles.length; i++) { 145 | var angle = majorGraduationsAngles[i]; 146 | var cos1Adj = Math.round(Math.cos((90 - angle) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop - majorGraduationLenght - textHorizontalPadding)); 147 | var sin1Adj = Math.round(Math.sin((90 - angle) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop - majorGraduationLenght - textVerticalPadding)); 148 | 149 | var sin1Factor = 1; 150 | if (sin1Adj < 0) sin1Factor = 1.1; 151 | if (sin1Adj > 0) sin1Factor = 0.9; 152 | if (cos1Adj > 0) { 153 | if (angle > 0 && angle < 45) { 154 | cos1Adj -= textWidth / 2; 155 | } else { 156 | cos1Adj -= textWidth; 157 | } 158 | } 159 | if (cos1Adj < 0) { 160 | if (angle < 0 && angle > -45) { 161 | cos1Adj -= textWidth / 2; 162 | } 163 | } 164 | if (cos1Adj == 0) { 165 | cos1Adj -= angle == 0 ? textWidth / 4 : textWidth / 2; 166 | } 167 | 168 | var x1 = centerX + cos1Adj; 169 | var y1 = centerY + sin1Adj * sin1Factor * -1; 170 | 171 | svg.append("text") 172 | .attr("class", "mtt-majorGraduationText") 173 | .style("font", fontStyle) 174 | .attr("text-align", "center") 175 | .attr("x", x1) 176 | .attr("dy", y1) 177 | .attr("fill", majorGraduationTextColor) 178 | .text(majorGraduationValues[i] + scope.valueUnit); 179 | } 180 | }; 181 | var renderGraduationNeedle = function (minLimit, maxLimit) { 182 | svg.selectAll('.mtt-graduation-needle').remove(); 183 | svg.selectAll('.mtt-graduationValueText').remove(); 184 | svg.selectAll('.mtt-graduation-needle-center').remove(); 185 | 186 | var centerX = width / 2; 187 | var centerY = width / 2; 188 | var centerColor; 189 | 190 | if (typeof scope.value === 'undefined') { 191 | centerColor = unactiveColor; 192 | } else { 193 | centerColor = needleColor; 194 | var needleValue = ((scope.value - minLimit) * 240 / (maxLimit - minLimit)) - 30; 195 | var thetaRad = needleValue * Math.PI / 180; 196 | 197 | var needleLen = innerRadius - majorGraduationLenght - majorGraduationMarginTop; 198 | var needleRadius = (width * 2.5) / 300; 199 | var topX = centerX - needleLen * Math.cos(thetaRad); 200 | var topY = centerY - needleLen * Math.sin(thetaRad); 201 | var leftX = centerX - needleRadius * Math.cos(thetaRad - Math.PI / 2); 202 | var leftY = centerY - needleRadius * Math.sin(thetaRad - Math.PI / 2); 203 | var rightX = centerX - needleRadius * Math.cos(thetaRad + Math.PI / 2); 204 | var rightY = centerY - needleRadius * Math.sin(thetaRad + Math.PI / 2); 205 | var triangle = "M " + leftX + " " + leftY + " L " + topX + " " + topY + " L " + rightX + " " + rightY; 206 | var textSize = isNaN(needleValueTextSize) ? (width * 12) / 300 : needleValueTextSize; 207 | var fontStyle = textSize + "px Courier"; 208 | 209 | if (scope.value >= minLimit && scope.value <= maxLimit) { 210 | svg.append("svg:path") 211 | .attr("d", triangle) 212 | .style("stroke-width", 1) 213 | .style("stroke", needleColor) 214 | .style("fill", needleColor) 215 | .attr("class", "mtt-graduation-needle"); 216 | } 217 | 218 | svg.append("text") 219 | .attr("x", centerX) 220 | .attr("y", centerY + valueVerticalOffset) 221 | .attr("class", "mtt-graduationValueText") 222 | .attr("fill", needleColor) 223 | .attr("text-anchor", "middle") 224 | .attr("font-weight", "bold") 225 | .style("font", fontStyle) 226 | .text('[ ' + scope.value.toFixed(scope.precision) + scope.valueUnit + ' ]'); 227 | } 228 | 229 | var circleRadius = (width * 6) / 300; 230 | 231 | svg.append("circle") 232 | .attr("r", circleRadius) 233 | .attr("cy", centerX) 234 | .attr("cx", centerY) 235 | .attr("fill", centerColor) 236 | .attr("class", "mtt-graduation-needle-center"); 237 | }; 238 | $window.onresize = function () { 239 | scope.$apply(); 240 | }; 241 | scope.$watch(function () { 242 | return angular.element($window)[0].innerWidth; 243 | }, function () { 244 | scope.render(); 245 | }); 246 | scope.$watch('ranges', function () { 247 | scope.render(); 248 | }, true); 249 | 250 | 251 | scope.render = function () { 252 | svg.selectAll('*').remove(); 253 | if (renderTimeout) clearTimeout(renderTimeout); 254 | 255 | renderTimeout = $timeout(function () { 256 | var d3DataSource = []; 257 | 258 | if (typeof scope.ranges === 'undefined') { 259 | d3DataSource.push([minLimit, maxLimit, unactiveColor]); 260 | } else { 261 | //Data Generation 262 | $.each(scope.ranges, function (index, value) { 263 | d3DataSource.push([value.min, value.max, value.color]); 264 | }); 265 | } 266 | 267 | //Render Gauge Color Area 268 | var translate = "translate(" + width / 2 + "," + width / 2 + ")"; 269 | var cScale = d3.scale.linear().domain([minLimit, maxLimit]).range([-120 * (Math.PI / 180), 120 * (Math.PI / 180)]); 270 | var arc = d3.svg.arc() 271 | .innerRadius(innerRadius) 272 | .outerRadius(outterRadius) 273 | .startAngle(function (d) { return cScale(d[0]); }) 274 | .endAngle(function (d) { return cScale(d[1]); }); 275 | svg.selectAll("path") 276 | .data(d3DataSource) 277 | .enter() 278 | .append("path") 279 | .attr("d", arc) 280 | .style("fill", function (d) { return d[2]; }) 281 | .attr("transform", translate); 282 | 283 | var majorGraduationsAngles = getMajorGraduationAngles(); 284 | var majorGraduationValues = getMajorGraduationValues(minLimit, maxLimit); 285 | renderMajorGraduations(majorGraduationsAngles); 286 | renderMajorGraduationTexts(majorGraduationsAngles, majorGraduationValues); 287 | renderGraduationNeedle(minLimit, maxLimit); 288 | initialized = true; 289 | }, 200); 290 | 291 | }; 292 | 293 | scope.$watch('value', function () { 294 | if (!initialized) return; 295 | renderGraduationNeedle(minLimit, maxLimit); 296 | }, true); 297 | }); 298 | } 299 | }; 300 | }]) 301 | --------------------------------------------------------------------------------