Created
November 25, 2022 07:58
-
-
Save Mohammed-Samour-pr/0f97f7f20e34350a2fd97024b1d4f88c to your computer and use it in GitHub Desktop.
water tank v2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div id="empty-space"></div> | |
<div id="wrapper" class="wrapper"> | |
</div> | |
<button id="onetext" onclick="setOneText()">Set one text</button> | |
<button id="twotext" onclick="setTwoText()">Set two texts</button> | |
<br> | |
<br> | |
<button id="color" onclick="fillColor()">Fill Color</button> | |
<button id="color" onclick="backFontColor()">Back Font Color</button> | |
<button id="color" onclick="frontFontColor()">Front Font Color</button> | |
<button id="color" onclick="topFillBackFontColor()">Top Fill Back Font Color</button> | |
<button id="color" onclick="bottomFillBackFontColor()">Bottom Fill Back Font Color</button> | |
<button id="color" onclick="topFillColor()">Top Fill Color</button> | |
<button id="color" onclick="bottomFillColor()">Bottom Fill Color</button> | |
<br> | |
<br> | |
<button id="shrink" onclick="shrink()">Shrink</button> | |
<button id="enlarge" onclick="enlarge()">Enlarge</button> | |
<br> | |
<br> | |
<button id="high" onclick="high()">High</button> | |
<button id="mid" onclick="mid()">Mid</button> | |
<button id="low" onclick="low()">Low</button> | |
<br> | |
<br> | |
<button onclick="addArrow()">Add arrow</button> | |
<button onclick="removeArrow()">Remove arrow</button> | |
<button onclick="upArrow()">Up arrow</button> | |
<button onclick="downArrow()">Down arrow</button> | |
<button onclick="defaultArrow()">Default arrow</button> | |
<button onclick="topFillArrowColor()">Top fill arrow color</button> | |
<button onclick="bottomFillBackArrowColor()">Bottom fill arrow color</button> | |
<button onclick="frontArrowColor()">Front arrow color</button> | |
<button onclick="backArrowColor()">Back arrow color</button> | |
<br> | |
<br> | |
<button onclick="setDecimalZero()">No Decimal</button> | |
<button onclick="setDecimalOne()">One Decimal</button> | |
<button onclick="setDecimalTwo()">Two Decimal</button> | |
<br> | |
<br> | |
<button onclick="tower()">Tower</button> | |
<button onclick="round()">Round</button> | |
<br> | |
<br> | |
<button onclick="destroy()">Destroy</button> | |
<br> | |
<br> | |
<button onclick="updateLookupTableValue()">Set Random Lookup Value</button> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var log = console.log.bind(console); | |
console.clear(); | |
(function ($) { | |
$.fn.analogTank = function (config) { | |
let $this = $(this); | |
return new AnalogTank($this, config); | |
}; | |
class AnalogTank { | |
constructor($this, config) { | |
let defaults = { | |
tankType: "tower", // available types: 'tower', 'round' | |
tankWidth: null, // outside width. | |
tankHeight: null, // outside height. | |
fillPadding: null, // gap between perimeter and inside tank area that displays water. | |
borderWidth: 2, // perimeter width. | |
borderColor: "#333", // outside border color. usually the perimeter of the tank | |
defaultFillColor: "#3fabd4", // default water color. this is assigned to fillColor if water level does not pass any thresholds. | |
fillColor: null, // used later to set water color. it could be different color depending on situations. | |
backFillColor: "#fafafa", // background color inside the tank where there is no water. | |
backFillOpacity: 1, // opacity of the background. | |
innerCornerRadius: 3, | |
borderCornerRadius: 5, | |
innerWidth: null, | |
innerHeight: null, | |
neckWidth: 50, // only applies to round tank | |
neckHeight: 50, // only applies to round tank | |
fillAnimationColor: null, // used later to set the color while animating. | |
fillMaxValue: 100, // maximum possible value for the main text. | |
fillMinValue: 0, // minimum possible value for the main text. | |
fillValue: null, // value used to display the main text. | |
fillUnit: null, // unit that is appended to the main text. | |
decimal: 1, // number of decimal places for the main text. | |
overlayTextFillOpacity: 0.8, // opacity of the main text. | |
arrow: true, // arrow that is displayed to the right of the main text. | |
fontFamily: "Helvetica", | |
fontWeight: "bold", | |
fontSize: 20, | |
backFontColor: null, | |
backFontAnimationColor: null, | |
frontFontColor: null, | |
waveWidth: 100, | |
amplitude: 3, | |
horizontalWaveDuration: 2000, | |
transitionDuration: 1000, | |
delay: 0, | |
ease: d3.easePolyInOut.exponent(4), | |
marker: true, | |
markerPosition: "in", | |
markerGap: [5, 3], | |
markerLabelXOffset: 0, | |
markerLabelYOffset: 0, | |
markerWidth: 2, | |
markerLength: 10, | |
topMarkerText: null, | |
topMarkerColor: "#133440", | |
topMarkerFontColor: "#133440", | |
bottomMarkerText: null, | |
bottomMarkerColor: "#133440", | |
bottomMarkerFontColor: "#133440", | |
markerFontSize: 10, | |
markerFontWeight: "bold", | |
markerFontFamily: "Helvetica", | |
enableSupportLabel: true, | |
supportLabelFontColor: "#133440", | |
supportLabelFontFamily: "Helvetica", | |
supportLabelFontWeight: "bold", | |
supportLabelFontSize: 14, | |
supportLabelText: "NA", | |
supportLabelYOffset: -1, | |
mergeSupportLabelToBorder: false, | |
dualSupportLabel: false, | |
topSupportLabelFontColor: "#133440", | |
topSupportLabelFontFamily: "Helvetica", | |
topSupportLabelFontWeight: "bold", | |
topSupportLabelFontSize: 14, | |
topSupportLabelText: "NA", | |
topSupportLabelYOffset: -1, | |
enableSupportLabelBg: true, | |
supportLabelBackgroundColor: "#fff", | |
supportLabelBackgroundOpacity: 0.7, | |
supportLabelBackgroundHeight: null, | |
supportLabelBackgroundWidth: null, | |
supportLabelBackgroundBorderWidth: 1, | |
supportLabelBackgroundBorderColor: null, | |
supportLabelPadding: 0, | |
supportLabelWidthFix: 0, | |
arrowName: null, | |
upArrowName: "\uf176", | |
downArrowName: "\uf175", | |
noArrowName: "\uf07e", | |
arrowFontFamily: "FontAwesome", | |
arrowFontWeight: "bold", | |
arrowFontSize: 12, | |
arrowXOffset: 3, | |
arrowYOffset: -1, | |
topFillBackArrowColor: null, | |
bottomFillBackArrowColor: null, | |
frontArrowColor: null, | |
backArrowColor: null, | |
markerBarXOffset: 3, | |
tooltipFontSize: 10, | |
thresholds: [], | |
lookupTableValue: null, // if lookup table value is set, a secondary text is displayed under the main text. | |
lookupTableValueUnit: null, // unit for lookupTableValue. | |
lookupTableValueDecimal: 0, // number of decimal places for lookup table value. | |
lookupTableValueEnabled: false, | |
lookupTableValueFontSize: 14, | |
lookupTableValueYOffset: 2, | |
changeRateValueArrowEnabled: false, | |
changeRateValueArrowYOffset: 0, | |
changeRateValue: null, | |
changeRateValueDecimal: 0, | |
changeRateValueEnabled: false, | |
changeRateValueFontSize: 14, | |
changeRateValueYOffset: 2, | |
changeRateValueUnit: 'gal/min' | |
}; | |
Object.assign(defaults, config); | |
this.container = $this; | |
Object.assign(this, defaults); | |
this.url = window.location.href; | |
if (this.tankType !== 'tower' && this.tankType !== 'round') { | |
throw new Error(`Unknown Tank Type specified: ${this.tankType}. Should be either 'tower' or 'round'.`); | |
} | |
this.init(); | |
} | |
// initializer | |
init() { | |
this.setInitialValues(); | |
this.drawSvgContainer(); | |
if ( this.tankType === "tower" ) { | |
this.initTower(); | |
} else if ( this.tankType === "round" ) { | |
this.initRound(); | |
} | |
this.setMarkerAttributes(); | |
this.calculateDimensions(); | |
this.setGaugeScale(); | |
this.getNewHeight(); | |
this.addThresholdMarkers(); | |
this.applyFillAttributes(); | |
this.applyTextAttributes(); | |
this.applyWaveHorizontalAttributes(); | |
this.applySupportLabelAttributes(); | |
this.updateArrowPosition(); | |
this.tweenWaveHorizontal(); | |
this.animateFromZero(); | |
this.repositionElements(); | |
this.setBisector(); | |
this.hover(); | |
} | |
drawSvgContainer() { | |
this.width = this.container.outerWidth(); | |
this.height = this.container.outerHeight(); | |
let viewBoxDef = `0, 0, ${this.width}, ${this.height}`; | |
this.svgContainer = d3.select(this.container[0]) | |
.attr('id', 'svg-container') | |
.append("svg") | |
.attr("width", "100%") | |
.attr("height", "100%") | |
.attr("viewBox", viewBoxDef); | |
this.bodyGroup = this.svgContainer.append('g') | |
.attr('id', 'body-group') | |
.attr('transform', `translate(${this.width/2}, ${this.height/2})`); | |
} | |
// scale that returns pixel value for positioning the waveClip vertically | |
setGaugeScale () { | |
this.gaugeScale = d3.scaleLinear() | |
.domain([this.fillMinValue, this.fillMaxValue]) | |
.range([(this.innerHeight + this.amplitude)/2, -(this.innerHeight + this.amplitude)/2]) | |
.clamp(true); | |
} | |
getNewHeight () { | |
this.newHeight = this.fillValue === null ? 0 : this.gaugeScale(this.fillValue); | |
} | |
initTower () { | |
let uniqId = this.uniqId(); | |
// this.tankGroup = this.bodyGroup.append('g').attr('id', 'tank-group'); | |
this.waveClip = this.bodyGroup.append('defs').append('clipPath').attr('id', uniqId); | |
this.waveHorizontal = this.waveClip.append('path'); | |
this.backFill = this.bodyGroup.append('rect').attr('id', 'back-fill'); | |
this.border = this.bodyGroup.append('rect').attr('id', 'border'); | |
this.behindText = this.bodyGroup.append('text').attr('id', 'behind-text'); | |
this.behindArrow = this.bodyGroup.append('text').attr('id', 'behind-arrow'); | |
if (this.lookupTableValueEnabled) { | |
this.lookupTableValueBehindText = this.bodyGroup.append('text').attr('id', 'lookup-value-behind-text'); | |
} | |
if (this.changeRateValueEnabled) { | |
this.changeRateValueBehindText = this.bodyGroup.append('text').attr('id', 'change-rate-value-behind-text'); | |
} | |
this.waveGroup = this.bodyGroup.append('g').attr('clip-path', this.getUniqUrl(uniqId)); | |
this.waterFill = this.waveGroup.append('rect').attr('id', 'water-fill'); | |
this.overlayText = this.waveGroup.append('text').attr('id', 'overlay-text'); | |
this.overlayArrow = this.waveGroup.append('text').attr('id', 'overlay-arrow'); | |
if (this.lookupTableValueEnabled) { | |
this.lookupTableValueOverlayText = this.waveGroup.append('text').attr('id', 'lookup-value-overlay-text'); | |
} | |
if (this.changeRateValueEnabled) { | |
this.changeRateValueOverlayText = this.waveGroup.append('text').attr('id', 'change-rate-value-overlay-text'); | |
} | |
this.supportLabelGroup = this.bodyGroup.append('g').attr('id', 'support-label-group'); | |
this.supportLabelBg = this.supportLabelGroup.append('rect').attr('id', 'support-label-bg'); | |
this.supportLabel = this.supportLabelGroup.append('text').attr('id', 'overlay-support-label'); | |
this.topSupportLabel = this.supportLabelGroup.append('text').attr('id', 'top-overlay-support-label'); | |
this.topMarkerLabel = this.bodyGroup.append('text').attr('id', 'top-marker-label'); | |
this.bottomMarkerLabel = this.bodyGroup.append('text').attr('id', 'bottom-marker-label'); | |
this.markerBarGroup = this.bodyGroup.append('g').attr('id', 'marker-bar-group'); | |
// for debug purpose | |
// this.bodyGroup.append('path').attr('d', 'M-${this.height/2} 0 L ${this.height} 0').attr('stroke-width', 1).attr('stroke', 'red'); | |
} | |
initRound () { | |
let that = this; | |
let uniqId = this.uniqId(); | |
this.tankGroup = this.bodyGroup.append('g').attr('id', 'tank-group'); | |
this.waveClip = this.bodyGroup.append('defs').append('clipPath').attr('id', uniqId); | |
this.waveHorizontal = this.waveClip.append('path'); | |
this.backFill = this.tankGroup.append('ellipse').attr('id', 'back-fill'); | |
this.border = this.tankGroup.append('ellipse').attr('id', 'border'); | |
this.neckBackFill = this.tankGroup.append('path').attr('id', 'neck-back-fill'); | |
this.behindText = this.tankGroup.append('text').attr('id', 'behind-text'); | |
this.behindArrow = this.tankGroup.append('text').attr('id', 'behind-arrow'); | |
if (this.lookupTableValueEnabled) { | |
this.lookupTableValueBehindText = this.tankGroup.append('text').attr('id', 'lookup-value-behind-text'); | |
} | |
if (this.changeRateValueEnabled) { | |
this.changeRateValueBehindText = this.tankGroup.append('text').attr('id', 'lookup-value-rate-behind-text'); | |
} | |
this.waveGroup = this.tankGroup.append('g').attr('clip-path', this.getUniqUrl(uniqId)); | |
this.neckFill = this.waveGroup.append('path').attr('id', 'neck-fill'); | |
this.waterFill = this.waveGroup.append('ellipse').attr('id', 'water-fill'); | |
this.neckBorder = this.tankGroup.append('path').attr('id', 'neck-border'); | |
this.overlayText = this.waveGroup.append('text').attr('id', 'overlay-text'); | |
this.overlayArrow = this.waveGroup.append('text').attr('id', 'overlay-arrow'); | |
if (this.lookupTableValueEnabled) { | |
this.lookupTableValueOverlayText = this.waveGroup.append('text').attr('id', 'lookup-value-overlay-text'); | |
} | |
if (this.changeRateValueEnabled) { | |
this.changeRateValueOverlayText = this.waveGroup.append('text').attr('id', 'lookup-value-rate-overlay-text'); | |
} | |
this.supportLabelGroup = this.bodyGroup.append('g').attr('id', 'support-label-group'); | |
this.supportLabelBg = this.supportLabelGroup.append('rect').attr('id', 'support-label-bg'); | |
this.supportLabel = this.supportLabelGroup.append('text').attr('id', 'overlay-support-label'); | |
this.topSupportLabel = this.supportLabelGroup.append('text').attr('id', 'top-overlay-support-label'); | |
this.topMarker = this.tankGroup.append('line').attr('id', 'top-marker'); | |
this.bottomMarker = this.tankGroup.append('line').attr('id', 'bottom-marker'); | |
this.topMarkerLabel = this.tankGroup.append('text').attr('id', 'top-marker-label'); | |
this.bottomMarkerLabel = this.tankGroup.append('text').attr('id', 'bottom-marker-label'); | |
this.markerBarGroup = this.tankGroup.append('g').attr('id', 'marker-bar-group'); | |
} | |
// sets the inital text and color to display based on fillValue | |
setInitialValues () { | |
this.lookupTableValueEnabled = this.lookupTableValue !== null ? true : false; | |
this.changeRateValueEnabled = this.changeRateValue !== null ? true : false; | |
this.fillColor = this.defaultFillColor; | |
this.topMarkerText = this.topMarkerText === null ? this.fillMaxValue.toString() : this.topMarkerText; | |
this.bottomMarkerText = this.bottomMarkerText === null ? this.fillMinValue.toString() : this.bottomMarkerText; | |
this.topMarkerFontColor = this.tankType === 'tower' ? '#000' : '#fafafa'; | |
this.bottomMarkerFontColor = this.tankType === 'tower' ? '#000' : '#fafafa'; | |
} | |
setMarkerAttributes() { | |
this.applyAttributes(this.topMarkerLabel, { | |
'text-anchor': 'end', | |
'font-family': this.markerFontFamily, | |
'font-size': `${this.markerFontSize}px`, | |
fill: this.topMarkerFontColor, | |
'font-weight': this.markerFontWeight, | |
text: this.topMarkerText | |
}); | |
this.applyAttributes(this.bottomMarkerLabel, { | |
'text-anchor': 'end', | |
'font-family': this.markerFontFamily, | |
'font-size': `${this.markerFontSize}px`, | |
fill: this.bottomMarkerFontColor, | |
'font-weight': this.markerFontWeight, | |
text: this.bottomMarkerText | |
}); | |
} | |
calculateDimensions() { | |
this.markerLabelWidth = this.getMaxWidth(this.topMarkerLabel, this.bottomMarkerLabel); | |
let markerGapSum = this.markerGap[0] + this.markerGap[1]; | |
if (this.tankType === 'tower') { | |
this.tankWidth = this.width - this.borderWidth; | |
this.tankHeight = this.height - this.borderWidth; | |
if ( this.fillPadding !== null && typeof this.fillPadding === "number" && this.fillPadding !== 0 ) { | |
this.innerWidth = this.tankWidth - 2*this.fillPadding; // in case there is a padding, this will be the inner fill part | |
this.innerHeight = this.tankHeight - 2*this.fillPadding; // same as above | |
} else { | |
this.innerWidth = this.tankWidth - this.borderWidth; | |
this.innerHeight = this.tankHeight - this.borderWidth; | |
} | |
} else if (this.tankType === 'round') { | |
this.tankWidth = this.width - 2*(this.borderWidth + this.markerLabelWidth); // this is how wide the tank will draw | |
this.tankHeight = this.height - this.borderWidth - this.neckHeight; // this is how tall the round part of the tank will draw | |
if ( this.fillPadding !== null && typeof this.fillPadding === "number" && this.fillPadding !== 0 ) { | |
this.innerWidth = this.tankWidth - 2*this.fillPadding; // in case there is a padding, this will be the inner fill part | |
this.innerHeight = this.tankHeight - 2*this.fillPadding; // same as above | |
} else { | |
this.innerWidth = this.tankWidth - this.borderWidth; | |
this.innerHeight = this.tankHeight - this.borderWidth; | |
} | |
//subtract the support height use the minimum value | |
this.tankRx = this.tankWidth/2; | |
this.tankRy = this.tankHeight/2; | |
this.innerRx = this.innerWidth/2; | |
this.innerRy = this.innerHeight/2; | |
} | |
} | |
addThresholdMarkers() { | |
let topPixelPosition = this.gaugeScale(this.fillMaxValue) + this.borderWidth; | |
let color = this.tankType === 'round' ? '#fafafa' : '#000'; | |
if (this.tankType === 'round') { | |
this.markerBarGroup.append('line') | |
.attr('id', 'top-edge-marker') | |
.attr('x1', -this.markerLength-4) | |
.attr('x2', 1) | |
.attr('y1', topPixelPosition) | |
.attr('y2', topPixelPosition) | |
.attr('stroke-width', 1) | |
.attr('stroke', color) | |
.attr('stroke-linecap', 'round'); | |
let bottomPixelPosition = this.gaugeScale(this.fillMinValue) - this.borderWidth; | |
this.markerBarGroup.append('line') | |
.attr('id', 'bottom-edge-marker') | |
.attr('x1', -this.markerLength-4) | |
.attr('x2', 1) | |
.attr('y1', bottomPixelPosition) | |
.attr('y2', bottomPixelPosition) | |
.attr('stroke-width', 1) | |
.attr('stroke', color) | |
.attr('stroke-linecap', 'round'); | |
this.markerBar = this.markerBarGroup.append('line') | |
.attr('id', 'marker-bar') | |
.attr('x1', 1) | |
.attr('x2', 1) | |
.attr('y1', topPixelPosition) | |
.attr('y2', bottomPixelPosition) | |
.attr('stroke-width', 1) | |
.attr('stroke', color); | |
} | |
this.thresholdMarkerPositions = []; | |
this.thresholdTooltips = []; | |
this.thresholdMarkers = []; | |
this.thresholds.forEach( (threshold, i) => { | |
let id = this.uniqId(); | |
let pixelPosition = this.gaugeScale(threshold.value); | |
let marker = this.markerBarGroup.append('line') | |
.datum({ yCoord: pixelPosition, strokeWidth: this.markerWidth, x1: -this.markerLength }) | |
.attr('id', `threshold-marker-${id}`) | |
.attr('x1', -this.markerLength) | |
.attr('x2', 0) | |
.attr('y1', pixelPosition) | |
.attr('y2', pixelPosition) | |
.attr('stroke-width', this.markerWidth) | |
.attr('stroke', threshold.alarm ? 'red' : color); | |
let tooltip = d3.select(this.container[0]).append('div') | |
.datum({ name: threshold.name, value: threshold.value, type: threshold.type }) | |
.html(function(d) { return '<div>Name: ' + d.name + '</div>' + '<div>Value: ' + d.value + '</div>' + '<div>Type: ' + d.type + '</div>'; }) | |
.attr('id', `tooltip-${id}`) | |
.style('position', 'absolute') | |
.style('right', `${this.markerLabelWidth*2}px`) | |
.style('top', `${pixelPosition + this.innerHeight/2 - 30}px`) | |
.style('padding', '8px') | |
.style('background', 'rgba(97,97,97,0.9)') | |
.style('color', '#fff') | |
.style('font-family', "'Roboto', 'Helvetica', 'Arial', sans-serif") | |
.style('font-size', '10px') | |
.style('display', 'initial') | |
.style('-webkit-animation', 'pulse 200ms cubic-bezier(0, 0, 0.2, 1) forwards') | |
.style('animation', 'pulse 200ms cubic-bezier(0, 0, 0.2, 1) forwards'); | |
if (this.thresholdMarkers.length > 0) { | |
this.thresholdMarkerPositions.push({ yCoord: (this.thresholdMarkers[i-1].datum().yCoord + pixelPosition)/2 }); | |
} | |
this.thresholdTooltips.push(tooltip); | |
this.thresholdMarkers.push(marker); | |
tooltip.style('display', 'none'); | |
}); | |
} | |
applyFillAttributes() { | |
if (this.tankType === 'tower') { | |
this.applyAttributes(this.backFill, { | |
x: 0, | |
y: 0, | |
width: this.innerWidth, | |
height: this.innerHeight, | |
rx: this.innerCornerRadius, | |
ry: this.innerCornerRadius, | |
fill: this.backFillColor, | |
'fill-opacity': this.backFillOpacity, | |
transform: `translate(-${(this.tankWidth - this.borderWidth)/2}, -${(this.tankHeight - this.borderWidth)/2})` | |
}); | |
this.applyAttributes(this.waterFill, { | |
datum: { color: this.fillColor }, | |
x: 0, | |
y: 0, | |
width: this.innerWidth, | |
height: this.innerHeight, | |
rx: this.innerCornerRadius, | |
ry: this.innerCornerRadius, | |
fill: function(d) { return d.color; }, | |
transform: `translate(-${(this.tankWidth - this.borderWidth)/2}, -${(this.tankHeight - this.borderWidth)/2})` | |
}); | |
this.applyAttributes(this.border, { | |
x: 0, | |
y: 0, | |
width: this.tankWidth, | |
height: this.tankHeight, | |
rx: this.borderCornerRadius, | |
ry: this.borderCornerRadius, | |
'fill-opacity': 0, | |
stroke: this.borderColor, | |
'stroke-width': this.borderWidth, | |
transform: `translate(-${this.tankWidth/2}, -${this.tankHeight/2})` | |
}); | |
} else if (this.tankType === 'round') { | |
this.applyAttributes(this.tankGroup, { | |
transform: `translate(0, -${this.height/2 - this.tankRy - this.borderWidth/2})` | |
}); | |
this.applyAttributes(this.backFill, { | |
cx: 0, | |
cy: 0, | |
rx: this.innerRx, | |
ry: this.innerRy, | |
fill: this.backFillColor | |
}); | |
this.applyAttributes(this.waterFill, { | |
datum: { color: this.fillColor }, | |
cx: 0, | |
cy: 0, | |
rx: this.innerRx, | |
ry: this.innerRy, | |
fill: this.fillColor | |
}); | |
this.applyAttributes(this.border, { | |
cx: 0, | |
cy: 0, | |
rx: this.tankRx, | |
ry: this.tankRy, | |
'fill-opacity': 0, | |
stroke: this.borderColor, | |
'stroke-width': this.borderWidth | |
}); | |
let xCoord = this.getXCoordOfEllipse(this.tankRy/8*7); | |
let topRight = `${xCoord} ${this.tankRy/8*7}`; | |
let bottomRight = `${this.tankRx/4} ${this.height - this.tankRy - this.borderWidth/2}`; | |
let topLeft = `-${xCoord} ${this.tankRy/8*7}`; | |
let bottomLeft = `-${this.tankRx/4} ${this.height - this.tankRy - this.borderWidth/2}`; | |
let topRightInflectionPt = `${this.tankRx/4} ${this.tankRy}`; | |
let bottomRightInflectionPt = `${this.tankRx/4} ${this.tankRy}`; | |
let topLeftInflectionPt = `-${this.tankRx/4} ${this.tankRy}`; | |
let bottomLeftInflectionPt = `-${this.tankRx/4} ${this.tankRy}`; | |
// for debug purpose | |
// let topRightInfxPt = this.tankGroup.append('circle').attr('r', 2).attr('fill','red').attr('transform', `translate(${topRightInflectionPt})`); | |
// let bottomRightInfxPt = this.tankGroup.append('circle').attr('r', 2).attr('fill','red').attr('transform', `translate(${bottomRightInflectionPt})`); | |
let neckFillPathDef = `M${topRight}, C${topRightInflectionPt}, ${bottomRightInflectionPt}, ${bottomRight}, L${bottomLeft}, C${bottomLeftInflectionPt}, ${topLeftInflectionPt}, ${topLeft} Z`; | |
let neckBorderDef = `M${topRight}, C${topRightInflectionPt}, ${bottomRightInflectionPt}, ${bottomRight}, L${bottomLeft}, C${bottomLeftInflectionPt}, ${topLeftInflectionPt}, ${topLeft}`; | |
this.applyAttributes(this.neckFill, { | |
datum: { color: this.fillColor }, | |
d: neckFillPathDef, | |
fill: this.fillColor, | |
}); | |
this.applyAttributes(this.neckBorder, { | |
d: neckBorderDef, | |
stroke: this.borderColor, | |
fill: 'transparent', | |
'stroke-width': this.borderWidth | |
}); | |
this.applyAttributes(this.neckBackFill, { | |
d: neckFillPathDef, | |
fill: this.backFillColor, | |
'stroke-width': 0 | |
}); | |
} | |
} | |
applyTextAttributes() { | |
let transform = `translate(0, ${this.fontSize/4})`; | |
this.applyAttributes(this.behindText, { | |
datum: { color: this.backFontColor === null ? this.fillColor : this.backFontColor }, | |
'text-anchor': 'middle', | |
'font-family': this.fontFamily, | |
'font-size': `${this.fontSize}px`, | |
'font-weight': this.fontWeight, | |
fill: function(d) { return d.color; }, | |
text: `0 ${this.fillUnit}`, | |
transform: transform | |
}); | |
this.applyAttributes(this.behindArrow, { | |
datum: { color: this.backFontColor === null ? this.fillColor : this.backFontColor }, | |
'text-anchor': 'middle', | |
'font-family': this.arrowFontFamily, | |
'font-size': `${this.arrowFontSize}px`, | |
'font-weight': this.arrowFontWeight, | |
fill: function(d) { return d.color; }, | |
text: `${this.arrowName === null ? this.noArrowName : this.arrowName}`, | |
}); | |
this.applyAttributes(this.overlayText, { | |
datum: { color: this.frontFontColor === null ? "#fff" : this.frontFontColor }, | |
'text-anchor': 'middle', | |
'font-family': this.fontFamily, | |
'font-size': `${this.fontSize}px`, | |
'font-weight': this.fontWeight, | |
fill: function(d) { return d.color; }, | |
'fill-opacity': this.overlayTextFillOpacity, | |
text: `0 ${this.fillUnit}`, | |
transform: transform | |
}); | |
this.applyAttributes(this.overlayArrow, { | |
datum: { color: this.frontFontColor }, | |
'text-anchor': 'middle', | |
'font-family': this.arrowFontFamily, | |
'font-size': `${this.arrowFontSize}px`, | |
'font-weight': this.arrowFontWeight, | |
fill: function(d) { return d.color; }, | |
text: `${this.arrowName === null ? this.noArrowName : this.arrowName}`, | |
}); | |
if (this.lookupTableValueEnabled) { | |
let lookupTransform = `translate(0, ${this.fontSize/4 + this.lookupTableValueFontSize + this.lookupTableValueYOffset})`; | |
this.applyAttributes(this.lookupTableValueBehindText, { | |
datum: { color: this.backFontColor === null ? this.fillColor : this.backFontColor }, | |
'text-anchor': 'middle', | |
'font-family': this.fontFamily, | |
'font-size': `${this.lookupTableValueFontSize}px`, | |
'font-weight': this.fontWeight, | |
fill: function(d) { return d.color; }, | |
text: `0 ${this.lookupTableValueUnit}`, | |
transform: lookupTransform | |
}); | |
this.applyAttributes(this.lookupTableValueOverlayText, { | |
datum: { color: this.frontFontColor === null ? "#fff" : this.frontFontColor }, | |
'text-anchor': 'middle', | |
'font-family': this.fontFamily, | |
'font-size': `${this.lookupTableValueFontSize}px`, | |
'font-weight': this.fontWeight, | |
fill: function(d) { return d.color; }, | |
'fill-opacity': this.overlayTextFillOpacity, | |
text: `0 ${this.lookupTableValueUnit}`, | |
transform: lookupTransform | |
}); | |
} | |
if (this.changeRateValueEnabled) { | |
let yOffset = this.fontSize/4 + this.changeRateValueFontSize + this.changeRateValueYOffset; | |
if (this.lookupTableValueEnabled) { | |
yOffset += this.lookupTableValueFontSize + this.lookupTableValueYOffset; | |
} | |
let rateTransform = `translate(0, ${yOffset})`; | |
this.applyAttributes(this.changeRateValueBehindText, { | |
datum: { color: this.backFontColor === null ? this.fillColor : this.backFontColor }, | |
'text-anchor': 'middle', | |
'font-family': this.fontFamily, | |
'font-size': `${this.changeRateValueFontSize}px`, | |
'font-weight': this.fontWeight, | |
fill: function(d) { return d.color; }, | |
text: `0 ${this.changeRateValueUnit}`, | |
transform: rateTransform | |
}); | |
this.applyAttributes(this.changeRateValueOverlayText, { | |
datum: { color: this.frontFontColor === null ? "#fff" : this.frontFontColor }, | |
'text-anchor': 'middle', | |
'font-family': this.fontFamily, | |
'font-size': `${this.changeRateValueFontSize}px`, | |
'font-weight': this.fontWeight, | |
fill: function(d) { return d.color; }, | |
'fill-opacity': this.overlayTextFillOpacity, | |
text: `0 ${this.changeRateValueUnit}`, | |
transform: rateTransform | |
}); | |
} | |
} | |
applyWaveHorizontalAttributes() { | |
this.clipDef = `M0 0 Q${this.waveWidth/2} ${this.amplitude}, ${this.waveWidth} 0 T${2*this.waveWidth} 0`; | |
var minRequiredClipWidth = this.width*2 + 2*this.waveWidth + this.borderWidth/2; | |
this.clipWidth = 2*this.waveWidth; | |
while ( this.clipWidth < minRequiredClipWidth ) { | |
this.clipWidth += this.waveWidth; | |
this.clipDef += ` T${this.clipWidth} 0`; | |
this.clipWidth += this.waveWidth; | |
this.clipDef += ` T${this.clipWidth} 0`; | |
} | |
this.clipDefArray = [this.clipDef, `L${this.clipWidth}`, `${this.height}`, "L0", `${this.height}`, "Z"]; | |
this.clipDef = this.clipDefArray.join(" "); | |
this.applyAttributes(this.waveHorizontal, { | |
d: this.clipDef | |
}); | |
} | |
applySupportLabelAttributes() { | |
this.applyAttributes(this.supportLabelBg, { | |
datum: { color: this.fillColor }, | |
width: 50, | |
height: 50, | |
rx: this.innerCornerRadius, | |
ry: this.innerCornerRadius, | |
fill: '#fafafa', | |
'fill-opacity': this.supportLabelBackgroundOpacity, | |
stroke: function(d) { return d.color; }, | |
'stroke-width': this.supportLabelBackgroundBorderWidth | |
}); | |
this.applyAttributes(this.supportLabel, { | |
'text-anchor': 'middle', | |
'font-family': this.supportLabelFontFamily, | |
'font-size': `${this.supportLabelFontSize}px`, | |
fill: this.supportLabelFontColor, | |
'font-weight': this.supportLabelFontWeight, | |
text: `${this.supportLabelText}` | |
}); | |
this.applyAttributes(this.topSupportLabel, { | |
'text-anchor': 'middle', | |
'font-family': this.supportLabelFontFamily, | |
'font-size': `${this.topSupportLabelFontSize}px`, | |
fill: this.supportLabelFontColor, | |
'font-weight': this.supportLabelFontWeight, | |
text: `${this.topSupportLabelText}` | |
}); | |
} | |
tweenWaveHorizontal () { | |
let that = this; | |
let startHeight = (this.tankHeight - this.innerHeight)/2 - this.amplitude/2; | |
let transformStart = `translate(-${this.width + 2*this.waveWidth}, ${startHeight})`; | |
let transformEnd = `translate(-${this.width}, ${startHeight})`; | |
this.waveHorizontal.attr('transform', transformStart); | |
animate(); | |
function animate() { | |
that.waveHorizontal | |
.transition() | |
.duration(that.horizontalWaveDuration) | |
.ease(d3.easeLinear) | |
.attrTween("transform", function(d) { | |
return d3.interpolateString(transformStart, transformEnd); | |
}).on("end", function (d) { | |
animate(); | |
}); | |
} | |
} | |
animateFromZero () { | |
this.waveClip | |
.datum({ transform: `translate(0, ${this.gaugeScale(this.fillMinValue) + this.amplitude})`}) | |
.attr('transform', function(d) { return d.transform; }); | |
this.animateNewHeight(this.fillValue); | |
} | |
animateNewHeight (val) { | |
let that = this; | |
if ( typeof val !== "undefined" ) { | |
this.newHeight = this.gaugeScale(val); | |
this.fillValue = val; | |
} | |
this.tweenWaveVertical(); | |
this.tweenElements(); | |
} | |
tweenWaveVertical() { | |
let endTransform = `translate(0, ${this.newHeight})`; | |
return this.waveClip | |
.transition() | |
.delay(this.delay) | |
.duration(this.transitionDuration) | |
.ease(this.ease) | |
.attrTween("transform", function(d) { | |
let interpolator = d3.interpolateString(d.transform, endTransform); | |
return function(t) { | |
d.transform = interpolator(t); | |
return d.transform; | |
}; | |
}); | |
} | |
calculateColor () { | |
this.fillAnimationColor = this.fillColor === null ? this.defaultFillColor : this.fillColor; | |
this.backFontAnimationColor = this.backFontColor === null ? this.fillColor : this.backFontColor; | |
this.backArrowAnimationColor = this.backArrowColor === null ? this.fillColor : this.backArrowColor; | |
} | |
tweenElements () { | |
this.calculateColor(); | |
this.colorTransition(this.waterFill, "fill", this.fillAnimationColor); | |
this.colorTransition(this.supportLabelBg, 'stroke', this.fillAnimationColor); | |
this.tweenText(); | |
if ( this.arrow === true ) { | |
this.colorTransition(this.behindArrow, "fill", this.backArrowAnimationColor); | |
this.colorTransition(this.overlayArrow, "fill", this.frontArrowColor); | |
} | |
if (this.tankType === 'round') { | |
this.colorTransition(this.neckFill, 'fill', this.fillAnimationColor); | |
} | |
} | |
colorTransition(selection, attribute, targetColor) { | |
selection | |
.transition() | |
.delay(this.delay) | |
.duration(this.transitionDuration) | |
.ease(this.ease) | |
.attrTween(attribute, function(d) { | |
let interpolator = d3.interpolateRgb(d.color, targetColor); | |
return function(t) { | |
d.color = interpolator(t); | |
return d.color; | |
}; | |
}); | |
} | |
textFormatter(val) { | |
if (this.fillUnit) { | |
return `${(Number(Math.round(parseFloat(val) + 'e' + this.decimal) + 'e-' + this.decimal)).toFixed(this.decimal)} ${this.fillUnit}`; | |
} | |
return `${(Number(Math.round(parseFloat(val) + 'e' + this.decimal) + 'e-' + this.decimal)).toFixed(this.decimal)}`; | |
} | |
lookupTextFormatter(val) { | |
if (this.lookupTableValueUnit) { | |
return `${Number(Math.round(parseFloat(val) + 'e' + this.lookupTableValueDecimal) + 'e-' + this.lookupTableValueDecimal).toFixed(this.lookupTableValueDecimal)} ${this.lookupTableValueUnit}`; | |
} | |
return `${Number(Math.round(parseFloat(val) + 'e' + this.lookupTableValueDecimal) + 'e-' + this.lookupTableValueDecimal).toFixed(this.lookupTableValueDecimal)}`; | |
} | |
changeRateValueTextFormatter(val) { | |
if (this.changeRateValueUnit) { | |
return `${Number(Math.round(parseFloat(val) + 'e' + this.changeRateValueDecimal) + 'e-' + this.changeRateValueDecimal).toFixed(this.changeRateValueDecimal)} ${this.changeRateValueUnit}`; | |
} | |
return `${Number(Math.round(parseFloat(val) + 'e' + this.changeRateValueDecimal) + 'e-' + this.changeRateValueDecimal).toFixed(this.changeRateValueDecimal)}`; | |
} | |
tweenText() { | |
let that = this; | |
this.behindText | |
.transition() | |
.delay(this.delay) | |
.ease(this.ease) | |
.duration(this.transitionDuration) | |
.tween("text", function(d) { | |
let node = this; | |
let interpolate = d3.interpolate(that.textFormatter(node.textContent), that.textFormatter(that.fillValue)); | |
return function(t) { | |
node.textContent = that.textFormatter(interpolate(t)); | |
}; | |
}) | |
.attrTween("fill", function(d) { | |
let interpolator = d3.interpolateRgb(d.color, that.backFontAnimationColor); | |
return function(t) { | |
d.color = interpolator(t); | |
return d.color; | |
}; | |
}); | |
this.overlayText | |
.transition() | |
.delay(this.delay) | |
.ease(this.ease) | |
.duration(this.transitionDuration) | |
.tween("text", function(d) { | |
let node = this; | |
let interpolate = d3.interpolate(that.textFormatter(node.textContent), that.textFormatter(that.fillValue)); | |
return function(t) { | |
if (that.arrow === true) { | |
that.updateArrowPosition(); | |
} | |
node.textContent = that.textFormatter(interpolate(t)); | |
}; | |
}) | |
.attrTween("fill", function(d) { | |
let interpolator = d3.interpolateRgb(d.color, that.frontFontColor); | |
return function(t) { | |
d.color = interpolator(t); | |
return d.color; | |
}; | |
}) | |
.on('end', function() { | |
if (that.arrow === true) { | |
that.updateArrowPosition(); | |
} | |
}); | |
if (this.lookupTableValueEnabled) { | |
this.lookupTableValueBehindText | |
.transition() | |
.delay(this.delay) | |
.ease(this.ease) | |
.duration(this.transitionDuration) | |
.tween("text", function(d) { | |
let node = this; | |
let interpolate = d3.interpolate(that.lookupTextFormatter(node.textContent), that.lookupTextFormatter(that.lookupTableValue)); | |
return function(t) { | |
node.textContent = that.lookupTextFormatter(interpolate(t)); | |
}; | |
}) | |
.attrTween("fill", function(d) { | |
let interpolator = d3.interpolateRgb(d.color, that.backFontAnimationColor); | |
return function(t) { | |
d.color = interpolator(t); | |
return d.color; | |
}; | |
}); | |
this.lookupTableValueOverlayText | |
.transition() | |
.delay(this.delay) | |
.ease(this.ease) | |
.duration(this.transitionDuration) | |
.tween("text", function(d) { | |
let node = this; | |
let interpolate = d3.interpolate(that.lookupTextFormatter(node.textContent), that.lookupTextFormatter(that.lookupTableValue)); | |
return function(t) { | |
node.textContent = that.lookupTextFormatter(interpolate(t)); | |
}; | |
}) | |
.attrTween("fill", function(d) { | |
let interpolator = d3.interpolateRgb(d.color, that.frontFontColor); | |
return function(t) { | |
d.color = interpolator(t); | |
return d.color; | |
}; | |
}); | |
} | |
if (this.changeRateValueEnabled) { | |
this.changeRateValueBehindText | |
.transition() | |
.delay(this.delay) | |
.ease(this.ease) | |
.duration(this.transitionDuration) | |
.tween("text", function(d) { | |
let node = this; | |
let interpolate = d3.interpolate(that.changeRateValueTextFormatter(node.textContent), that.changeRateValueTextFormatter(that.changeRateValue)); | |
return function(t) { | |
node.textContent = that.changeRateValueTextFormatter(interpolate(t)); | |
}; | |
}) | |
.attrTween("fill", function(d) { | |
let interpolator = d3.interpolateRgb(d.color, that.backFontAnimationColor); | |
return function(t) { | |
d.color = interpolator(t); | |
return d.color; | |
}; | |
}); | |
this.changeRateValueOverlayText | |
.transition() | |
.delay(this.delay) | |
.ease(this.ease) | |
.duration(this.transitionDuration) | |
.tween("text", function(d) { | |
let node = this; | |
let interpolate = d3.interpolate(that.changeRateValueTextFormatter(node.textContent), that.changeRateValueTextFormatter(that.changeRateValue)); | |
return function(t) { | |
node.textContent = that.changeRateValueTextFormatter(interpolate(t)); | |
}; | |
}) | |
.attrTween("fill", function(d) { | |
let interpolator = d3.interpolateRgb(d.color, that.frontFontColor); | |
return function(t) { | |
d.color = interpolator(t); | |
return d.color; | |
}; | |
}); | |
} | |
} | |
updateArrowPosition () { | |
let {xOffset, yOffset} = this.calculateArrowPosition(); | |
this.behindArrow.attr('x', xOffset).attr('y', yOffset); | |
this.overlayArrow.attr('x', xOffset).attr('y', yOffset); | |
} | |
calculateArrowPosition () { | |
let xOffset, yOffset; | |
if (this.changeRateValueArrowEnabled && this.changeRateValueEnabled) { | |
xOffset = this.changeRateValueOverlayText.node().getBBox().width/2 + this.overlayArrow.node().getBBox().width/2 + this.arrowXOffset; | |
yOffset = this.fontSize/4 + this.changeRateValueFontSize + this.changeRateValueYOffset - 1; | |
if (this.lookupTableValueEnabled) { | |
yOffset += this.lookupTableValueFontSize + this.lookupTableValueYOffset; | |
} | |
} else { | |
xOffset = this.overlayText.node().getBBox().width/2 + this.overlayArrow.node().getBBox().width + this.arrowXOffset; | |
yOffset = this.overlayArrow.node().getBBox().height/4 + this.arrowYOffset; | |
} | |
return {xOffset: xOffset, yOffset: yOffset}; | |
} | |
repositionElements () { | |
this.repositionMarker(); | |
this.setSupportLabelText(this.supportLabelText); | |
} | |
// calculate the needed transformation values for positioning the markers | |
repositionMarker () { | |
let topMarkerLabelTrans = `translate(${this.innerWidth/2 + this.markerLabelXOffset - this.markerFontSize/4}, -${this.innerHeight/2 - this.markerFontSize + this.markerLabelYOffset})`; | |
let bottomMarkerLabelTrans = `translate(${this.innerWidth/2 + this.markerLabelXOffset - this.markerFontSize/4}, ${this.innerHeight/2 - this.markerFontSize/4 + this.markerLabelYOffset})`; | |
this.topMarkerLabel.attr('transform', topMarkerLabelTrans); | |
this.bottomMarkerLabel.attr('transform', bottomMarkerLabelTrans); | |
if (this.tankType === 'round') { | |
let markerBarGroupTrans = `translate(${this.innerWidth/2 + this.markerLength + this.markerBarXOffset}, 0)`; | |
this.markerBarGroup.attr('transform', markerBarGroupTrans); | |
} else if (this.tankType === 'tower') { | |
let markerBarGroupTrans = `translate(${this.innerWidth/2}, 0)`; | |
this.markerBarGroup.attr('transform', markerBarGroupTrans); | |
} | |
} | |
repositionSupportLabelGroup() { | |
this.repositionSupportLabelBg(); | |
this.repositionSupportLabel(); | |
this.supportLabelGroup | |
.attr('transform', `translate(0, ${this.height/2 - this.borderWidth - this.supportLabelBgHeight/2})`); | |
} | |
repositionSupportLabelBg () { | |
let paddingForAesthetic = 1.6*this.supportLabelPadding; | |
let {width, height} = this.getSupportLabelDimensions(); | |
let requiredWidth = width + 2*this.supportLabelPadding + paddingForAesthetic; | |
let requiredHeight = height + 2*this.supportLabelPadding; | |
this.supportLabelBgWidth = requiredWidth; | |
this.supportLabelBgHeight = requiredHeight; | |
this.supportLabelBg | |
.attr('width', this.supportLabelBgWidth) | |
.attr('height', this.supportLabelBgHeight) | |
.attr('transform', `translate(-${this.supportLabelBgWidth/2}, -${this.supportLabelBgHeight/2})`); | |
} | |
repositionSupportLabel () { | |
if ( this.dualSupportLabel === true ) { | |
this.topSupportLabelTrans = `translate(0, ${this.supportLabelYOffset + this.topSupportLabelYOffset})`; | |
this.supportLabelTrans = `translate(0, ${this.supportLabelFontSize + this.supportLabelYOffset})`; | |
} else if ( this.dualSupportLabel === false ) { | |
this.supportLabelTrans = `translate(0, ${this.supportLabelFontSize/2 + this.supportLabelYOffset})`; | |
} | |
this.topSupportLabel.attr('transform', this.topSupportLabelTrans); | |
this.supportLabel.attr('transform', this.supportLabelTrans); | |
} | |
applyAttributes(selection, datum = {}) { | |
let properties = Object.getOwnPropertyNames(datum); | |
properties.forEach((p) => { | |
if (p === 'datum') { | |
return selection.datum( datum[p] ); | |
} else if (p === 'text') { | |
return selection.text( datum[p] ); | |
} else if (p === 'style') { | |
return selection.style( datum[p] ); | |
} else { | |
return selection.attr( p, datum[p] ); | |
} | |
}); | |
} | |
getSupportLabelDimensions () { | |
let width, height; | |
if ( this.dualSupportLabel === true ) { | |
width = this.getMaxWidth(this.supportLabel, this.topSupportLabel); | |
height = this.supportLabelFontSize - this.supportLabelYOffset + this.topSupportLabelFontSize - this.topSupportLabelYOffset; | |
} else if ( this.dualSupportLabel === false ) { | |
width = this.supportLabel.node().getBBox().width; | |
height = this.supportLabelFontSize - this.supportLabelYOffset; | |
} | |
return {width: width, height: height}; | |
} | |
getHeight(selection) { | |
return selection.node().getBBox().height; | |
} | |
getMaxWidth (first, second) { | |
if (typeof second === 'object') { | |
return Math.max(first.node().getBBox().width, second.node().getBBox().width); | |
} | |
return first.node().getBBox().width; | |
} | |
getXCoordOfEllipse(y) { | |
return Math.sqrt( Math.pow(this.tankRx, 2)*(1 - ( Math.pow(y, 2)/Math.pow(this.tankRy, 2) )) ); | |
} | |
getYCoordOfEllipse(x) { | |
return Math.sqrt( Math.pow(this.tankRy, 2)*(1 - ( Math.pow(x, 2)/Math.pow(this.tankRx, 2) )) ); | |
} | |
slopeOfLineTangentToEllipse(x, y) { | |
return -x*Math.pow(this.tankRy,2)/(y*Math.pow(this.tankRx,2)); | |
} | |
setDecimal (val) { | |
this.decimal = val; | |
this.tweenText(); | |
} | |
updateHeight (val) { | |
this.animateNewHeight(val); | |
} | |
updateLookupTableValue (val) { | |
this.lookupTableValue = val; | |
} | |
updateChangeRateValue (val) { | |
this.changeRateValue = val; | |
} | |
setMarkerText (top, bottom) { | |
if ( this.marker === true ) { | |
this.topMarkerLabel.text(top); | |
this.bottomMarkerLabel.text(bottom); | |
} else { | |
console.log("markers are not enabled."); | |
} | |
this.repositionMarker(); | |
} | |
setSupportLabelText (...args) { | |
if ( this.enableSupportLabel === true ) { | |
if ( args.length === 1 ) { | |
this.dualSupportLabel = false; | |
this.topSupportLabel.attr('fill-opacity', 0); | |
this.supportLabel.text(args[0]); | |
} else if ( args.length === 2 ) { | |
this.dualSupportLabel = true; | |
this.topSupportLabel.attr('fill-opacity', 1); | |
this.supportLabel.text(args[1]); | |
this.topSupportLabel.text(args[0]); | |
} | |
} | |
//resize and reposition support label elements | |
this.repositionSupportLabelGroup(); | |
} | |
updateFillColor (options) { | |
if ( typeof options === "object" ) { | |
if ( "fillColor" in options ) { | |
this.fillColor = options.fillColor; | |
} | |
if ( "overlayTextFillOpacity" in options ) { | |
this.overlayTextFillOpacity = options.overlayTextFillOpacity; | |
} | |
if ( "topFillColor" in options ) { | |
this.topFillColor = options.topFillColor; | |
} | |
if ( "topFillBackFontColor" in options ) { | |
this.topFillBackFontColor = options.topFillBackFontColor; | |
} | |
if ( "bottomFillColor" in options ) { | |
this.bottomFillColor = options.bottomFillColor; | |
} | |
if ( "bottomFillBackFontColor" in options ) { | |
this.bottomFillBackFontColor = options.bottomFillBackFontColor; | |
} | |
if ( "backFontColor" in options ) { | |
this.backFontColor = options.backFontColor; | |
} | |
if ( "frontFontColor" in options ) { | |
this.frontFontColor = options.frontFontColor; | |
} | |
if ( "topFillBackArrowColor" in options ) { | |
this.topFillBackArrowColor = options.topFillBackArrowColor; | |
} | |
if ( "bottomFillBackArrowColor" in options ) { | |
this.bottomFillBackArrowColor = options.bottomFillBackArrowColor; | |
} | |
if ( "frontArrowColor" in options ) { | |
this.frontArrowColor = options.frontArrowColor; | |
} | |
if ( "backArrowColor" in options ) { | |
this.backArrowColor = options.backArrowColor; | |
} | |
this.tweenElements(); | |
} | |
} | |
updateColor (color) { | |
this.fillColor = color; | |
this.backFontColor = color; | |
this.backArrowColor = color; | |
this.tweenElements(); | |
} | |
updateArrow (options) { | |
if ( "destroy" in options ) { | |
if ( options.destroy === true ) { | |
this.removeArrow(); | |
} | |
} else if ( "enable" in options ) { | |
this.arrow = true; | |
this.addArrow(); | |
} | |
if ( typeof this.behindArrow !== "undefined" && typeof this.overlayArrow !== "undefined" ) { | |
if ( "topFillBackArrowColor" in options ) { | |
this.topFillBackArrowColor = options.topFillBackArrowColor; | |
} | |
if ( "bottomFillBackArrowColor" in options ) { | |
this.bottomFillBackArrowColor = options.bottomFillBackArrowColor; | |
} | |
if ( "frontArrowColor" in options ) { | |
this.frontArrowColor = options.frontArrowColor; | |
} | |
if ( "backArrowColor" in options ) { | |
this.backArrowColor = options.backArrowColor; | |
} | |
if ( "direction" in options ) { | |
let direction = options.direction; | |
if ( direction === "up" ) { | |
this.arrowName = this.upArrowName; | |
} else if ( direction === "down" ) { | |
this.arrowName = this.downArrowName; | |
} else if ( direction === "none" ) { | |
this.arrowName = this.noArrowName; | |
} | |
this.behindArrow.text(this.arrowName); | |
this.overlayArrow.text(this.arrowName); | |
} | |
if ( "changeRateValueArrowEnabled" in options ) { | |
this.changeRateValueArrowEnabled = options.changeRateValueArrowEnabled; | |
} | |
this.tweenElements(); | |
this.updateArrowPosition(); | |
} | |
} | |
addArrow() { | |
this.behindArrow.attr('fill-opacity', 1); | |
this.overlayArrow.attr('fill-opacity', 1); | |
} | |
removeArrow () { | |
this.behindArrow.attr('fill-opacity', 0); | |
this.overlayArrow.attr('fill-opacity', 0); | |
this.arrow = false; | |
} | |
redraw() { | |
this.destroy(); | |
this.init(); | |
} | |
destroy() { | |
this.tankWidth = null; | |
this.tankHeight = null; | |
this.viewPortWidth = null; | |
this.viewPortHeight = null; | |
this.topMarkerText = null; | |
this.bottomMarkerText = null; | |
this.svgContainer.remove(); | |
} | |
click(callback) { | |
if ( typeof callback !== "function" ) { | |
throw new Error("argument must be a function"); | |
} | |
this.svgContainer.on("click", callback); | |
} | |
setBisector() { | |
this.bisector = d3.bisector(function(d) { return d.yCoord; }).left; | |
} | |
transitionMarker(marker, targetWidth, targetX1) { | |
marker | |
.transition() | |
.duration(200) | |
.ease(d3.easeLinear) | |
.attrTween('stroke-width', function(d) { | |
let interpolator = d3.interpolateNumber(d.strokeWidth, targetWidth); | |
return function(t) { | |
d.strokeWidth = interpolator(t); | |
return d.strokeWidth; | |
}; | |
}) | |
.attrTween('x1', function(d) { | |
let interpolator = d3.interpolateNumber(d.x1, targetX1); | |
return function(t) { | |
d.x1 = interpolator(t); | |
return d.x1; | |
}; | |
}); | |
} | |
hover() { | |
d3.select(this.svgContainer.node().parentNode).on('mouseleave', () => { | |
this.thresholdMarkers.forEach( (marker, i) => { | |
this.transitionMarker(marker, this.markerWidth, -this.markerLength); | |
this.thresholdTooltips[i].style('display', 'none'); | |
}); | |
}); | |
d3.select(this.svgContainer.node().parentNode).on('mousemove', () => { | |
let yCoord = d3.mouse(this.markerBarGroup.node())[1]; | |
let locationIndex = this.bisector(this.thresholdMarkerPositions, yCoord); | |
if (locationIndex >= 0) { | |
this.thresholdMarkers.forEach( (marker, i) => { | |
if ( i === locationIndex) { | |
this.transitionMarker(marker, this.markerWidth+3, -(this.markerLength+3)); | |
this.thresholdTooltips[i].style('display', 'initial'); | |
} else { | |
this.transitionMarker(marker, this.markerWidth, -this.markerLength); | |
this.thresholdTooltips[i].style('display', 'none'); | |
} | |
}); | |
} else { | |
this.transitionMarker(this.thresholdMarkers[0], this.markerWidth+3, -(this.markerLength+3)); | |
this.thresholdTooltips[0].style('display', 'initial'); | |
} | |
}); | |
} | |
// utility functions | |
uniqId() { | |
// Convert it to base 36 (numbers + letters), and grab the first 9 characters | |
// after the decimal. | |
return "clipPath" + Math.random().toString(36).substr(2, 9); | |
} | |
getUniqUrl(id) { | |
return `url(${this.url}#${id})`; | |
} | |
insertFirstBeforeSecond(container, first, second) { | |
return container.insert( | |
function() { return first.node(); }, | |
function() { return second.node(); } | |
); | |
} | |
insertFirstAfterSecond(container, first, second) { | |
container.insert( | |
function() { return first.node(); }, | |
function() { return second.node(); } | |
); | |
return container.insert( | |
function() { return second.node(); }, | |
function() { return first.node(); } | |
); | |
} | |
appendSecondElementToFirst(first, ...args) { | |
args.forEach((arg) => first.append( () => arg.node() ) ); // for each second argument, return a function: first.append( function(arg) { arg.node() }); | |
} | |
} // end of class | |
})(jQuery); | |
let thresholds = [ | |
{ | |
name: 'Alarm High', | |
value: 90, | |
type: 'High', | |
alarm: true | |
}, | |
{ | |
name: 'Pump On', | |
value: 55, | |
type: 'High', | |
alarm: false | |
}, | |
{ | |
name: 'Pump On', | |
value: 40, | |
type: 'Low', | |
alarm: false | |
}, | |
{ | |
name: 'Alarm Low', | |
value: 10, | |
type: 'Low', | |
alarm: true | |
} | |
]; | |
let options = { | |
tankType: 'tower', | |
fillValue: 55, | |
fillUnit: "ft", | |
supportLabelPadding: 5, | |
frontFontColor: "#003B42", | |
thresholds: thresholds, | |
lookupTableValue: 1700, | |
lookupTableValueUnit: 'gal', | |
lookupTableValueDecimal: 1, | |
changeRateValueDecimal: 3, | |
changeRateValueArrowEnabled: true, | |
changeRateValue: 0.3, | |
changeRateValueUnit: 'gal/min' | |
} | |
let tank = $('.wrapper').analogTank(options); | |
$(".wrapper").resizable({ | |
stop: function( event, ui ) { | |
tank.redraw(); | |
} | |
}); | |
let that = this; | |
let randomColor = ["#C62828", "#BA68C8", "#1976D2", "#FFB74D", "#607D8B"]; | |
function getRandomColor() { | |
return randomColor[Math.floor(Math.random() * 5)]; | |
} | |
function getNow() { | |
return Date().slice(16, 24); | |
} | |
function getRandom() { | |
return Math.floor(Math.random() * 100); | |
} | |
tank.click(function () { | |
var randomVal = getRandom(); | |
tank.updateHeight(randomVal); | |
}); | |
function setOneText() { | |
tank.setSupportLabelText(getNow()); | |
} | |
function setTwoText() { | |
tank.setSupportLabelText(getNow(), "bottom label"); | |
} | |
function fillColor() { | |
tank.updateColor(getRandomColor()); | |
} | |
function backFontColor() { | |
tank.updateFillColor({ backFontColor: getRandomColor() }); | |
} | |
function frontFontColor() { | |
tank.updateFillColor({ frontFontColor: getRandomColor() }); | |
} | |
function topFillBackFontColor() { | |
tank.updateFillColor({ topFillBackFontColor: getRandomColor() }); | |
} | |
function bottomFillBackFontColor() { | |
tank.updateFillColor({ bottomFillBackFontColor: getRandomColor() }); | |
} | |
function topFillColor() { | |
tank.updateFillColor({ topFillColor: getRandomColor() }); | |
} | |
function bottomFillColor() { | |
tank.updateFillColor({ bottomFillColor: getRandomColor() }); | |
} | |
function shrink() { | |
document.getElementsByClassName('wrapper')[0].setAttribute("style", "width:200px;height:200px;"); | |
tank.redraw(); | |
} | |
function enlarge() { | |
document.getElementsByClassName('wrapper')[0].setAttribute("style", "width:400px;height:400px;"); | |
tank.redraw(); | |
} | |
function high() { | |
tank.updateHeight(90); | |
} | |
function mid() { | |
tank.updateHeight(55); | |
} | |
function low() { | |
tank.updateHeight(0); | |
} | |
function addArrow() { | |
tank.updateArrow({ enable: true }); | |
} | |
function removeArrow() { | |
tank.updateArrow({ destroy: true }); | |
} | |
function upArrow() { | |
tank.updateArrow({ direction: "up" }); | |
} | |
function downArrow() { | |
tank.updateArrow({ direction: "down" }); | |
} | |
function defaultArrow() { | |
tank.updateArrow({ direction: "none" }); | |
} | |
function topFillArrowColor() { | |
tank.updateArrow({ topFillBackArrowColor: getRandomColor() }); | |
} | |
function bottomFillBackArrowColor() { | |
tank.updateArrow({ bottomFillBackArrowColor: getRandomColor() }); | |
} | |
function frontArrowColor() { | |
tank.updateArrow({ frontArrowColor: getRandomColor() }); | |
} | |
function backArrowColor() { | |
tank.updateArrow({ backArrowColor: getRandomColor() }); | |
} | |
function setDecimalZero() { | |
tank.setDecimal(0); | |
} | |
function setDecimalOne() { | |
tank.setDecimal(1); | |
} | |
function setDecimalTwo() { | |
tank.setDecimal(2); | |
} | |
function tower() { | |
tank.tankType = 'tower'; | |
delete tank.topMarkerFontColor; | |
delete tank.bottomMarkerFontColor; | |
tank.redraw(); | |
} | |
function round() { | |
tank.tankType = 'round'; | |
tank.topMarkerFontColor = "#fafafa"; | |
tank.bottomMarkerFontColor = "#fafafa"; | |
tank.redraw(); | |
} | |
function destroy() { | |
tank.destroy(); | |
} | |
function updateLookupTableValue() { | |
tank.updateLookupTableValue(parseInt(Math.random()*1000)) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.3/d3.js"></script> | |
<script src="https://code.jquery.com/jquery-3.1.0.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.js"></script> | |
<script src="https://code.getmdl.io/1.2.1/material.min.js"></script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
background-color: #133440 | |
} | |
.wrapper { | |
background-color: #2d4e5a; | |
height: 200px; | |
width: 200px; | |
position: relative; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<link href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.css" rel="stylesheet" /> | |
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet" /> | |
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.css.map" rel="stylesheet" /> | |
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/fonts/fontawesome-webfont.svg" rel="stylesheet" /> | |
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/fonts/fontawesome-webfont.eot" rel="stylesheet" /> | |
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/fonts/fontawesome-webfont.ttf" rel="stylesheet" /> | |
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" /> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment