Created
July 2, 2015 11:00
-
-
Save tomsseisums/ad7ba49684549b6674d6 to your computer and use it in GitHub Desktop.
snap.svg, GSAP progress meters for javascript, preset for bar and donut (circle)
This file contains hidden or 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 donut = new ProgressMeter.Donut({ | |
| fat : 20, | |
| }, { | |
| progress : Math.random() * 100, | |
| keyframes : function(timeline, style) | |
| { | |
| timeline.fromTo(style.meter, 0.5, { colorProps : { fill : 'lime' } }, { colorProps : { fill : 'orange' } }, 0); | |
| timeline.to(style.meter, 0.5, { colorProps : { fill : 'red' } }, 0.5); | |
| }, | |
| complete : function() | |
| { | |
| this.animate(Math.random() * 100); | |
| }, | |
| duration : 5000 | |
| }, document.getElementById('cpu')); | |
| donut.animate(Math.random() * 100); |
This file contains hidden or 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
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <link rel="stylesheet" href="style.css"> | |
| </head> | |
| <body> | |
| <figure id="cpu" class="progressmeter donut"></figure> | |
| <script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/3.9.3/lodash.min.js"></script> | |
| <script src="//cdnjs.cloudflare.com/ajax/libs/gsap/latest/TweenMax.min.js"></script> | |
| <script src="//cdnjs.cloudflare.com/ajax/libs/gsap/latest/plugins/ColorPropsPlugin.min.js"></script> | |
| <!--<script src="//cdnjs.cloudflare.com/ajax/libs/snap.svg/0.3.0/snap.svg-min.js"></script>--> | |
| <script src="snapsvg.min.js"></script> | |
| <script src="plugin.js"></script> | |
| <script src="app.js"></script> | |
| </body> | |
| </html> |
This file contains hidden or 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
| (function(global, lodash, snap, timeline, easing) | |
| { | |
| console.info("Loading ProgressMeter v0.0.1-alpha"); | |
| // Generators. | |
| var generators = | |
| { | |
| bar : function(progress, fat) | |
| { | |
| progress = Math.max(0, Math.min(progress, 100)); | |
| return [ | |
| // Top left corner. | |
| ['M', 0, 0], | |
| // Top right corner. | |
| ['L', progress, 0], | |
| // Bottom right corner. | |
| ['L', progress, fat], | |
| // Bottom left corner. | |
| ['L', 0, fat], | |
| // Up to top left. | |
| ['L', 0, 0], | |
| // Close path. | |
| ['z'] | |
| ]; | |
| }, | |
| donut : function(progress, fat, offset) | |
| { | |
| progress = Math.max(0, Math.min(progress, 100)); | |
| offset = offset || 0; | |
| var center = 50; | |
| var innerRadius = center - fat - offset; | |
| var outerRadius = center - offset; | |
| var alpha = 360 / 100 * progress; | |
| var a = (90 - alpha) * Math.PI / -180; | |
| var innerX = center + innerRadius * Math.cos(a); | |
| var innerY = center + innerRadius * Math.sin(a); | |
| var outerX = center + outerRadius * Math.cos(a); | |
| var outerY = center + outerRadius * Math.sin(a); | |
| var path; | |
| if (progress === 100) | |
| { | |
| path = [ | |
| // move to outer ring start | |
| [ | |
| "M", | |
| center, | |
| center - outerRadius | |
| ], | |
| // arc around the clock | |
| [ | |
| "A", | |
| outerRadius, | |
| outerRadius, | |
| 0, | |
| +(alpha > 180), | |
| 1, | |
| outerX - .1, | |
| outerY | |
| ], | |
| // connect | |
| [ | |
| "z" | |
| ], | |
| // move to inner circle start | |
| [ | |
| "M", | |
| innerX, | |
| innerY | |
| ], | |
| // arc around the clock | |
| [ | |
| "A", | |
| innerRadius, | |
| innerRadius, | |
| 0, | |
| +(alpha > 180), | |
| 0, | |
| innerX + .1, | |
| innerY | |
| ], | |
| // and connect | |
| [ | |
| "z" | |
| ] | |
| ]; | |
| } | |
| else | |
| { | |
| path = [ | |
| // move to start point of inner ring | |
| [ | |
| "M", | |
| center, | |
| center - innerRadius | |
| ], | |
| // draw a line to outer ring | |
| [ | |
| "L", | |
| center, | |
| center - outerRadius | |
| ], | |
| // arc to outer ring end | |
| [ | |
| "A", | |
| outerRadius, | |
| outerRadius, | |
| 0, | |
| +(alpha > 180), | |
| 1, | |
| outerX, | |
| outerY | |
| ], | |
| // move to inner ring end | |
| [ | |
| "L", | |
| innerX, | |
| innerY | |
| ], | |
| // arc to inner ring start | |
| [ | |
| "A", | |
| innerRadius, | |
| innerRadius, | |
| 0, | |
| +(alpha > 180), | |
| 0, | |
| center, | |
| center - innerRadius | |
| ], | |
| [ | |
| "z" | |
| ] | |
| ]; | |
| } | |
| return path; | |
| } | |
| }; | |
| var inherit = function(target, source) | |
| { | |
| target.prototype = new source(); | |
| target.prototype.constructor = target; | |
| return target.prototype; | |
| }; | |
| var implement = function(target, implementation) | |
| { | |
| return lodash.assign(target, implementation); | |
| }; | |
| var Shape = function(style, options, appendTo) | |
| { | |
| // Throw a better error if progress meter is not initialized with | |
| // the new keyword. | |
| if (!(this instanceof Shape)) | |
| { | |
| throw new Error("Constructor was called without new keyword."); | |
| } | |
| // Prevent calling constructor wihtout parameters so inheritance | |
| // works correctly. | |
| if (arguments.length === 0) return; | |
| // Optional argument switching. | |
| if (options instanceof Node) | |
| { | |
| appendTo = options; | |
| options = {}; | |
| } | |
| // Set up options. | |
| this.options = lodash.merge({ | |
| style : | |
| { | |
| fat : 1, | |
| trail : | |
| { | |
| fill : 'gray' | |
| }, | |
| meter : | |
| { | |
| fill : 'yellow' | |
| } | |
| }, | |
| duration : 1000, | |
| easing : easing.map.linear, | |
| progress : 0, | |
| keyframes : function(timeline, style) {}, | |
| environment : function() {}, | |
| update : function() {}, | |
| complete : function() {} | |
| }, this.specialOptions, options, { style : style }); | |
| // Save defaults. | |
| this.defaults = lodash.assign({}, this.options); | |
| // Initialize container. | |
| this.makeContainer(); | |
| this.personalizeContainer(); | |
| // Initialize snap.svg. | |
| this.snap = snap(this.container); | |
| // Environment callback. | |
| this.options.environment.call(this); | |
| // Create meter elements. | |
| this.makeMeter(); | |
| // Create timeline. | |
| this.createTimeline(); | |
| // Set the initial value. | |
| this.set(this.defaults.progress); | |
| // Append the element, if needed. | |
| if (appendTo && appendTo instanceof HTMLElement) appendTo.appendChild(this.container); | |
| // Add the class. | |
| this.container.classList.add('progressmeter'); | |
| }; | |
| implement(Shape.prototype, | |
| { | |
| specialOptions : {}, | |
| generator : function() { return ''; }, | |
| generatorSpecialArguments : function() { return []; }, | |
| makeContainer : function() | |
| { | |
| this.container = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); | |
| }, | |
| personalizeContainer : function() | |
| { | |
| var viewBox = [0, 0, 100, 100].join(' '); | |
| this.container.setAttribute('viewBox', viewBox); | |
| }, | |
| generate : function(progress) | |
| { | |
| progress = progress || this.options.progress; | |
| var specArgs = this.generatorSpecialArguments(); | |
| var generatorArguments = [progress, this.options.style.fat].concat(specArgs); | |
| return this.generator.apply(null, generatorArguments); | |
| }, | |
| makeMeter : function() | |
| { | |
| // Create tail object if needed. | |
| this.trail = null; | |
| if (this.options.trail !== null) | |
| { | |
| this.trail = this.snap.path(this.generate(100)); | |
| this.trail.attr(this.options.style.trail); | |
| } | |
| // Create actual meter. | |
| this.meter = this.snap.path(this.generate(0)); | |
| this.meter.attr(this.options.style.meter); | |
| }, | |
| createTimeline : function() | |
| { | |
| var self = this; | |
| this.timeline = new timeline({ | |
| paused : true, | |
| onUpdate : this.update.bind(this), | |
| }); | |
| // Call the callback, to initialize frames. | |
| this.options.keyframes.call(this, this.timeline, this.options.style); | |
| // Add progress change to timeline. | |
| this.timeline.fromTo(this.options, 1, { progress : 0 }, { progress: 100, ease : this.options.easing }, 0); | |
| this.timeline.totalDuration(this.options.duration / 1000); | |
| }, | |
| set : function(progress) | |
| { | |
| // console.log('set', progress); | |
| var percentage = progress / 100; | |
| this.timeline.progress(percentage); | |
| }, | |
| animate : function(progress, options) | |
| { | |
| var percentage = progress / 100; | |
| options = options || {}; | |
| // Get total duration. | |
| var currentTimeScale = this.timeline.timeScale(); | |
| var totalDuration = this.timeline.totalDuration(); | |
| var currentScaledTotalDuration = totalDuration / currentTimeScale; | |
| // Calculate target duration. | |
| var targetDuration = totalDuration * percentage; | |
| var duration = options.duration || null; | |
| if (duration !== null) | |
| { | |
| // Remove from options. | |
| delete options.duration; | |
| // Secondize. | |
| duration /= 1000; | |
| // If duration is specified, we need to change the timescale | |
| // so that the progress change animation lives the given amount. | |
| var currentPercentage = this.options.progress / 100; | |
| var percentageDifference = Math.max(currentPercentage, percentage) - Math.min(currentPercentage, percentage); | |
| var newTotalDuration = duration / percentageDifference; | |
| this.timeline.totalDuration(newTotalDuration); | |
| this.timeline.tweenTo(targetDuration, lodash.assign({ | |
| onComplete : function() | |
| { | |
| // Reset duration back to initial. | |
| this.timeline.totalDuration(currentScaledTotalDuration); | |
| this.options.complete.bind(this)(); | |
| }.bind(this) | |
| }, options)); | |
| } | |
| else | |
| { | |
| this.timeline.tweenTo(targetDuration, lodash.assign({ | |
| onComplete : this.options.complete.bind(this) | |
| }, options)); | |
| } | |
| }, | |
| redraw : function() | |
| { | |
| // Path is added first, so it can be overwritten if there is need for it. | |
| // Redraw tail, if any. | |
| if (this.trail) | |
| { | |
| var trailOptions = lodash.merge({ | |
| path : this.generate(100) | |
| }, this.options.style.trail); | |
| this.trail.attr(trailOptions); | |
| } | |
| // Redraw meter. | |
| var meterOptions = lodash.merge({ | |
| path : this.generate() | |
| }, this.options.style.meter); | |
| this.meter.attr(meterOptions); | |
| }, | |
| update : function() | |
| { | |
| // Redraw. | |
| this.redraw(); | |
| // Call callback. | |
| this.options.update.call(this); | |
| } | |
| }); | |
| // Bar. | |
| var Bar = function(options, appendTo) | |
| { | |
| // Call super constructor. | |
| Shape.apply(this, arguments); | |
| this.container.classList.addClass('bar'); | |
| }; | |
| implement(inherit(Bar, Shape), | |
| { | |
| generator : generators.bar, | |
| personalizeContainer : function() | |
| { | |
| var viewBox = [0, 0, 100, this.options.style.fat].join(' '); | |
| this.container.setAttribute('viewBox', viewBox); | |
| this.container.setAttribute('preserveAspectRatio', 'none'); | |
| } | |
| }); | |
| // Donut. | |
| var Donut = function(options, appendTo) | |
| { | |
| // Call super constructor. | |
| Shape.apply(this, arguments); | |
| this.container.classList.add('donut'); | |
| }; | |
| implement(inherit(Donut, Shape), | |
| { | |
| specialOptions : { | |
| offset : 0 | |
| }, | |
| generator : generators.donut, | |
| generatorSpecialArguments : function() | |
| { | |
| return [this.options.style.offset]; | |
| } | |
| }); | |
| lodash.assign(global, | |
| { | |
| ProgressMeter : | |
| { | |
| Bar : Bar, | |
| Donut : Donut | |
| } | |
| }); | |
| })(window, _, Snap, TimelineMax, Ease); |
This file contains hidden or 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
| figure.progressmeter.donut | |
| { | |
| position: relative; | |
| } | |
| figure.progressmeter.donut svg | |
| { | |
| display: block; | |
| margin: 0; | |
| } | |
| figure.progressmeter.donut::before, figure.progressmeter.donut::after | |
| { | |
| content: ""; | |
| position: absolute; | |
| width: 100%; | |
| height: 100%; | |
| left: 0; | |
| top: 0; | |
| border-radius: 100%; | |
| /*border: solid 5px black;*/ | |
| box-shadow: 0px 0px 5px black; | |
| box-sizing: border-box; | |
| } | |
| figure.progressmeter.donut::after | |
| { | |
| width: 60%; | |
| height: 60%; | |
| left: 20%; | |
| top: 20%; | |
| box-shadow: inset 0px 0px 5px black; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment