liquid.meter.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. /**
  2. * @package Liquid Meter
  3. * @author Okler Themes
  4. * @license Licensed under CC BY-NC-ND
  5. *
  6. * Copyright (c) 2014
  7. * Inspired on dribble shot:
  8. * http://dribbble.com/shots/1069484-Charging-Animation
  9. */
  10. (function($) {
  11. 'use strict';
  12. var defaultOptions = {
  13. shape: 'circle',
  14. color: '#82BF41',
  15. background: '#4C4C52',
  16. stroke: '#23282F',
  17. textColor: '#FFFFFF',
  18. fontFamily: 'Open Sans',
  19. fontSize: '24px',
  20. fontWeight: '600',
  21. liquidOpacity: 0.9,
  22. liquidPalette: ['#82BF41'],
  23. speed: 3000,
  24. animate: true
  25. };
  26. var LiquidMeter = function(el, options) {
  27. if (typeof Snap == 'undefined') {
  28. throw 'Snap.js not found.';
  29. }
  30. this.meter = $(el);
  31. this.options = options;
  32. this.options.text = this.meter.text();
  33. this.init();
  34. };
  35. LiquidMeter.prototype = {
  36. init: function(el, options) {
  37. this.wrapper = this.meter.parent('.liquid-meter');
  38. if ( !this.wrapper.get(0) ) {
  39. var wrapper = $('<div>').attr({ 'class': 'liquid-meter' });
  40. this.wrapper = this.meter.wrap( wrapper ).parent();
  41. }
  42. var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
  43. svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
  44. svg.setAttribute('viewBox', '0 0 220 220');
  45. this.wrapper.prepend(svg);
  46. this.svg = $(svg).attr({
  47. width: '100%',
  48. height: '100%'
  49. });
  50. this.frames = [
  51. [6, -6, -10, 6, 13, -10], // 1
  52. [6, -5, -13, 4, 31, -5], // 2
  53. [7, -5, -9, 0, 31, -5], // 3
  54. [7, -5, -9, -6, 30, -6], // 4
  55. [6, -10, -10, -10, 33, -4], // 5
  56. [8, -4, -8, -13, 38, 7], // 6
  57. [8, -8, -5, -15, 55, 11], // 7
  58. [7, -19, -2, -16, 33, 16], // 8
  59. [3, -23, -2, -16, 42, 21], // 9
  60. [3, -22, 7, -18, 27, 21], // 10
  61. [2, -26, 15, -18, 25, 20] , // 11
  62. [-3, -23, 18, -18, 26, 19] , // 12
  63. [-6, -40, 18, -18, 22, 22] , // 13
  64. [-15, -16, 23, -13, 37, 22], // 14
  65. [-15, -16, 23, -13, 37, 22], // 15
  66. [-17, -16, 23, -10, 33, 22], // 16
  67. [-18, -16, 23, -10, 35, 21], // 17
  68. [-20, -12, 23, -5, 34, 20], // 18
  69. [-22, -13, 20, -1, 35, 22], // 19
  70. [-22, -13, 20, 5, 32, 21], // 20
  71. [-21, -16, 19, 8, 34, 15], // 21
  72. [-23, -16, 16, 10, 36, 9], // 22
  73. [-22, -23, 13, 10, 48, 0], // 23
  74. [-23, -20, 13, 8, 45, -6], // 24
  75. [-20, -22, 7, 10, 44, -6], // 25
  76. [-20, -22, 5, 10, 42, -7], // 26
  77. [-18, -20, 0, 10, 41, -8], // 27
  78. [-15, -22, -7, 10, 41, -8], // 28
  79. [-11, -33, -8, 10, 41, -8], // 29
  80. [-7, -36, -6, 10, 15, -11], // 30
  81. [0, -39, -8, 10, 15, -11], // 31
  82. [0, -36, -8, 7, 15, -11], // 32
  83. ];
  84. this.paper = Snap(svg);
  85. this.draw();
  86. this.observe();
  87. if ( this.options.animate ) {
  88. this.animate();
  89. } else {
  90. this.drawFrame(1);
  91. }
  92. this.wrapper.addClass( 'liquid-meter-loaded' );
  93. return this;
  94. },
  95. draw: function() {
  96. this.value = parseInt(this.meter.val() || this.meter.attr('value'), 10);
  97. var paper = this.paper;
  98. this.recipient = paper.circle(110, 110, 95);
  99. var background = this.makeGradient(this.options.background);
  100. this.recipient.attr({
  101. fill: background,
  102. stroke: this.options.stroke,
  103. strokeWidth: 15
  104. });
  105. var mask = paper.circle(110, 110, 87);
  106. mask.attr({
  107. fill: '#FFFFFF'
  108. });
  109. this.liquid = paper.path();
  110. this.liquid.attr({
  111. id: 'front',
  112. fill: this.options.color,
  113. mask: mask,
  114. stroke: lighten(this.options.color, 20),
  115. strokeWidth: 1
  116. });
  117. this.label = paper.text('50%', '50%', [this.value, '%']);
  118. this.label.attr({
  119. fill: this.options.textColor,
  120. dy: '.4em',
  121. 'font-family': this.options.fontFamily,
  122. 'font-size': this.options.fontSize,
  123. 'font-weight': this.options.fontWeight,
  124. 'text-anchor': 'middle',
  125. stroke: this.options.textColor
  126. });
  127. this.label.selectAll('tspan:nth-child(2)').attr({
  128. 'font-size': '24',
  129. stroke: 'none'
  130. });
  131. return this;
  132. },
  133. observe: function() {
  134. var _self = this;
  135. this.meter.on('change', function() {
  136. _self.set( $(this).val() );
  137. });
  138. },
  139. set: function( val ) {
  140. var value = parseInt( val, 10 );
  141. this.meter.val( value );
  142. this.value = value;
  143. this.label.node.textContent = value + '%';
  144. },
  145. liquidPalette: function( val ) {
  146. this.options.liquidPalette = val;
  147. },
  148. background: function( val ) {
  149. this.options.background = val;
  150. },
  151. stroke: function( val ) {
  152. this.options.stroke = val;
  153. },
  154. color: function( val ) {
  155. this.options.color = val;
  156. this.liquid.attr({
  157. fill: this.options.color,
  158. stroke: lighten(this.options.color, 20)
  159. });
  160. },
  161. makeGradient: function(color) {
  162. return this.paper.gradient(Snap.format('L(0, 0, 100, 100){color.top}-{color.bottom}', {
  163. color: {
  164. top: lighten(color, 6),
  165. bottom: color
  166. }
  167. }));
  168. },
  169. makePath: function(value, t1, x1, y1, t2, x2, y2) {
  170. var top = ((100 - value) * 1.76) + 22;
  171. return Snap.format('M0,{left} C{curve.x1},{curve.y1} {curve.x2},{curve.y2} 220,{right} L220,220 L0,220 z', {
  172. left: top - (t1 || 0),
  173. right: top - (t2 || 0),
  174. curve: {
  175. x1: x1 + top,
  176. y1: y1 + top,
  177. x2: x2 + top,
  178. y2: y2 + top
  179. }
  180. });
  181. },
  182. drawFrame: function(number) {
  183. var frame = this.frames[number - 1],
  184. t1 = frame[0],
  185. x1 = frame[1],
  186. y1 = frame[2],
  187. t2 = frame[3],
  188. x2 = frame[4],
  189. y2 = frame[5];
  190. this.liquid.attr({ d: this.makePath( this.value, t1, x1, y1, t2, x2, y2 )});
  191. },
  192. animate: function() {
  193. var requestAnimationFrame = window.requestAnimationFrame ||
  194. window.webkitRequestAnimationFrame ||
  195. window.mozRequestAnimationFrame ||
  196. window.oRequestAnimationFrame ||
  197. window.msRequestAnimationFrame ||
  198. function (callback) {
  199. setTimeout(callback, 0);
  200. },
  201. frames = this.frames.length,
  202. interval = this.options.speed / frames,
  203. currentFrame = 1,
  204. drawFrame = this.drawFrame.bind(this);
  205. function loop() {
  206. requestAnimationFrame(function() {
  207. draw();
  208. });
  209. setTimeout(loop, interval);
  210. }
  211. function draw() {
  212. drawFrame(currentFrame);
  213. currentFrame++;
  214. if (currentFrame >= frames) {
  215. currentFrame = 1;
  216. }
  217. }
  218. loop();
  219. return this;
  220. }
  221. };
  222. /**
  223. * liquidMeter jQuery plugin - public method
  224. * @param - {Object} options - Passed options extending defaultOptions
  225. * @return - {Selector} - Array of elements
  226. */
  227. $.fn.liquidMeter = function(options, value) {
  228. var isSetter = (typeof options === 'string' && !!value);
  229. if ( !isSetter ) {
  230. options = $.extend({}, defaultOptions, options);
  231. }
  232. return this.map(function () {
  233. // preventing against multiple instantiations
  234. var instance = $(this).data('liquid-meter');
  235. if (!instance) {
  236. instance = new LiquidMeter(this, options);
  237. $(this).data('liquid-meter', instance);
  238. }
  239. if ( isSetter ) {
  240. instance[ options ]( value );
  241. }
  242. return instance;
  243. });
  244. };
  245. /**
  246. * Util function for lightening the colour with a %
  247. * @param - string - colour with leading #
  248. * @param - number - percentage to lighten by
  249. */
  250. function lighten(color, percent) {
  251. var n = parseInt(color.slice(1), 16),
  252. a = Math.round(2.55 * percent || 0),
  253. // Bitshift 16 bits to the left
  254. r = (n >> 16) + a,
  255. // Bitshift 8 bits to the left based on blue
  256. b = (n >> 8 & 0x00FF) + a,
  257. //
  258. g = (n & 0x0000FF) + a;
  259. // Calculate
  260. return '#' + (
  261. 0x1000000 + (r < 255 ? r < 1 ? 0 : r : 255) * 0x10000 +
  262. (b < 255 ? b < 1 ? 0 : b : 255) * 0x100 + (g < 255 ? g < 1 ? 0 : g : 255)
  263. ).toString(16).slice(1).toUpperCase();
  264. };
  265. }(jQuery));