UNPKG

22 kBHTMLView Raw
1<!DOCTYPE html>
2<html xmlns="http://www.w3.org/1999/xhtml">
3<head>
4 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
5 <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
6 <script>
7angular.module("ngRadialGauge",[]).directive('ngRadialGauge', ['$window', '$timeout',
8 function ($window, $timeout) {
9 return {
10 restrict: 'EAC',
11 scope: {
12 data: '=',
13 lowerLimit: '=',
14 upperLimit: '=',
15 ranges: '=',
16 value: '=',
17 valueUnit: '=',
18 precision: '=',
19 majorGraduationPrecision: '=',
20 label: '=',// MODIFIED
21 onClick: '&'
22 },
23 link: function (scope, ele, attrs) {
24 var defaultUpperLimit = 100;
25 var defaultLowerLimit = 0;
26 var initialized = false;
27
28 var renderTimeout;
29 var gaugeAngle = parseInt(attrs.angle) || 120;
30
31 //New width variable, now works in conjunction with fixed viewBox sizing
32 var _width = attrs.width || "100%";
33
34 /* Colin Bester
35 Width and height are not really such an issue with SVG but choose these values as
36 width of 300 seems to be pretty baked into code.
37 I took the easy path seeing as size is not that relevant and hard coded width and height
38 as I was too lazy to dig deep into code.
39 May be the wrong call, but seems safe option.
40 */
41 var view = {
42 width : 300,
43 height : 225
44 };
45 var innerRadius = Math.round((view.width * 130) / 300);
46 var outerRadius = Math.round((view.width * 145) / 300);
47 var majorGraduations = parseInt(attrs.majorGraduations - 1) || 5;
48 var minorGraduations = parseInt(attrs.minorGraduations) || 10;
49 var majorGraduationLength = Math.round((view.width * 16) / 300);
50 var minorGraduationLength = Math.round((view.width * 10) / 300);
51 var majorGraduationMarginTop = Math.round((view.width * 7) / 300);
52 var majorGraduationColor = attrs.majorGraduationColor || "#B0B0B0";
53 var minorGraduationColor = attrs.minorGraduationColor || "#D0D0D0";
54 var majorGraduationTextColor = attrs.majorGraduationTextColor || "#6C6C6C";
55 var needleColor = attrs.needleColor || "#416094";
56 var valueVerticalOffset = Math.round((view.width * 30) / 300);
57 var inactiveColor = "#D7D7D7";
58 var transitionMs = parseInt(attrs.transitionMs) || 750;
59 var majorGraduationTextSize = parseInt(attrs.majorGraduationTextSize);
60 var needleValueTextSize = parseInt(attrs.needleValueTextSize);
61 var needle = undefined;
62
63 //The scope.data object might contain the data we need, otherwise we fall back on the scope.xyz property
64 var extractData = function (prop) {
65 if (!scope.data) return scope[prop];
66 if (scope.data[prop] === undefined || scope.data[prop] == null) {
67 return scope[prop];
68 }
69 return scope.data[prop];
70 };
71
72 var maxLimit;
73 var minLimit;
74 var value;
75 var valueUnit;
76 var precision;
77 var majorGraduationPrecision;
78 var ranges;
79 var label;
80
81 var updateInternalData = function() {
82 maxLimit = extractData('upperLimit') ? extractData('upperLimit') : defaultUpperLimit;
83 minLimit = extractData('lowerLimit') ? extractData('lowerLimit') : defaultLowerLimit;
84 value = extractData('value');
85 valueUnit = extractData('valueUnit');
86 precision = extractData('precision');
87 majorGraduationPrecision = extractData('majorGraduationPrecision');
88 ranges = extractData('ranges');
89 label = extractData('label'); // MODIFIED
90 };
91 updateInternalData();
92
93 /* Colin Bester
94 Add viewBox and width attributes.
95 Used view.width and view.height in case it's decided that hardcoding these values is an issue.
96 Width can be specified as %, px etc and will scale image to fit.
97 */
98 var svg = d3.select(ele[0])
99 .append('svg')
100 .attr('width', _width)
101 .attr('viewBox', '0 0 '+view.width+' '+view.height);
102 // .attr('view.width', view.width)
103 // .attr('height', view.width * 0.75);
104 var renderMajorGraduations = function (majorGraduationsAngles) {
105 var centerX = view.width / 2;
106 var centerY = view.width / 2;
107 //Render Major Graduations
108 majorGraduationsAngles.forEach(function (pValue, index) {
109 var cos1Adj = Math.round(Math.cos((90 - pValue) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop - majorGraduationLength));
110 var sin1Adj = Math.round(Math.sin((90 - pValue) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop - majorGraduationLength));
111 var cos2Adj = Math.round(Math.cos((90 - pValue) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop));
112 var sin2Adj = Math.round(Math.sin((90 - pValue) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop));
113 var x1 = centerX + cos1Adj;
114 var y1 = centerY + sin1Adj * -1;
115 var x2 = centerX + cos2Adj;
116 var y2 = centerY + sin2Adj * -1;
117 svg.append("svg:line")
118 .attr("x1", x1)
119 .attr("y1", y1)
120 .attr("x2", x2)
121 .attr("y2", y2)
122 .style("stroke", majorGraduationColor);
123
124 renderMinorGraduations(majorGraduationsAngles, index);
125 });
126 };
127 var renderMinorGraduations = function (majorGraduationsAngles, indexMajor) {
128 var minorGraduationsAngles = [];
129
130 if (indexMajor > 0) {
131 var minScale = majorGraduationsAngles[indexMajor - 1];
132 var maxScale = majorGraduationsAngles[indexMajor];
133 var scaleRange = maxScale - minScale;
134
135 for (var i = 1; i < minorGraduations; i++) {
136 var scaleValue = minScale + i * scaleRange / minorGraduations;
137 minorGraduationsAngles.push(scaleValue);
138 }
139
140 var centerX = view.width / 2;
141 var centerY = view.width / 2;
142 //Render Minor Graduations
143 minorGraduationsAngles.forEach(function (pValue, indexMinor) {
144 var cos1Adj = Math.round(Math.cos((90 - pValue) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop - minorGraduationLength));
145 var sin1Adj = Math.round(Math.sin((90 - pValue) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop - minorGraduationLength));
146 var cos2Adj = Math.round(Math.cos((90 - pValue) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop));
147 var sin2Adj = Math.round(Math.sin((90 - pValue) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop));
148 var x1 = centerX + cos1Adj;
149 var y1 = centerY + sin1Adj * -1;
150 var x2 = centerX + cos2Adj;
151 var y2 = centerY + sin2Adj * -1;
152 svg.append("svg:line")
153 .attr("x1", x1)
154 .attr("y1", y1)
155 .attr("x2", x2)
156 .attr("y2", y2)
157 .style("stroke", minorGraduationColor);
158 });
159 }
160 };
161 var getMajorGraduationValues = function (pMinLimit, pMaxLimit, pPrecision) {
162 var scaleRange = pMaxLimit - pMinLimit;
163 var majorGraduationValues = [];
164 for (var i = 0; i <= majorGraduations; i++) {
165 var scaleValue = pMinLimit + i * scaleRange / (majorGraduations);
166 majorGraduationValues.push(scaleValue.toFixed(pPrecision));
167 }
168
169 return majorGraduationValues;
170 };
171 var getMajorGraduationAngles = function () {
172 var scaleRange = 2 * gaugeAngle;
173 var minScale = -1 * gaugeAngle;
174 var graduationsAngles = [];
175 for (var i = 0; i <= majorGraduations; i++) {
176 var scaleValue = minScale + i * scaleRange / (majorGraduations);
177 graduationsAngles.push(scaleValue);
178 }
179
180 return graduationsAngles;
181 };
182 var getNewAngle = function(pValue) {
183 var scale = d3.scale.linear().range([0, 1]).domain([minLimit, maxLimit]);
184 var ratio = scale(pValue);
185 var scaleRange = 2 * gaugeAngle;
186 var minScale = -1 * gaugeAngle;
187 var newAngle = minScale + (ratio * scaleRange);
188 return newAngle;
189 };
190 var renderMajorGraduationTexts = function (majorGraduationsAngles, majorGraduationValues, pValueUnit) {
191 if (!ranges) return;
192
193 var centerX = view.width / 2;
194 var centerY = view.width / 2;
195 var textVerticalPadding = 5;
196 var textHorizontalPadding = 5;
197
198 var lastGraduationValue = majorGraduationValues[majorGraduationValues.length - 1];
199 var textSize = isNaN(majorGraduationTextSize) ? (view.width * 12) / 300 : majorGraduationTextSize;
200 var fontStyle = textSize + "px Courier";
201
202 var dummyText = svg.append("text")
203 .attr("x", centerX)
204 .attr("y", centerY)
205 .attr("fill", "transparent")
206 .attr("text-anchor", "middle")
207 .style("font", fontStyle)
208 .text(lastGraduationValue + pValueUnit);
209
210 var textWidth = dummyText.node().getBBox().width;
211
212 for (var i = 0; i < majorGraduationsAngles.length; i++) {
213 var angle = majorGraduationsAngles[i];
214 var cos1Adj = Math.round(Math.cos((90 - angle) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop - majorGraduationLength - textHorizontalPadding));
215 var sin1Adj = Math.round(Math.sin((90 - angle) * Math.PI / 180) * (innerRadius - majorGraduationMarginTop - majorGraduationLength - textVerticalPadding));
216
217 var sin1Factor = 1;
218 if (sin1Adj < 0) sin1Factor = 1.1;
219 if (sin1Adj > 0) sin1Factor = 0.9;
220 if (cos1Adj > 0) {
221 if (angle > 0 && angle < 45) {
222 cos1Adj -= textWidth / 2;
223 } else {
224 cos1Adj -= textWidth;
225 }
226 }
227 if (cos1Adj < 0) {
228 if (angle < 0 && angle > -45) {
229 cos1Adj -= textWidth / 2;
230 }
231 }
232 if (cos1Adj == 0) {
233 cos1Adj -= angle == 0 ? textWidth / 4 : textWidth / 2;
234 }
235
236 var x1 = centerX + cos1Adj;
237 var y1 = centerY + sin1Adj * sin1Factor * -1;
238
239 svg.append("text")
240 .attr("class", "mtt-majorGraduationText")
241 .style("font", fontStyle)
242 .attr("text-align", "center")
243 .attr("x", x1)
244 .attr("dy", y1)
245 .attr("fill", majorGraduationTextColor)
246 .text(majorGraduationValues[i] + pValueUnit);
247 }
248 };
249 var renderGraduationNeedle = function (value, valueUnit, precision, minLimit, maxLimit) {
250 svg.selectAll('.mtt-graduation-needle').remove();
251 svg.selectAll('.mtt-graduationValueText').remove();
252 svg.selectAll('.mtt-graduation-needle-center').remove();
253
254 var centerX = view.width / 2;
255 var centerY = view.width / 2;
256 var centerColor;
257
258 if (typeof value === 'undefined') {
259 centerColor = inactiveColor;
260 } else {
261 centerColor = needleColor;
262 var needleAngle = getNewAngle(value);
263 var needleLen = innerRadius - majorGraduationLength - majorGraduationMarginTop;
264 var needleRadius = (view.width * 2.5) / 300;
265 var textSize = isNaN(needleValueTextSize) ? (view.width * 12) / 300 : needleValueTextSize;
266 var fontStyle = textSize + "px Courier";
267
268 if (value >= minLimit && value <= maxLimit) {
269 var lineData = [
270 [needleRadius, 0],
271 [0, -needleLen],
272 [-needleRadius, 0],
273 [needleRadius, 0]
274 ];
275 var pointerLine = d3.svg.line().interpolate('monotone');
276 var pg = svg.append('g').data([lineData])
277 .attr('class', 'mtt-graduation-needle')
278 .style("fill", needleColor)
279 .attr('transform', 'translate(' + centerX + ',' + centerY + ')');
280 needle = pg.append('path')
281 .attr('d', pointerLine)
282 .attr('transform', 'rotate('+needleAngle+')');
283 }
284
285 svg.append("text")
286 .attr("x", centerX)
287 .attr("y", centerY + valueVerticalOffset)
288 .attr("class", "mtt-graduationValueText")
289 .attr("fill", needleColor)
290 .attr("text-anchor", "middle")
291 .attr("font-weight", "bold")
292 .style("font", fontStyle)
293 .text(value.toFixed(precision) + valueUnit);
294
295 // MODIFIED: Added a customizable label
296 svg.append("text")
297 .attr("x", centerX)
298 .attr("y", centerY + valueVerticalOffset + 10)
299 .attr("class", "mtt-graduationValueText")
300 .attr("fill", needleColor)
301 .attr("text-anchor", "middle")
302 .attr("font-weight", "bold")
303 .style("font", fontStyle)
304 .text(label);
305 }
306
307 var circleRadius = (view.width * 6) / 300;
308
309 svg.append("circle")
310 .attr("r", circleRadius)
311 .attr("cy", centerX)
312 .attr("cx", centerY)
313 .attr("fill", centerColor)
314 .attr("class", "mtt-graduation-needle-center");
315 };
316 $window.onresize = function () {
317 scope.$apply();
318 };
319 scope.$watch(function () {
320 return angular.element($window)[0].innerWidth;
321 }, function () {
322 scope.render();
323 });
324
325 /* Colin Bester
326 Removed watching of data.value as couldn't see reason for this, plus it's the cause of flicker when using
327 data=option mode of using directive.
328 I'm assuming that calling render function is not what was intended on every value update.
329 */
330 // scope.$watchCollection('[ranges, data.ranges, data.value]', function () {
331 scope.$watchCollection('[ranges, data.ranges]', function () {
332 scope.render();
333 }, true);
334
335
336 scope.render = function () {
337 updateInternalData();
338 svg.selectAll('*').remove();
339 if (renderTimeout) clearTimeout(renderTimeout);
340
341 renderTimeout = $timeout(function () {
342 var d3DataSource = [];
343
344 if (typeof ranges === 'undefined') {
345 d3DataSource.push([minLimit, maxLimit, inactiveColor]);
346 } else {
347 //Data Generation
348 ranges.forEach(function (pValue, index) {
349 d3DataSource.push([pValue.min, pValue.max, pValue.color]);
350 });
351 }
352
353 //Render Gauge Color Area
354 var translate = "translate(" + view.width / 2 + "," + view.width / 2 + ")";
355 var cScale = d3.scale.linear().domain([minLimit, maxLimit]).range([-1 * gaugeAngle * (Math.PI / 180), gaugeAngle * (Math.PI / 180)]);
356 var arc = d3.svg.arc()
357 .innerRadius(innerRadius)
358 .outerRadius(outerRadius)
359 .startAngle(function (d) { return cScale(d[0]); })
360 .endAngle(function (d) { return cScale(d[1]); });
361 svg.selectAll("path")
362 .data(d3DataSource)
363 .enter()
364 .append("path")
365 .attr("d", arc)
366 .style("fill", function (d) { return d[2]; })
367 .attr("transform", translate);
368
369 var majorGraduationsAngles = getMajorGraduationAngles();
370 var majorGraduationValues = getMajorGraduationValues(minLimit, maxLimit, majorGraduationPrecision);
371 renderMajorGraduations(majorGraduationsAngles);
372 renderMajorGraduationTexts(majorGraduationsAngles, majorGraduationValues, valueUnit);
373 renderGraduationNeedle(value, valueUnit, precision, minLimit, maxLimit);
374 initialized = true;
375 }, 200);
376
377 };
378 var onValueChanged = function(pValue, pPrecision, pValueUnit) {
379 if (typeof pValue === 'undefined' || pValue == null) return;
380
381 if (needle && pValue >= minLimit && pValue <= maxLimit) {
382 var needleAngle = getNewAngle(pValue);
383 needle.transition()
384 .duration(transitionMs)
385 .ease('elastic')
386 .attr('transform', 'rotate('+needleAngle+')');
387 svg.selectAll('.mtt-graduationValueText')
388 .text('[ ' + pValue.toFixed(pPrecision) + pValueUnit + ' ]') ;
389 } else {
390 svg.selectAll('.mtt-graduation-needle').remove();
391 svg.selectAll('.mtt-graduationValueText').remove();
392 svg.selectAll('.mtt-graduation-needle-center').attr("fill", inactiveColor);
393 }
394 };
395 scope.$watchCollection('[value, data.value]', function () {
396 if (!initialized) return;
397 updateInternalData();
398 onValueChanged(value, precision, valueUnit);
399 }, true);
400 }
401 };
402 }]);
403
404 </script>
405 <script src=saveSvgAsPng.js></script>
406 <script>
407 angular.module('RadialGaugeDemo', [
408 'ngRadialGauge'
409 ]);
410
411 angular.module('RadialGaugeDemo').controller('RadialGaugeDemoCtrl', ['$scope', '$timeout', function ($scope, $timeout) {
412 $scope.value = 1.5;
413 $scope.upperLimit = 6;
414 $scope.lowerLimit = 0;
415 $scope.unit = "kW";
416 $scope.precision = 2;
417 $scope.ranges = [
418 {
419 min: 0,
420 max: 1.5,
421 color: '#DEDEDE'
422 },
423 {
424 min: 1.5,
425 max: 2.5,
426 color: '#8DCA2F'
427 },
428 {
429 min: 2.5,
430 max: 3.5,
431 color: '#FDC702'
432 },
433 {
434 min: 3.5,
435 max: 4.5,
436 color: '#FF7700'
437 },
438 {
439 min: 4.5,
440 max: 6.0,
441 color: '#C50200'
442 }
443 ];
444 $scope.OnClick = function() {
445 console.log("click");
446 svgAsDataUri(document.getElementsByTagName("svg")[0], null, function (uri) {
447 var img = '<img class="img-thumbnail" src="' + uri + '">';
448 d3.select("#svgpreview").html(img);
449 });
450 }
451 }]);
452 </script>
453</head>
454<body ng-app="RadialGaugeDemo">
455 <div ng-controller="RadialGaugeDemoCtrl">
456 <div width="10%" ng-radial-gauge ranges="ranges" value="value" value-unit="unit" precision="precision" lower-limit="lowerLimit" upper-limit="upperLimit"></div>
457 <a href="" ng-click="OnClick()">Click Here to show image Preview</a>
458 <div id="svgpreview"></div>
459 </div>
460</body>
461</html>
\No newline at end of file