/** * @package Liquid Meter * @author Okler Themes * @license Licensed under CC BY-NC-ND * * Copyright (c) 2014 * Inspired on dribble shot: * http://dribbble.com/shots/1069484-Charging-Animation */ (function($) { 'use strict'; var defaultOptions = { min: 0, max: 100, shape: 'circle', color: '#82BF41', background: '#4C4C52', stroke: '#23282F', textColor: '#FFFFFF', fontFamily: 'Open Sans', fontSize: '24px', fontWeight: '600', liquidOpacity: 0.9, liquidPalette: ['#82BF41'], speed: 3000, animate: true }; var LiquidMeter = function(el, options) { if (typeof Snap == 'undefined') { throw 'Snap.js not found.'; } this.meter = $(el); this.options = options; this.options.text = this.meter.text(); this.init(); }; LiquidMeter.prototype = { init: function(el, options) { this.wrapper = this.meter.parent('.liquid-meter'); if ( !this.wrapper.get(0) ) { var wrapper = $('
').attr({ 'class': 'liquid-meter' }); this.wrapper = this.meter.wrap( wrapper ).parent(); } var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svg.setAttribute('preserveAspectRatio', 'xMidYMid meet'); svg.setAttribute('viewBox', '0 0 220 220'); this.wrapper.prepend(svg); this.svg = $(svg).attr({ width: '100%', height: '100%' }); this.frames = [ [6, -6, -10, 6, 13, -10], // 1 [6, -5, -13, 4, 31, -5], // 2 [7, -5, -9, 0, 31, -5], // 3 [7, -5, -9, -6, 30, -6], // 4 [6, -10, -10, -10, 33, -4], // 5 [8, -4, -8, -13, 38, 7], // 6 [8, -8, -5, -15, 55, 11], // 7 [7, -19, -2, -16, 33, 16], // 8 [3, -23, -2, -16, 42, 21], // 9 [3, -22, 7, -18, 27, 21], // 10 [2, -26, 15, -18, 25, 20] , // 11 [-3, -23, 18, -18, 26, 19] , // 12 [-6, -40, 18, -18, 22, 22] , // 13 [-15, -16, 23, -13, 37, 22], // 14 [-15, -16, 23, -13, 37, 22], // 15 [-17, -16, 23, -10, 33, 22], // 16 [-18, -16, 23, -10, 35, 21], // 17 [-20, -12, 23, -5, 34, 20], // 18 [-22, -13, 20, -1, 35, 22], // 19 [-22, -13, 20, 5, 32, 21], // 20 [-21, -16, 19, 8, 34, 15], // 21 [-23, -16, 16, 10, 36, 9], // 22 [-22, -23, 13, 10, 48, 0], // 23 [-23, -20, 13, 8, 45, -6], // 24 [-20, -22, 7, 10, 44, -6], // 25 [-20, -22, 5, 10, 42, -7], // 26 [-18, -20, 0, 10, 41, -8], // 27 [-15, -22, -7, 10, 41, -8], // 28 [-11, -33, -8, 10, 41, -8], // 29 [-7, -36, -6, 10, 15, -11], // 30 [0, -39, -8, 10, 15, -11], // 31 [0, -36, -8, 7, 15, -11], // 32 ]; this.paper = Snap(svg); this.draw(); this.observe(); if ( this.options.animate ) { this.animate(); } else { this.drawFrame(1); } this.wrapper.addClass( 'liquid-meter-loaded' ); return this; }, draw: function() { this.min = parseInt(this.meter.attr('min'), 10); this.max = parseInt(this.meter.attr('max'), 10); this.value = parseInt(this.meter.val() || this.meter.attr('value'), 10); var paper = this.paper; this.recipient = paper.circle(110, 110, 95); var background = this.makeGradient(this.options.background); this.recipient.attr({ fill: background, stroke: this.options.stroke, strokeWidth: 15 }); var mask = paper.circle(110, 110, 87); mask.attr({ fill: '#FFFFFF' }); this.liquid = paper.path(); this.liquid.attr({ id: 'front', fill: this.options.color, mask: mask, stroke: lighten(this.options.color, 20), strokeWidth: 1 }); this.label = paper.text('50%', '50%', [this.value, '%']); this.label.attr({ fill: this.options.textColor, dy: '.4em', 'font-family': this.options.fontFamily, 'font-size': this.options.fontSize, 'font-weight': this.options.fontWeight, 'text-anchor': 'middle', stroke: this.options.textColor }); this.label.selectAll('tspan:nth-child(2)').attr({ 'font-size': '24', stroke: 'none' }); return this; }, observe: function() { var _self = this; this.meter.on('change', function() { _self.set( $(this).val() ); }); }, set: function( val ) { var value = parseInt( val, 10 ); this.meter.val( value ); this.value = value; this.label.node.textContent = value + '%'; }, liquidPalette: function( val ) { this.options.liquidPalette = val; }, background: function( val ) { this.options.background = val; }, stroke: function( val ) { this.options.stroke = val; }, color: function( val ) { this.options.color = val; this.liquid.attr({ fill: this.options.color, stroke: lighten(this.options.color, 20) }); }, makeGradient: function(color) { return this.paper.gradient(Snap.format('L(0, 0, 100, 100){color.top}-{color.bottom}', { color: { top: lighten(color, 6), bottom: color } })); }, makePath: function(value, t1, x1, y1, t2, x2, y2) { var top = ((100 - ((value*100)/this.max)) * 1.76) + 22; return Snap.format('M0,{left} C{curve.x1},{curve.y1} {curve.x2},{curve.y2} 220,{right} L220,220 L0,220 z', { left: top - (t1 || 0), right: top - (t2 || 0), curve: { x1: x1 + top, y1: y1 + top, x2: x2 + top, y2: y2 + top } }); }, drawFrame: function(number) { var frame = this.frames[number - 1], t1 = frame[0], x1 = frame[1], y1 = frame[2], t2 = frame[3], x2 = frame[4], y2 = frame[5]; this.liquid.attr({ d: this.makePath( this.value, t1, x1, y1, t2, x2, y2 )}); }, animate: function() { var requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) { setTimeout(callback, 0); }, frames = this.frames.length, interval = this.options.speed / frames, currentFrame = 1, drawFrame = this.drawFrame.bind(this); function loop() { requestAnimationFrame(function() { draw(); }); setTimeout(loop, interval); } function draw() { drawFrame(currentFrame); currentFrame++; if (currentFrame >= frames) { currentFrame = 1; } } loop(); return this; } }; /** * liquidMeter jQuery plugin - public method * @param - {Object} options - Passed options extending defaultOptions * @return - {Selector} - Array of elements */ $.fn.liquidMeter = function(options, value) { var isSetter = (typeof options === 'string' && !!value); if ( !isSetter ) { options = $.extend({}, defaultOptions, options); } return this.map(function () { // preventing against multiple instantiations var instance = $(this).data('liquid-meter'); if (!instance) { instance = new LiquidMeter(this, options); $(this).data('liquid-meter', instance); } if ( isSetter ) { instance[ options ]( value ); } return instance; }); }; /** * Util function for lightening the colour with a % * @param - string - colour with leading # * @param - number - percentage to lighten by */ function lighten(color, percent) { var n = parseInt(color.slice(1), 16), a = Math.round(2.55 * percent || 0), // Bitshift 16 bits to the left r = (n >> 16) + a, // Bitshift 8 bits to the left based on blue b = (n >> 8 & 0x00FF) + a, // g = (n & 0x0000FF) + a; // Calculate return '#' + ( 0x1000000 + (r < 255 ? r < 1 ? 0 : r : 255) * 0x10000 + (b < 255 ? b < 1 ? 0 : b : 255) * 0x100 + (g < 255 ? g < 1 ? 0 : g : 255) ).toString(16).slice(1).toUpperCase(); }; }(jQuery));