1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009 |
- // Copyright 2007 The Closure Library Authors. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS-IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- /**
- * @fileoverview Gauge UI component, using browser vector graphics.
- * @see ../demos/gauge.html
- */
- goog.provide('goog.ui.Gauge');
- goog.provide('goog.ui.GaugeColoredRange');
- goog.require('goog.a11y.aria');
- goog.require('goog.asserts');
- goog.require('goog.dom.TagName');
- goog.require('goog.events');
- goog.require('goog.fx.Animation');
- goog.require('goog.fx.Transition');
- goog.require('goog.fx.easing');
- goog.require('goog.graphics');
- goog.require('goog.graphics.Font');
- goog.require('goog.graphics.Path');
- goog.require('goog.graphics.SolidFill');
- goog.require('goog.math');
- goog.require('goog.ui.Component');
- goog.require('goog.ui.GaugeTheme');
- /**
- * Information on how to decorate a range in the gauge.
- * This is an internal-only class.
- * @param {number} fromValue The range start (minimal) value.
- * @param {number} toValue The range end (maximal) value.
- * @param {string} backgroundColor Color to fill the range background with.
- * @constructor
- * @final
- */
- goog.ui.GaugeColoredRange = function(fromValue, toValue, backgroundColor) {
- /**
- * The range start (minimal) value.
- * @type {number}
- */
- this.fromValue = fromValue;
- /**
- * The range end (maximal) value.
- * @type {number}
- */
- this.toValue = toValue;
- /**
- * Color to fill the range background with.
- * @type {string}
- */
- this.backgroundColor = backgroundColor;
- };
- /**
- * A UI component that displays a gauge.
- * A gauge displayes a current value within a round axis that represents a
- * given range.
- * The gauge is built from an external border, and internal border inside it,
- * ticks and labels inside the internal border, and a needle that points to
- * the current value.
- * @param {number} width The width in pixels.
- * @param {number} height The height in pixels.
- * @param {goog.dom.DomHelper=} opt_domHelper The DOM helper object for the
- * document we want to render in.
- * @constructor
- * @extends {goog.ui.Component}
- * @final
- */
- goog.ui.Gauge = function(width, height, opt_domHelper) {
- goog.ui.Component.call(this, opt_domHelper);
- /**
- * The width in pixels of this component.
- * @type {number}
- * @private
- */
- this.width_ = width;
- /**
- * The height in pixels of this component.
- * @type {number}
- * @private
- */
- this.height_ = height;
- /**
- * The underlying graphics.
- * @type {goog.graphics.AbstractGraphics}
- * @private
- */
- this.graphics_ =
- goog.graphics.createGraphics(width, height, null, null, opt_domHelper);
- /**
- * Colors to paint the background of certain ranges (optional).
- * @type {Array<goog.ui.GaugeColoredRange>}
- * @private
- */
- this.rangeColors_ = [];
- };
- goog.inherits(goog.ui.Gauge, goog.ui.Component);
- /**
- * Constant for a background color for a gauge area.
- */
- goog.ui.Gauge.RED = '#ffc0c0';
- /**
- * Constant for a background color for a gauge area.
- */
- goog.ui.Gauge.GREEN = '#c0ffc0';
- /**
- * Constant for a background color for a gauge area.
- */
- goog.ui.Gauge.YELLOW = '#ffffa0';
- /**
- * The radius of the entire gauge from the canvas size.
- * @type {number}
- */
- goog.ui.Gauge.FACTOR_RADIUS_FROM_SIZE = 0.45;
- /**
- * The ratio of internal gauge radius from entire radius.
- * The remaining area is the border around the gauge.
- * @type {number}
- */
- goog.ui.Gauge.FACTOR_MAIN_AREA = 0.9;
- /**
- * The ratio of the colored background area for value ranges.
- * The colored area width is computed as
- * InternalRadius * (1 - FACTOR_COLOR_RADIUS)
- * @type {number}
- */
- goog.ui.Gauge.FACTOR_COLOR_RADIUS = 0.75;
- /**
- * The ratio of the major ticks length start position, from the radius.
- * The major ticks length width is computed as
- * InternalRadius * (1 - FACTOR_MAJOR_TICKS)
- * @type {number}
- */
- goog.ui.Gauge.FACTOR_MAJOR_TICKS = 0.8;
- /**
- * The ratio of the minor ticks length start position, from the radius.
- * The minor ticks length width is computed as
- * InternalRadius * (1 - FACTOR_MINOR_TICKS)
- * @type {number}
- */
- goog.ui.Gauge.FACTOR_MINOR_TICKS = 0.9;
- /**
- * The length of the needle front (value facing) from the internal radius.
- * The needle front is the part of the needle that points to the value.
- * @type {number}
- */
- goog.ui.Gauge.FACTOR_NEEDLE_FRONT = 0.95;
- /**
- * The length of the needle back relative to the internal radius.
- * The needle back is the part of the needle that points away from the value.
- * @type {number}
- */
- goog.ui.Gauge.FACTOR_NEEDLE_BACK = 0.3;
- /**
- * The width of the needle front at the hinge.
- * This is the width of the curve control point, the actual width is
- * computed by the curve itself.
- * @type {number}
- */
- goog.ui.Gauge.FACTOR_NEEDLE_WIDTH = 0.07;
- /**
- * The width (radius) of the needle hinge from the gauge radius.
- * @type {number}
- */
- goog.ui.Gauge.FACTOR_NEEDLE_HINGE = 0.15;
- /**
- * The title font size (height) for titles relative to the internal radius.
- * @type {number}
- */
- goog.ui.Gauge.FACTOR_TITLE_FONT_SIZE = 0.16;
- /**
- * The offset of the title from the center, relative to the internal radius.
- * @type {number}
- */
- goog.ui.Gauge.FACTOR_TITLE_OFFSET = 0.35;
- /**
- * The formatted value font size (height) relative to the internal radius.
- * @type {number}
- */
- goog.ui.Gauge.FACTOR_VALUE_FONT_SIZE = 0.18;
- /**
- * The title font size (height) for tick labels relative to the internal radius.
- * @type {number}
- */
- goog.ui.Gauge.FACTOR_TICK_LABEL_FONT_SIZE = 0.14;
- /**
- * The offset of the formatted value down from the center, relative to the
- * internal radius.
- * @type {number}
- */
- goog.ui.Gauge.FACTOR_VALUE_OFFSET = 0.75;
- /**
- * The font name for title text.
- * @type {string}
- */
- goog.ui.Gauge.TITLE_FONT_NAME = 'arial';
- /**
- * The maximal size of a step the needle can move (percent from size of range).
- * If the needle needs to move more, it will be moved in animated steps, to
- * show a smooth transition between values.
- * @type {number}
- */
- goog.ui.Gauge.NEEDLE_MOVE_MAX_STEP = 0.02;
- /**
- * Time in miliseconds for animating a move of the value pointer.
- * @type {number}
- */
- goog.ui.Gauge.NEEDLE_MOVE_TIME = 400;
- /**
- * Tolerance factor for how much values can exceed the range (being too
- * low or too high). The value is presented as a position (percentage).
- * @type {number}
- */
- goog.ui.Gauge.MAX_EXCEED_POSITION_POSITION = 0.02;
- /**
- * The minimal value that can be displayed.
- * @private
- * @type {number}
- */
- goog.ui.Gauge.prototype.minValue_ = 0;
- /**
- * The maximal value that can be displayed.
- * @private
- * @type {number}
- */
- goog.ui.Gauge.prototype.maxValue_ = 100;
- /**
- * The number of major tick sections.
- * @private
- * @type {number}
- */
- goog.ui.Gauge.prototype.majorTicks_ = 5;
- /**
- * The number of minor tick sections in each major tick section.
- * @private
- * @type {number}
- */
- goog.ui.Gauge.prototype.minorTicks_ = 2;
- /**
- * The current value that needs to be displayed in the gauge.
- * @private
- * @type {number}
- */
- goog.ui.Gauge.prototype.value_ = 0;
- /**
- * The current value formatted into a String.
- * @private
- * @type {?string}
- */
- goog.ui.Gauge.prototype.formattedValue_ = null;
- /**
- * The current colors theme.
- * @private
- * @type {goog.ui.GaugeTheme?}
- */
- goog.ui.Gauge.prototype.theme_ = null;
- /**
- * Title to display above the gauge center.
- * @private
- * @type {?string}
- */
- goog.ui.Gauge.prototype.titleTop_ = null;
- /**
- * Title to display below the gauge center.
- * @private
- * @type {?string}
- */
- goog.ui.Gauge.prototype.titleBottom_ = null;
- /**
- * Font to use for drawing titles.
- * If null (default), computed dynamically with a size relative to the
- * gauge radius.
- * @private
- * @type {goog.graphics.Font?}
- */
- goog.ui.Gauge.prototype.titleFont_ = null;
- /**
- * Font to use for drawing the formatted value.
- * If null (default), computed dynamically with a size relative to the
- * gauge radius.
- * @private
- * @type {goog.graphics.Font?}
- */
- goog.ui.Gauge.prototype.valueFont_ = null;
- /**
- * Font to use for drawing tick labels.
- * If null (default), computed dynamically with a size relative to the
- * gauge radius.
- * @private
- * @type {goog.graphics.Font?}
- */
- goog.ui.Gauge.prototype.tickLabelFont_ = null;
- /**
- * The size in angles of the gauge axis area.
- * @private
- * @type {number}
- */
- goog.ui.Gauge.prototype.angleSpan_ = 270;
- /**
- * The radius for drawing the needle.
- * Computed on full redraw, and used on every animation step of moving
- * the needle.
- * @type {number}
- * @private
- */
- goog.ui.Gauge.prototype.needleRadius_ = 0;
- /**
- * The group elemnt of the needle. Contains all elements that change when the
- * gauge value changes.
- * @type {goog.graphics.GroupElement?}
- * @private
- */
- goog.ui.Gauge.prototype.needleGroup_ = null;
- /**
- * The current position (0-1) of the visible needle.
- * Initially set to null to prevent animation on first opening of the gauge.
- * @type {?number}
- * @private
- */
- goog.ui.Gauge.prototype.needleValuePosition_ = null;
- /**
- * Text labels to display by major tick marks.
- * @type {Array<string>?}
- * @private
- */
- goog.ui.Gauge.prototype.majorTickLabels_ = null;
- /**
- * Animation object while needle is being moved (animated).
- * @type {goog.fx.Animation?}
- * @private
- */
- goog.ui.Gauge.prototype.animation_ = null;
- /**
- * @return {number} The minimum value of the range.
- */
- goog.ui.Gauge.prototype.getMinimum = function() {
- return this.minValue_;
- };
- /**
- * Sets the minimum value of the range
- * @param {number} min The minimum value of the range.
- */
- goog.ui.Gauge.prototype.setMinimum = function(min) {
- this.minValue_ = min;
- var element = this.getElement();
- if (element) {
- goog.a11y.aria.setState(element, 'valuemin', min);
- }
- };
- /**
- * @return {number} The maximum value of the range.
- */
- goog.ui.Gauge.prototype.getMaximum = function() {
- return this.maxValue_;
- };
- /**
- * Sets the maximum number of the range
- * @param {number} max The maximum value of the range.
- */
- goog.ui.Gauge.prototype.setMaximum = function(max) {
- this.maxValue_ = max;
- var element = this.getElement();
- if (element) {
- goog.a11y.aria.setState(element, 'valuemax', max);
- }
- };
- /**
- * Sets the current value range displayed by the gauge.
- * @param {number} value The current value for the gauge. This value
- * determines the position of the needle of the gauge.
- * @param {string=} opt_formattedValue The string value to show in the gauge.
- * If not specified, no string value will be displayed.
- */
- goog.ui.Gauge.prototype.setValue = function(value, opt_formattedValue) {
- this.value_ = value;
- this.formattedValue_ = opt_formattedValue || null;
- this.stopAnimation_(); // Stop the active animation if exists
- // Compute desired value position (normalize value to range 0-1)
- var valuePosition = this.valueToRangePosition_(value);
- if (this.needleValuePosition_ == null) {
- // No animation on initial display
- this.needleValuePosition_ = valuePosition;
- this.drawValue_();
- } else {
- // Animate move
- this.animation_ = new goog.fx.Animation(
- [this.needleValuePosition_], [valuePosition],
- goog.ui.Gauge.NEEDLE_MOVE_TIME, goog.fx.easing.inAndOut);
- var events = [
- goog.fx.Transition.EventType.BEGIN, goog.fx.Animation.EventType.ANIMATE,
- goog.fx.Transition.EventType.END
- ];
- goog.events.listen(this.animation_, events, this.onAnimate_, false, this);
- goog.events.listen(
- this.animation_, goog.fx.Transition.EventType.END, this.onAnimateEnd_,
- false, this);
- // Start animation
- this.animation_.play(false);
- }
- var element = this.getElement();
- if (element) {
- goog.a11y.aria.setState(element, 'valuenow', this.value_);
- }
- };
- /**
- * Sets the number of major tick sections and minor tick sections.
- * @param {number} majorUnits The number of major tick sections.
- * @param {number} minorUnits The number of minor tick sections for each major
- * tick section.
- */
- goog.ui.Gauge.prototype.setTicks = function(majorUnits, minorUnits) {
- this.majorTicks_ = Math.max(1, majorUnits);
- this.minorTicks_ = Math.max(1, minorUnits);
- this.draw_();
- };
- /**
- * Sets the labels of the major ticks.
- * @param {Array<string>} tickLabels A text label for each major tick value.
- */
- goog.ui.Gauge.prototype.setMajorTickLabels = function(tickLabels) {
- this.majorTickLabels_ = tickLabels;
- this.draw_();
- };
- /**
- * Sets the top title of the gauge.
- * The top title is displayed above the center.
- * @param {string} text The top title text.
- */
- goog.ui.Gauge.prototype.setTitleTop = function(text) {
- this.titleTop_ = text;
- this.draw_();
- };
- /**
- * Sets the bottom title of the gauge.
- * The top title is displayed below the center.
- * @param {string} text The bottom title text.
- */
- goog.ui.Gauge.prototype.setTitleBottom = function(text) {
- this.titleBottom_ = text;
- this.draw_();
- };
- /**
- * Sets the font for displaying top and bottom titles.
- * @param {goog.graphics.Font} font The font for titles.
- */
- goog.ui.Gauge.prototype.setTitleFont = function(font) {
- this.titleFont_ = font;
- this.draw_();
- };
- /**
- * Sets the font for displaying the formatted value.
- * @param {goog.graphics.Font} font The font for displaying the value.
- */
- goog.ui.Gauge.prototype.setValueFont = function(font) {
- this.valueFont_ = font;
- this.drawValue_();
- };
- /**
- * Sets the color theme for drawing the gauge.
- * @param {goog.ui.GaugeTheme} theme The color theme to use.
- */
- goog.ui.Gauge.prototype.setTheme = function(theme) {
- this.theme_ = theme;
- this.draw_();
- };
- /**
- * Set the background color for a range of values on the gauge.
- * @param {number} fromValue The lower (start) value of the colored range.
- * @param {number} toValue The higher (end) value of the colored range.
- * @param {string} color The color name to paint the range with. For example
- * 'red', '#ffcc00' or constants like goog.ui.Gauge.RED.
- */
- goog.ui.Gauge.prototype.addBackgroundColor = function(
- fromValue, toValue, color) {
- this.rangeColors_.push(
- new goog.ui.GaugeColoredRange(fromValue, toValue, color));
- this.draw_();
- };
- /**
- * Creates the DOM representation of the graphics area.
- * @override
- */
- goog.ui.Gauge.prototype.createDom = function() {
- this.setElementInternal(
- this.getDomHelper().createDom(
- goog.dom.TagName.DIV, goog.getCssName('goog-gauge'),
- this.graphics_.getElement()));
- };
- /**
- * Clears the entire graphics area.
- * @private
- */
- goog.ui.Gauge.prototype.clear_ = function() {
- this.graphics_.clear();
- this.needleGroup_ = null;
- };
- /**
- * Redraw the entire gauge.
- * @private
- */
- goog.ui.Gauge.prototype.draw_ = function() {
- if (!this.isInDocument()) {
- return;
- }
- this.clear_();
- var x, y;
- var size = Math.min(this.width_, this.height_);
- var r = Math.round(goog.ui.Gauge.FACTOR_RADIUS_FROM_SIZE * size);
- var cx = this.width_ / 2;
- var cy = this.height_ / 2;
- var theme = this.theme_;
- if (!theme) {
- // Lazy allocation of default theme, common to all instances
- theme = goog.ui.Gauge.prototype.theme_ = new goog.ui.GaugeTheme();
- }
- // Draw main circle frame around gauge
- var graphics = this.graphics_;
- var stroke = this.theme_.getExternalBorderStroke();
- var fill = theme.getExternalBorderFill(cx, cy, r);
- graphics.drawCircle(cx, cy, r, stroke, fill);
- r -= stroke.getWidth();
- r = Math.round(r * goog.ui.Gauge.FACTOR_MAIN_AREA);
- stroke = theme.getInternalBorderStroke();
- fill = theme.getInternalBorderFill(cx, cy, r);
- graphics.drawCircle(cx, cy, r, stroke, fill);
- r -= stroke.getWidth() * 2;
- // Draw Background with external and internal borders
- var rBackgroundInternal = r * goog.ui.Gauge.FACTOR_COLOR_RADIUS;
- for (var i = 0; i < this.rangeColors_.length; i++) {
- var rangeColor = this.rangeColors_[i];
- var fromValue = rangeColor.fromValue;
- var toValue = rangeColor.toValue;
- var path = new goog.graphics.Path();
- var fromAngle = this.valueToAngle_(fromValue);
- var toAngle = this.valueToAngle_(toValue);
- // Move to outer point at "from" angle
- path.moveTo(
- cx + goog.math.angleDx(fromAngle, r),
- cy + goog.math.angleDy(fromAngle, r));
- // Arc to outer point at "to" angle
- path.arcTo(r, r, fromAngle, toAngle - fromAngle);
- // Line to inner point at "to" angle
- path.lineTo(
- cx + goog.math.angleDx(toAngle, rBackgroundInternal),
- cy + goog.math.angleDy(toAngle, rBackgroundInternal));
- // Arc to inner point at "from" angle
- path.arcTo(
- rBackgroundInternal, rBackgroundInternal, toAngle, fromAngle - toAngle);
- path.close();
- fill = new goog.graphics.SolidFill(rangeColor.backgroundColor);
- graphics.drawPath(path, null, fill);
- }
- // Draw titles
- if (this.titleTop_ || this.titleBottom_) {
- var font = this.titleFont_;
- if (!font) {
- // Lazy creation of font
- var fontSize = Math.round(r * goog.ui.Gauge.FACTOR_TITLE_FONT_SIZE);
- font = new goog.graphics.Font(fontSize, goog.ui.Gauge.TITLE_FONT_NAME);
- this.titleFont_ = font;
- }
- fill = new goog.graphics.SolidFill(theme.getTitleColor());
- if (this.titleTop_) {
- y = cy - Math.round(r * goog.ui.Gauge.FACTOR_TITLE_OFFSET);
- graphics.drawTextOnLine(
- this.titleTop_, 0, y, this.width_, y, 'center', font, null, fill);
- }
- if (this.titleBottom_) {
- y = cy + Math.round(r * goog.ui.Gauge.FACTOR_TITLE_OFFSET);
- graphics.drawTextOnLine(
- this.titleBottom_, 0, y, this.width_, y, 'center', font, null, fill);
- }
- }
- // Draw tick marks
- var majorTicks = this.majorTicks_;
- var minorTicks = this.minorTicks_;
- var rMajorTickInternal = r * goog.ui.Gauge.FACTOR_MAJOR_TICKS;
- var rMinorTickInternal = r * goog.ui.Gauge.FACTOR_MINOR_TICKS;
- var ticks = majorTicks * minorTicks;
- var valueRange = this.maxValue_ - this.minValue_;
- var tickValueSpan = valueRange / ticks;
- var majorTicksPath = new goog.graphics.Path();
- var minorTicksPath = new goog.graphics.Path();
- var tickLabelFill = new goog.graphics.SolidFill(theme.getTickLabelColor());
- var tickLabelFont = this.tickLabelFont_;
- if (!tickLabelFont) {
- tickLabelFont = new goog.graphics.Font(
- Math.round(r * goog.ui.Gauge.FACTOR_TICK_LABEL_FONT_SIZE),
- goog.ui.Gauge.TITLE_FONT_NAME);
- }
- var tickLabelFontSize = tickLabelFont.size;
- for (var i = 0; i <= ticks; i++) {
- var angle = this.valueToAngle_(i * tickValueSpan + this.minValue_);
- var isMajorTick = i % minorTicks == 0;
- var rInternal = isMajorTick ? rMajorTickInternal : rMinorTickInternal;
- var path = isMajorTick ? majorTicksPath : minorTicksPath;
- x = cx + goog.math.angleDx(angle, rInternal);
- y = cy + goog.math.angleDy(angle, rInternal);
- path.moveTo(x, y);
- x = cx + goog.math.angleDx(angle, r);
- y = cy + goog.math.angleDy(angle, r);
- path.lineTo(x, y);
- // Draw the tick's label for major ticks
- if (isMajorTick && this.majorTickLabels_) {
- var tickIndex = Math.floor(i / minorTicks);
- var label = this.majorTickLabels_[tickIndex];
- if (label) {
- x = cx + goog.math.angleDx(angle, rInternal - tickLabelFontSize / 2);
- y = cy + goog.math.angleDy(angle, rInternal - tickLabelFontSize / 2);
- var x1, x2;
- var align = 'center';
- if (angle > 280 || angle < 90) {
- align = 'right';
- x1 = 0;
- x2 = x;
- } else if (angle >= 90 && angle < 260) {
- align = 'left';
- x1 = x;
- x2 = this.width_;
- } else {
- // Values around top (angle 260-280) are centered around point
- var dw = Math.min(x, this.width_ - x); // Nearest side border
- x1 = x - dw;
- x2 = x + dw;
- y += Math.round(tickLabelFontSize / 4); // Movea bit down
- }
- graphics.drawTextOnLine(
- label, x1, y, x2, y, align, tickLabelFont, null, tickLabelFill);
- }
- }
- }
- stroke = theme.getMinorTickStroke();
- graphics.drawPath(minorTicksPath, stroke, null);
- stroke = theme.getMajorTickStroke();
- graphics.drawPath(majorTicksPath, stroke, null);
- // Draw the needle and the value label. Stop animation when doing
- // full redraw and jump to the final value position.
- this.stopAnimation_();
- this.needleRadius_ = r;
- this.drawValue_();
- };
- /**
- * Handle animation events while the hand is moving.
- * @param {goog.fx.AnimationEvent} e The event.
- * @private
- */
- goog.ui.Gauge.prototype.onAnimate_ = function(e) {
- this.needleValuePosition_ = e.x;
- this.drawValue_();
- };
- /**
- * Handle animation events when hand move is complete.
- * @private
- */
- goog.ui.Gauge.prototype.onAnimateEnd_ = function() {
- this.stopAnimation_();
- };
- /**
- * Stop the current animation, if it is active.
- * @private
- */
- goog.ui.Gauge.prototype.stopAnimation_ = function() {
- if (this.animation_) {
- goog.events.removeAll(this.animation_);
- this.animation_.stop(false);
- this.animation_ = null;
- }
- };
- /**
- * Convert a value to the position in the range. The returned position
- * is a value between 0 and 1, where 0 indicates the lowest range value,
- * 1 is the highest range, and any value in between is proportional
- * to mapping the range to (0-1).
- * If the value is not within the range, the returned value may be a bit
- * lower than 0, or a bit higher than 1. This is done so that values out
- * of range will be displayed just a bit outside of the gauge axis.
- * @param {number} value The value to convert.
- * @private
- * @return {number} The range position.
- */
- goog.ui.Gauge.prototype.valueToRangePosition_ = function(value) {
- var valueRange = this.maxValue_ - this.minValue_;
- var valuePct = (value - this.minValue_) / valueRange; // 0 to 1
- // If value is out of range, trim it not to be too much out of range
- valuePct = Math.max(valuePct, -goog.ui.Gauge.MAX_EXCEED_POSITION_POSITION);
- valuePct = Math.min(valuePct, 1 + goog.ui.Gauge.MAX_EXCEED_POSITION_POSITION);
- return valuePct;
- };
- /**
- * Convert a value to an angle based on the value range and angle span
- * @param {number} value The value.
- * @return {number} The angle where this value is located on the round
- * axis, based on the range and angle span.
- * @private
- */
- goog.ui.Gauge.prototype.valueToAngle_ = function(value) {
- var valuePct = this.valueToRangePosition_(value);
- return this.valuePositionToAngle_(valuePct);
- };
- /**
- * Convert a value-position (percent in the range) to an angle based on
- * the angle span. A value-position is a value that has been proportinally
- * adjusted to a value betwwen 0-1, proportionaly to the range.
- * @param {number} valuePct The value.
- * @return {number} The angle where this value is located on the round
- * axis, based on the range and angle span.
- * @private
- */
- goog.ui.Gauge.prototype.valuePositionToAngle_ = function(valuePct) {
- var startAngle = goog.math.standardAngle((360 - this.angleSpan_) / 2 + 90);
- return this.angleSpan_ * valuePct + startAngle;
- };
- /**
- * Draw the elements that depend on the current value (the needle and
- * the formatted value). This function is called whenever a value is changed
- * or when the entire gauge is redrawn.
- * @private
- */
- goog.ui.Gauge.prototype.drawValue_ = function() {
- if (!this.isInDocument()) {
- return;
- }
- var r = this.needleRadius_;
- var graphics = this.graphics_;
- var theme = this.theme_;
- var cx = this.width_ / 2;
- var cy = this.height_ / 2;
- var angle = this.valuePositionToAngle_(
- /** @type {number} */ (this.needleValuePosition_));
- // Compute the needle path
- var frontRadius = Math.round(r * goog.ui.Gauge.FACTOR_NEEDLE_FRONT);
- var backRadius = Math.round(r * goog.ui.Gauge.FACTOR_NEEDLE_BACK);
- var frontDx = goog.math.angleDx(angle, frontRadius);
- var frontDy = goog.math.angleDy(angle, frontRadius);
- var backDx = goog.math.angleDx(angle, backRadius);
- var backDy = goog.math.angleDy(angle, backRadius);
- var angleRight = goog.math.standardAngle(angle + 90);
- var distanceControlPointBase = r * goog.ui.Gauge.FACTOR_NEEDLE_WIDTH;
- var controlPointMidDx =
- goog.math.angleDx(angleRight, distanceControlPointBase);
- var controlPointMidDy =
- goog.math.angleDy(angleRight, distanceControlPointBase);
- var path = new goog.graphics.Path();
- path.moveTo(cx + frontDx, cy + frontDy);
- path.curveTo(
- cx + controlPointMidDx, cy + controlPointMidDy,
- cx - backDx + (controlPointMidDx / 2),
- cy - backDy + (controlPointMidDy / 2), cx - backDx, cy - backDy);
- path.curveTo(
- cx - backDx - (controlPointMidDx / 2),
- cy - backDy - (controlPointMidDy / 2), cx - controlPointMidDx,
- cy - controlPointMidDy, cx + frontDx, cy + frontDy);
- // Draw the needle hinge
- var rh = Math.round(r * goog.ui.Gauge.FACTOR_NEEDLE_HINGE);
- // Clean previous needle
- var needleGroup = this.needleGroup_;
- if (needleGroup) {
- needleGroup.clear();
- } else {
- needleGroup = this.needleGroup_ = graphics.createGroup();
- }
- // Draw current formatted value if provided.
- if (this.formattedValue_) {
- var font = this.valueFont_;
- if (!font) {
- var fontSize = Math.round(r * goog.ui.Gauge.FACTOR_VALUE_FONT_SIZE);
- font = new goog.graphics.Font(fontSize, goog.ui.Gauge.TITLE_FONT_NAME);
- font.bold = true;
- this.valueFont_ = font;
- }
- var fill = new goog.graphics.SolidFill(theme.getValueColor());
- var y = cy + Math.round(r * goog.ui.Gauge.FACTOR_VALUE_OFFSET);
- graphics.drawTextOnLine(
- this.formattedValue_, 0, y, this.width_, y, 'center', font, null, fill,
- needleGroup);
- }
- // Draw the needle
- var stroke = theme.getNeedleStroke();
- var fill = theme.getNeedleFill(cx, cy, rh);
- graphics.drawPath(path, stroke, fill, needleGroup);
- stroke = theme.getHingeStroke();
- fill = theme.getHingeFill(cx, cy, rh);
- graphics.drawCircle(cx, cy, rh, stroke, fill, needleGroup);
- };
- /**
- * Redraws the entire gauge.
- * Should be called after theme colors have been changed.
- */
- goog.ui.Gauge.prototype.redraw = function() {
- this.draw_();
- };
- /** @override */
- goog.ui.Gauge.prototype.enterDocument = function() {
- goog.ui.Gauge.superClass_.enterDocument.call(this);
- // set roles and states
- var el = this.getElement();
- goog.asserts.assert(el, 'The DOM element for the gauge cannot be null.');
- goog.a11y.aria.setRole(el, 'progressbar');
- goog.a11y.aria.setState(el, 'live', 'polite');
- goog.a11y.aria.setState(el, 'valuemin', this.minValue_);
- goog.a11y.aria.setState(el, 'valuemax', this.maxValue_);
- goog.a11y.aria.setState(el, 'valuenow', this.value_);
- this.draw_();
- };
- /** @override */
- goog.ui.Gauge.prototype.exitDocument = function() {
- goog.ui.Gauge.superClass_.exitDocument.call(this);
- this.stopAnimation_();
- };
- /** @override */
- goog.ui.Gauge.prototype.disposeInternal = function() {
- this.stopAnimation_();
- this.graphics_.dispose();
- delete this.graphics_;
- delete this.needleGroup_;
- delete this.theme_;
- delete this.rangeColors_;
- goog.ui.Gauge.superClass_.disposeInternal.call(this);
- };
|