AnalogGauge.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.widget.AnalogGauge"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.widget.AnalogGauge"] = true;
  8. dojo.provide("dojox.widget.AnalogGauge");
  9. dojo.require("dojox.gfx");
  10. dojo.require("dojox.widget.gauge._Gauge");
  11. dojo.experimental("dojox.widget.AnalogGauge");
  12. dojo.declare("dojox.widget.gauge.AnalogLineIndicator",[dojox.widget.gauge._Indicator],{
  13. _getShapes: function(){
  14. // summary:
  15. // Private function for generating the shapes for this indicator. An indicator that behaves the
  16. // same might override this one and simply replace the shapes (such as ArrowIndicator).
  17. return [this._gauge.surface.createLine({x1: 0, y1: -this.offset,
  18. x2: 0, y2: -this.length-this.offset})
  19. .setStroke({color: this.color, width: this.width})];
  20. },
  21. draw: function(/*Boolean?*/ dontAnimate){
  22. // summary:
  23. // Override of dojox.widget.gauge._Indicator.draw
  24. // dontAnimate: Boolean
  25. // Indicates if the drawing should not be animated (vs. the default of doing an animation)
  26. if(this.shapes){
  27. this._move(dontAnimate);
  28. }else{
  29. if(this.text){
  30. this._gauge.surface.rawNode.removeChild(this.text);
  31. this.text = null;
  32. }
  33. var a = this._gauge._getAngle(Math.min(Math.max(this.value, this._gauge.min), this._gauge.max));
  34. this.color = this.color || '#000000';
  35. this.length = this.length || this._gauge.radius;
  36. this.width = this.width || 1;
  37. this.offset = this.offset || 0;
  38. this.highlight = this.highlight || '#D0D0D0';
  39. this.shapes = this._getShapes(this._gauge, this);
  40. if(this.shapes){
  41. for(var s = 0; s < this.shapes.length; s++){
  42. this.shapes[s].setTransform([{dx:this._gauge.cx,dy:this._gauge.cy}, dojox.gfx.matrix.rotateg(a)]);
  43. if(this.hover){
  44. this.shapes[s].getEventSource().setAttribute('hover',this.hover);
  45. }
  46. if(this.onDragMove && !this.noChange){
  47. //TODO
  48. this._gauge.connect(this.shapes[s].getEventSource(), 'onmousedown', this._gauge.handleMouseDown);
  49. this.shapes[s].getEventSource().style.cursor = 'pointer';
  50. }
  51. }
  52. }
  53. if(this.label){
  54. var len=this.length+this.offset,
  55. rad=this._gauge._getRadians(a),
  56. x=this._gauge.cx+(len+5)*Math.sin(rad),
  57. y=this._gauge.cy-(len+5)*Math.cos(rad),
  58. align = 'start',
  59. aa = Math.abs(a)
  60. ;
  61. if(a <= -10){align = 'end';}
  62. if(aa < 10){align='middle';}
  63. var vAlign = 'bottom';
  64. if(aa > 90){vAlign = 'top';}
  65. this.text = this._gauge.drawText(''+this.label, x, y, align, vAlign, this.color, this.font);
  66. }
  67. this.currentValue = this.value;
  68. }
  69. },
  70. _move: function(/*Boolean?*/ dontAnimate){
  71. // summary:
  72. // Moves this indicator (since it's already been drawn once)
  73. // dontAnimate: Boolean
  74. // Indicates if the drawing should not be animated (vs. the default of doing an animation)
  75. var v = Math.min(Math.max(this.value, this._gauge.min), this._gauge.max),
  76. c = this.currentValue
  77. ;
  78. if(dontAnimate){
  79. var angle = this._gauge._getAngle(v);
  80. for(var i in this.shapes){
  81. this.shapes[i].setTransform([{dx:this._gauge.cx,dy:this._gauge.cy}, dojox.gfx.matrix.rotateg(angle)]);
  82. if(this.hover){
  83. this.shapes[i].getEventSource().setAttribute('hover',this.hover);
  84. }
  85. }
  86. }else{
  87. if(c!=v){
  88. var anim = new dojo.Animation({curve: [c, v], duration: this.duration, easing: this.easing});
  89. dojo.connect(anim, "onAnimate", dojo.hitch(this, function(step){
  90. for(var i in this.shapes){
  91. this.shapes[i].setTransform([{dx:this._gauge.cx,dy:this._gauge.cy}, dojox.gfx.matrix.rotateg(this._gauge._getAngle(step))]);
  92. if(this.hover){
  93. this.shapes[i].getEventSource().setAttribute('hover',this.hover);
  94. }
  95. }
  96. this.currentValue = step;
  97. }));
  98. anim.play();
  99. }
  100. }
  101. }
  102. });
  103. dojo.declare("dojox.widget.AnalogGauge",dojox.widget.gauge._Gauge,{
  104. // summary:
  105. // a gauge built using the dojox.gfx package.
  106. //
  107. // description:
  108. // using dojo.gfx (and thus either SVG or VML based on what is supported), this widget
  109. // builds a gauge component, used to display numerical data in a familiar format
  110. //
  111. // usage:
  112. // <script type="text/javascript">
  113. // dojo.require("dojox.widget.AnalogGauge");
  114. // dojo.require("dijit.util.parser");
  115. // </script>
  116. // ...
  117. // <div dojoType="dojox.widget.AnalogGauge"
  118. // id="testGauge"
  119. // width="300"
  120. // height="200"
  121. // cx=150
  122. // cy=175
  123. // radius=125
  124. // image="gaugeOverlay.png"
  125. // imageOverlay="false"
  126. // imageWidth="280"
  127. // imageHeight="155"
  128. // imageX="12"
  129. // imageY="38">
  130. // </div>
  131. // startAngle: Number
  132. // angle (in degrees) for start of gauge (default is -90)
  133. startAngle: -90,
  134. // endAngle: Number
  135. // angle (in degrees) for end of gauge (default is 90)
  136. endAngle: 90,
  137. // cx: Number
  138. // center of gauge x coordinate (default is gauge width / 2)
  139. cx: 0,
  140. // cy: Number
  141. // center of gauge x coordinate (default is gauge height / 2)
  142. cy: 0,
  143. // radius: Number
  144. // radius of gauge (default is smaller of cx-25 or cy-25)
  145. radius: 0,
  146. // _defaultIndicator: override of dojox.widget._Gauge._defaultIndicator
  147. _defaultIndicator: dojox.widget.gauge.AnalogLineIndicator,
  148. startup: function(){
  149. // handle settings from HTML by making sure all the options are
  150. // converted correctly to numbers and that we calculate defaults
  151. // for cx, cy and radius
  152. // also connects mouse handling events
  153. if(this.getChildren){
  154. dojo.forEach(this.getChildren(), function(child){ child.startup(); });
  155. }
  156. this.startAngle = Number(this.startAngle);
  157. this.endAngle = Number(this.endAngle);
  158. this.cx = Number(this.cx);
  159. if(!this.cx){this.cx = this.width/2;}
  160. this.cy = Number(this.cy);
  161. if(!this.cy){this.cy = this.height/2;}
  162. this.radius = Number(this.radius);
  163. if(!this.radius){this.radius = Math.min(this.cx,this.cy) - 25;}
  164. this._oppositeMiddle = (this.startAngle+this.endAngle)/2+180;
  165. this.inherited(arguments);
  166. },
  167. _getAngle: function(/*Number*/value){
  168. // summary:
  169. // This is a helper function used to determine the angle that represents
  170. // a given value on the gauge
  171. // value: Number
  172. // A value to be converted to an angle for this gauage.
  173. return (value - this.min)/(this.max - this.min)*(this.endAngle - this.startAngle) + this.startAngle;
  174. },
  175. _getValueForAngle: function(/*Number*/angle){
  176. // summary:
  177. // This is a helper function used to determie the value represented by a
  178. // given angle on the gauge
  179. // angle: Number
  180. // A angle to be converted to a value for this gauge.
  181. if(angle > this._oppositeMiddle){ angle -= 360; }
  182. return (angle - this.startAngle)*(this.max - this.min)/(this.endAngle - this.startAngle) + this.min;
  183. },
  184. _getRadians: function(/*Number*/angle){
  185. // summary:
  186. // This is a helper function than converts degrees to radians
  187. // angle: Number
  188. // An angle, in degrees, to be converted to radians.
  189. return angle*Math.PI/180;
  190. },
  191. _getDegrees: function(/*Number*/radians){
  192. // summary:
  193. // This is a helper function that converts radians to degrees
  194. // radians: Number
  195. // An angle, in radians, to be converted to degrees.
  196. return radians*180/Math.PI;
  197. },
  198. draw: function(){
  199. // summary:
  200. // This function is used to draw (or redraw) the gauge.
  201. // description:
  202. // Draws the gauge by drawing the surface, the ranges, and the indicators.
  203. var i;
  204. if(this._rangeData){
  205. for(i=0; i<this._rangeData.length; i++){
  206. this.drawRange(this._rangeData[i]);
  207. }
  208. if(this._img && this.image.overlay){
  209. this._img.moveToFront();
  210. }
  211. }
  212. if(this._indicatorData){
  213. for(i=0; i<this._indicatorData.length; i++){
  214. this._indicatorData[i].draw();
  215. }
  216. }
  217. },
  218. drawRange: function(/*Object*/range){
  219. // summary:
  220. // This function is used to draw (or redraw) a range
  221. // description:
  222. // Draws a range (colored area on the background of the gauge)
  223. // based on the given arguments.
  224. // range:
  225. // A range is a dojox.widget.gauge.Range or an object
  226. // with similar parameters (low, high, hover, etc.).
  227. var path;
  228. if(range.shape){
  229. this.surface.remove(range.shape);
  230. range.shape = null;
  231. }
  232. var a1, a2;
  233. if((range.low == this.min) && (range.high == this.max) && ((this.endAngle - this.startAngle) == 360)){
  234. path = this.surface.createCircle({cx: this.cx, cy: this.cy, r: this.radius});
  235. }else{
  236. a1 = this._getRadians(this._getAngle(range.low));
  237. a2 = this._getRadians(this._getAngle(range.high));
  238. var x1=this.cx+this.radius*Math.sin(a1),
  239. y1=this.cy-this.radius*Math.cos(a1),
  240. x2=this.cx+this.radius*Math.sin(a2),
  241. y2=this.cy-this.radius*Math.cos(a2),
  242. big=0
  243. ;
  244. if((a2-a1)>Math.PI){big=1;}
  245. path = this.surface.createPath();
  246. if(range.size){
  247. path.moveTo(this.cx+(this.radius-range.size)*Math.sin(a1),
  248. this.cy-(this.radius-range.size)*Math.cos(a1));
  249. }else{
  250. path.moveTo(this.cx,this.cy);
  251. }
  252. path.lineTo(x1,y1);
  253. path.arcTo(this.radius,this.radius,0,big,1,x2,y2);
  254. if(range.size){
  255. path.lineTo(this.cx+(this.radius-range.size)*Math.sin(a2),
  256. this.cy-(this.radius-range.size)*Math.cos(a2));
  257. path.arcTo((this.radius-range.size),(this.radius-range.size),0,big,0,
  258. this.cx+(this.radius-range.size)*Math.sin(a1),
  259. this.cy-(this.radius-range.size)*Math.cos(a1));
  260. }
  261. path.closePath();
  262. }
  263. if(dojo.isArray(range.color) || dojo.isString(range.color)){
  264. path.setStroke({color: range.color});
  265. path.setFill(range.color);
  266. }else if(range.color.type){
  267. // Color is a gradient
  268. a1 = this._getRadians(this._getAngle(range.low));
  269. a2 = this._getRadians(this._getAngle(range.high));
  270. range.color.x1 = this.cx+(this.radius*Math.sin(a1))/2;
  271. range.color.x2 = this.cx+(this.radius*Math.sin(a2))/2;
  272. range.color.y1 = this.cy-(this.radius*Math.cos(a1))/2;
  273. range.color.y2 = this.cy-(this.radius*Math.cos(a2))/2;
  274. path.setFill(range.color);
  275. path.setStroke({color: range.color.colors[0].color});
  276. }else{
  277. // We've defined a style rather than an explicit color
  278. path.setStroke({color: "green"}); // Arbitrary color, just have to indicate
  279. path.setFill("green"); // that we want it filled
  280. path.getEventSource().setAttribute("class", range.color.style);
  281. }
  282. if(range.hover){
  283. path.getEventSource().setAttribute('hover',range.hover);
  284. }
  285. range.shape = path;
  286. },
  287. getRangeUnderMouse: function(/*Object*/event){
  288. // summary:
  289. // Determines which range the mouse is currently over
  290. // event: Object
  291. // The event object as received by the mouse handling functions below.
  292. var range = null,
  293. pos = dojo.coords(this.gaugeContent),
  294. x = event.clientX - pos.x,
  295. y = event.clientY - pos.y,
  296. r = Math.sqrt((y - this.cy)*(y - this.cy) + (x - this.cx)*(x - this.cx))
  297. ;
  298. if(r < this.radius){
  299. var angle = this._getDegrees(Math.atan2(y - this.cy, x - this.cx) + Math.PI/2),
  300. //if(angle > this.endAngle){angle = angle - 360;}
  301. value = this._getValueForAngle(angle)
  302. ;
  303. if(this._rangeData){
  304. for(var i=0; (i<this._rangeData.length) && !range; i++){
  305. if((Number(this._rangeData[i].low) <= value) && (Number(this._rangeData[i].high) >= value)){
  306. range = this._rangeData[i];
  307. }
  308. }
  309. }
  310. }
  311. return range;
  312. },
  313. _dragIndicator: function(/*Object*/ widget, /*Object*/ event){
  314. // summary:
  315. // Handles the dragging of an indicator, including moving/re-drawing
  316. // get angle for mouse position
  317. var pos = dojo.coords(widget.gaugeContent),
  318. x = event.clientX - pos.x,
  319. y = event.clientY - pos.y,
  320. angle = widget._getDegrees(Math.atan2(y - widget.cy, x - widget.cx) + Math.PI/2),
  321. //if(angle > widget.endAngle){angle = angle - 360;}
  322. // get value and restrict to our min/max
  323. value = widget._getValueForAngle(angle)
  324. ;
  325. value = Math.min(Math.max(value, widget.min), widget.max);
  326. // update the indicator
  327. widget._drag.value = widget._drag.currentValue = value;
  328. // callback
  329. widget._drag.onDragMove(widget._drag);
  330. // rotate indicator
  331. widget._drag.draw(true);
  332. dojo.stopEvent(event);
  333. }
  334. });
  335. }