|
<!doctype html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="utf-8"> |
|
<meta http-equiv="x-ua-compatible" content="ie=edge"> |
|
<title>Material Design Gauge</title> |
|
<meta name="description" |
|
content="Simple Material Design gauge control implemented in pure CSS/HTML"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1"> |
|
<style> |
|
|
|
body { |
|
color: #444; |
|
font-family: Varela, Helvetica, Arial, sans-serif; |
|
font-size: 24px; |
|
font-weight: normal; |
|
height: 32px; |
|
letter-spacing: normal; |
|
line-height: 32px; |
|
} |
|
|
|
p { |
|
text-align: center; |
|
} |
|
|
|
/* |
|
* #### Gauge Component |
|
* |
|
* The standard markup for the component is: |
|
* |
|
* <div class="gauge"> |
|
* <div class="gauge--container"> |
|
* <div class="gauge--marker"></div> |
|
* <div class="gauge--background"></div> |
|
* <div class="gauge--center"></div> |
|
* <div class="gauge--data"></div> |
|
* <div class="gauge--needle"></div> |
|
* </div> |
|
* <div class="gauge--labels"> |
|
* <span class="gauge--label__low">No</span> |
|
* <span class="gauge--label__spacer"></span> |
|
* <span class="gauge--label__high">Yes</span> |
|
* </div> |
|
* </div> |
|
*/ |
|
|
|
/* |
|
* First define all of the relevant rules that aren't dependent |
|
* on the size of the gauge. We want to collect the size-depenent |
|
* rules in one place to make it easier to adjust the size. |
|
*/ |
|
|
|
.gauge { |
|
position: relative; |
|
} |
|
|
|
.gauge--container { |
|
margin: 0; |
|
padding: 0; |
|
position: absolute; |
|
left: 50%; |
|
overflow: hidden; |
|
text-align: center; |
|
-webkit-transform: translateX(-50%); |
|
-moz-transform: translateX(-50%); |
|
-ms-transform: translateX(-50%); |
|
-o-transform: translateX(-50%); |
|
transform: translateX(-50%); |
|
} |
|
|
|
.gauge--background { |
|
z-index: 0; |
|
position: absolute; |
|
background-color: #C0D3D3; |
|
top: 0; |
|
border-radius: 300px 300px 0 0; |
|
} |
|
|
|
.gauge--data { |
|
z-index: 1; |
|
position: absolute; |
|
background-color: #007979; |
|
margin-left: auto; |
|
margin-right: auto; |
|
border-radius: 300px 300px 0 0; |
|
-webkit-transform-origin: center bottom; |
|
-moz-transform-origin: center bottom; |
|
-ms-transform-origin: center bottom; |
|
-o-transform-origin: center bottom; |
|
transform-origin: center bottom; |
|
} |
|
|
|
.gauge--center { |
|
z-index: 2; |
|
position: absolute; |
|
background-color: #fff; |
|
margin-right: auto; |
|
border-radius: 300px 300px 0 0; |
|
} |
|
|
|
.gauge--marker { |
|
z-index: 3; |
|
background-color: #fff; |
|
position: absolute; |
|
width: 1px; |
|
} |
|
|
|
.gauge--needle { |
|
z-index: 4; |
|
background-color: #F77C15; |
|
height: 3px; |
|
position: absolute; |
|
-webkit-transform-origin: left center; |
|
-moz-transform-origin: left center; |
|
-ms-transform-origin: left center; |
|
-o-transform-origin: left center; |
|
transform-origin: left center; |
|
} |
|
|
|
.gauge--labels { |
|
display: table; |
|
margin: 0 auto; |
|
position: relative; |
|
} |
|
|
|
.gauge--label__low { |
|
display: table-cell; |
|
text-align: center; |
|
} |
|
|
|
.gauge--label__spacer { |
|
display: table-cell; |
|
} |
|
|
|
.gauge--label__high { |
|
display: table-cell; |
|
text-align: center; |
|
} |
|
|
|
/* |
|
* Now define the rules that depend on the size of |
|
* the gauge. We start with sizing for a small mobile |
|
* device. |
|
*/ |
|
|
|
.gauge { height: calc(120px + 3em); } |
|
.gauge--container { width: 240px; height: 120px; } |
|
.gauge--marker { height: 120px; left: 119.5px; } |
|
.gauge--background { width: 240px; height: 120px; } |
|
.gauge--center { width: 144px; height: 72px; top: 48px; margin-left: 48px; } |
|
.gauge--data { width: 240px; height: 120px; } |
|
.gauge--needle { left: 120px; top: 117px; width: 120px; } |
|
.gauge--labels { top: 120px; width: 240px; } |
|
.gauge--label__low { width: 48px; } |
|
.gauge--label__spacer { width: 144px; } |
|
.gauge--label__high { width: 48px; } |
|
|
|
/* |
|
* Increase the gauge size slightly on larger viewports. |
|
*/ |
|
|
|
@media only screen and (min-width: 400px) { |
|
.gauge { height: calc(150px + 3em); } |
|
.gauge--container { width: 300px; height: 150px; } |
|
.gauge--marker { height: 150px; left: 149.5px; } |
|
.gauge--background { width: 300px; height: 150px; } |
|
.gauge--center { width: 180px; height: 90px; top: 60px; margin-left: 60px; } |
|
.gauge--data { width: 300px; height: 150px; } |
|
.gauge--needle { left: 150px; top: 147px; width: 150px; } |
|
.gauge--labels { top: 150px; width: 300px; } |
|
.gauge--label__low { width: 60px; } |
|
.gauge--label__spacer { width: 180px; } |
|
.gauge--label__high { width: 60px; } |
|
} |
|
|
|
/* |
|
* As an option, the `gauge__liveupdate` class can be added |
|
* to the main gauge element. When this class is present, |
|
* we add a transition that animates any changes to the gauge |
|
* value. Currently, the app does not use this option because |
|
* all the inputs that can change gauge values are present |
|
* on tab panels that are different from the gauge itself. |
|
* Therefore, users won't be able to see any gauge changes |
|
* when they make input changes. The code is available, though, |
|
* should this change. |
|
*/ |
|
|
|
.gauge__liveupdate .gauge--data, |
|
.gauge__liveupdate .gauge--needle { |
|
-webkit-transition: all 1s ease-in-out; |
|
-moz-transition: all 1s ease-in-out; |
|
-ms-transition: all 1s ease-in-out; |
|
-o-transition: all 1s ease-in-out; |
|
transition: all 1s ease-in-out; |
|
} |
|
|
|
/* |
|
* For a given gauge value, x, ranging from 0.0 to 1.0, set |
|
* the `transform: rotate()` property according to the |
|
* following equation: `-0.5 + 0.5x turns` The default |
|
* properties below represent an x value of 0. |
|
*/ |
|
|
|
.gauge--data { |
|
-webkit-transform: rotate(-.50turn); |
|
-moz-transform: rotate(-.50turn); |
|
-ms-transform: rotate(-.50turn); |
|
-o-transform: rotate(-.50turn); |
|
transform: rotate(-.50turn); |
|
} |
|
.gauge--needle { |
|
-webkit-transform: rotate(-.50turn); |
|
-moz-transform: rotate(-.50turn); |
|
-ms-transform: rotate(-.50turn); |
|
-o-transform: rotate(-.50turn); |
|
transform: rotate(-.50turn); |
|
} |
|
|
|
</style> |
|
</head> |
|
<body> |
|
<p>Hover over me</p> |
|
<div class="gauge gauge__liveupdate" id="gauge"> |
|
<div class="gauge--container" id="gauge-container"> |
|
<div class="gauge--marker"></div> |
|
<div class="gauge--background"></div> |
|
<div class="gauge--center"></div> |
|
<div class="gauge--data"></div> |
|
<div class="gauge--needle"></div> |
|
</div> |
|
<div class="gauge--labels mdl-typography--headline"> |
|
<span class="gauge--label__low">No</span> |
|
<span class="gauge--label__spacer"></span> |
|
<span class="gauge--label__high">Yes</span> |
|
</div> |
|
</div> |
|
<script> |
|
|
|
// #### Gauge |
|
|
|
// The Gauge object encapsulates the behavior |
|
// of simple gauge. Most of the implementation |
|
// is in the CSS rules, but we do have a bit |
|
// of JavaScript to set or read the gauge value |
|
|
|
function Gauge(el) { |
|
|
|
// ##### Private Properties and Attributes |
|
|
|
var element, // Containing element for the info component |
|
data, // `.gauge--data` element |
|
needle, // `.gauge--needle` element |
|
value = 0.0, // Current gauge value from 0 to 1 |
|
prop; // Style for transform |
|
|
|
// ##### Private Methods and Functions |
|
|
|
var setElement = function(el) { |
|
// Keep a reference to the various elements and sub-elements |
|
element = el; |
|
data = element.querySelector(".gauge--data"); |
|
needle = element.querySelector(".gauge--needle"); |
|
}; |
|
|
|
var setValue = function(x) { |
|
value = x; |
|
var turns = -0.5 + (x * 0.5); |
|
data.style[prop] = "rotate(" + turns + "turn)"; |
|
needle.style[prop] = "rotate(" + turns + "turn)"; |
|
}; |
|
|
|
// ##### Object to be Returned |
|
|
|
function exports() { }; |
|
|
|
// ##### Public API Methods |
|
|
|
exports.element = function(el) { |
|
if (!arguments.length) { return element; } |
|
setElement(el); |
|
return this; |
|
}; |
|
|
|
exports.value = function(x) { |
|
if (!arguments.length) { return value; } |
|
setValue(x); |
|
return this; |
|
}; |
|
|
|
// ##### Initialization |
|
|
|
var body = document.getElementsByTagName("body")[0]; |
|
["webkitTransform", "mozTransform", "msTransform", "oTransform", "transform"]. |
|
forEach(function(p) { |
|
if (typeof body.style[p] !== "undefined") { prop = p; } |
|
} |
|
); |
|
|
|
if (arguments.length) { |
|
setElement(el); |
|
} |
|
|
|
return exports; |
|
|
|
}; |
|
|
|
var gauge = new Gauge(document.getElementById("gauge")); |
|
|
|
gauge.value(0.25); |
|
document.getElementById("gauge-container").addEventListener("mouseover", function() { |
|
gauge.value(0.75); |
|
}); |
|
document.getElementById("gauge-container").addEventListener("mouseout", function() { |
|
gauge.value(0.25); |
|
}); |
|
|
|
</script> |
|
</body> |
|
</html> |