├── .github
└── workflows
│ └── stale.yml
├── .gitignore
├── Gruntfile.js
├── LICENSE
├── README.md
├── bower.json
├── dist
└── angular-raphael-gauge.min.js
├── example
└── index.html
├── package.json
└── src
└── angular-raphael-gauge.js
/.github/workflows/stale.yml:
--------------------------------------------------------------------------------
1 | name: Close inactive issues
2 | on:
3 | schedule:
4 | - cron: "30 1 * * *"
5 |
6 | jobs:
7 | close-issues:
8 | runs-on: ubuntu-latest
9 | permissions:
10 | issues: write
11 | pull-requests: write
12 | steps:
13 | - uses: actions/stale@v5
14 | with:
15 | days-before-issue-stale: 30
16 | days-before-issue-close: 14
17 | stale-issue-label: "stale"
18 | stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
19 | close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
20 | days-before-pr-stale: 60
21 | days-before-pr-close: 14
22 | repo-token: ${{ secrets.GITHUB_TOKEN }}
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | bower_components
3 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function(grunt) {
2 |
3 | // Project configuration.
4 | grunt.initConfig({
5 | pkg: grunt.file.readJSON('package.json'),
6 | uglify: {
7 | options: {
8 | mangle: false,
9 | sourceMap: false,
10 | compress: {
11 | drop_console: true
12 | }
13 | },
14 | js: {
15 | files: {
16 | 'dist/angular-raphael-gauge.min.js': 'src/angular-raphael-gauge.js'
17 | }
18 | }
19 | }
20 | });
21 |
22 | // Load the plugin that provides the "uglify" task.
23 | grunt.loadNpmTasks('grunt-contrib-uglify');
24 |
25 | grunt.registerTask('default', ['uglify']);
26 | };
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Piotr Wasilewski
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | jQuery Raphael Gauge
2 | =============
3 | [](https://codeclimate.com/github/wasilak/angular-raphael-gauge)
4 |
5 | You can see demo [here](http://wasilak.github.io/angular-raphael-gauge/).
6 |
7 | Why bother?
8 | -------------------
9 | I needed this kind of Gauge "chart" for one of my projects and it was great opportunity to get some experience in both AngularJS and Raphael.js :)
10 |
11 | INSTALLATION
12 | -------------------
13 |
14 | via bower:
15 |
16 | ```
17 | bower install angular-raphael-gauge
18 | ```
19 |
20 | or simply download latest source code from repository: [link](https://github.com/wasilak/angular-raphael-gauge/archive/master.zip)
21 |
22 | USAGE
23 | -------------------
24 |
25 | Include ```angular-raphael-gauge``` in your HTML file. Remember about including [Raphael.js](http://raphaeljs.com/) and [jQuery]().
26 |
27 | ```html
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | ```
40 |
41 | Next plugin-in ```angular-raphael-gauge``` into your application:
42 |
43 | ```javascript
44 | var angularDemo = angular.module('angularDemo', ['angular-raphael-gauge']);
45 | ```
46 |
47 | Now all you have to do is put directive code into HTML file:
48 | ```javascript
49 |
50 | ```
51 |
52 | and pass some options via config object in ```$scope```:
53 | ```javascript
54 | angularDemo.controller('DemoCtrl', ['$scope', function ($scope) {
55 | $scope.gauge = {
56 | name: 'Some data',
57 | opacity: 0.55,
58 | value: 65,
59 | text: 'some cool data'
60 | };
61 | }]);
62 | ```
63 |
64 | Updating ```$scope.value``` will update gauge value and render animation.
65 |
66 | That's it! :) See [demo](http://wasilak.github.io/angular-raphael-gauge/) for working example.
67 |
68 | OPTIONS
69 | -------------------
70 |
71 | Here are options available to be set during runtime:
72 |
73 | ```javascript
74 | var options = {
75 | name: false, // text under gauge
76 | value: 25, // value
77 | image: false, // path to image (should be square) - it will be under gauge
78 | text: false, // text in the middle of gauge
79 | textColor: '#000000', // text color
80 | arcColor: '#57E0EA', // animated arc color
81 | bgArcColor: '#000', // round background under arc
82 | opacity: false, // arc opacity
83 | duration: 1600, // animation duration
84 | easing: 'bounce' // Raphael easing effect. Don't use backIn or Elastic, they mess up animation :/
85 | };
86 | ```
87 |
88 | Building / Minifing
89 | ----------
90 |
91 | You can build minified version yourself, by simply using [Grunt](http://gruntjs.com) in project root.
92 |
93 | ```bash
94 | grunt
95 | ```
96 |
97 | Contributing
98 | --------------
99 |
100 | 1. Fork it
101 | 2. Create your feature branch (`git checkout -b my-new-feature`)
102 | 3. Commit your changes (`git commit -am 'Add some feature'`)
103 | 4. Push to the branch (`git push origin my-new-feature`)
104 | 5. Create new Pull Request
105 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-raphael-gauge",
3 | "description": "Nice and simple gauge Raphael.js for AngularJS.",
4 | "main": "dist/angular-raphael-gauge.js",
5 | "keywords": ["angularjs", "raphael", "chart", "gauge"],
6 | "homepage": "https://github.com/wasilak/angular-raphael-gauge",
7 | "repository": {
8 | "type": "git",
9 | "url": "git@github.com:wasilak/angular-raphael-gauge.git"
10 | },
11 | "license": "MIT",
12 | "authors": [
13 | "Piotr Wasilewski", "Piotr Wasilewski "
14 | ],
15 | "ignore": [
16 | ".gitignore",
17 | ".jshintrc",
18 | "Gruntfile.js",
19 | "package.json",
20 | "bower_components"
21 | ],
22 | "dependencies": {
23 | "angular": "~1.2.x",
24 | "raphael": "~2.x",
25 | "jquery": "~1.x"
26 | },
27 | "devDependencies": {}
28 | }
29 |
--------------------------------------------------------------------------------
/dist/angular-raphael-gauge.min.js:
--------------------------------------------------------------------------------
1 | !function(Raphael){"use strict";angular.module("angular-raphael-gauge",[]).directive("raphaelGauge",function(){return{restrict:"EA",scope:{config:"="},template:"",replace:!0,controller:function($scope,$element){var options={element:$element[0].id,name:!1,value:25,image:!1,icon:!1,text:!1,textColor:"#000000",arcColor:"#57E0EA",bgArcColor:"#000",opacity:!1,duration:1600,easing:"bounce"};options=$.extend(options,$scope.config),$("#"+options.element).html("");var radius=$("#"+options.element).width(),paper=new Raphael(options.element,radius,radius);$("#"+options.element+" svg").css({height:"100%",width:"100%"}),paper.setViewBox(0,0,radius,radius,!0),paper.canvas.setAttribute("preserveAspectRatio","none"),paper.customAttributes.arc=function(xloc,yloc,value,total,R){var path,alpha=360/total*value,a=(90-alpha)*Math.PI/180,x=xloc+R*Math.cos(a),y=yloc-R*Math.sin(a);return path=total===value?[["M",xloc,yloc-R],["A",R,R,0,1,1,xloc-.01,yloc-R]]:[["M",xloc,yloc-R],["A",R,R,0,+(alpha>180),1,x,y]],{path:path}};var counter=function(el,n,max){!function loop(){el.html(n+"%"),n++'),$("#"+element).css("font-size",$("#"+element).width()/10+"px"),$("#"+element+"Percentage").css("font-size",$("#"+element).width()/4+"px"),$(window).resize(function(){$("#"+element).css("font-size",$("#"+element).width()/10+"px"),$("#"+element+"Percentage").css("font-size",$("#"+element).width()/4+"px")}),counter($("#"+element+"Percentage"),0,options.value)};if(options.image){paper.image(options.image,0,0,radius,radius)}if(options.text){paper.text(radius/2,radius/2,options.text).attr({"font-size":radius/16,stroke:options.textColor,fill:options.textColor})}var newArc=!1,newArcBg=!1;$scope.$watch("config.value",function(){if(options.value=$scope.config.value,newArc&&newArc.remove(),newArcBg&&newArcBg.remove(),newArcBg=paper.path().attr({"stroke-opacity":options.opacity?options.opacity:"1",stroke:options.bgArcColor,"stroke-width":.1*radius,arc:[radius/2,radius/2,100,100,.425*radius]}),newArc=paper.path().attr({"stroke-opacity":options.opacity?options.opacity:"1",stroke:options.arcColor,"stroke-width":.15*radius,arc:[radius/2,radius/2,0,100,.425*radius]}),newArc.hover(function(){options.opacity&&newArc.animate({"stroke-opacity":"1"},200)},function(){options.opacity&&newArc.animate({"stroke-opacity":options.opacity},200)}),newArc.rotate(0,100,100).animate({arc:[radius/2,radius/2,options.value,100,.425*radius]},options.duration,options.easing),options.name){var textName=options.element+"Text";$("#"+options.element).append(''),gaugeText(textName,options.name,options.value)}})}}})}(Raphael);
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Angular Raphael Gauge Demo
5 |
6 |
7 |
8 | for this demo to work, you have to install dependencies with bower.
9 |
10 |
11 |
Gauge Demo
12 |
13 | Change value:
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-raphael-gauge",
3 | "version": "0.0.1",
4 | "devDependencies": {},
5 | "dependencies": {
6 | "grunt": "^0.4.5",
7 | "grunt-contrib-uglify": "^0.5.1"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/angular-raphael-gauge.js:
--------------------------------------------------------------------------------
1 | // using Raphael.js (be sure to include it earlier)
2 | ;(function(Raphael) {
3 | 'use strict';
4 |
5 | // module definition, this has to be included in your app
6 | angular.module('angular-raphael-gauge', [])
7 | // directive definition, if you want to use itm you have to include it in controller
8 | .directive('raphaelGauge', function() {
9 | return {
10 | // this directive can be used as an Element or an Attribute
11 | restrict: 'EA',
12 | scope: {
13 | // setting config attribute to isolated scope
14 | // config object is 1:1 configuration C3.js object, for avaiable options see: http://c3js.org/examples.html
15 | config: '='
16 | },
17 | template: '',
18 | replace: true,
19 | controller: function($scope, $element) {
20 |
21 | var options = {
22 | element: $element[0].id,
23 | name: false,
24 | value: 25,
25 | image: false,
26 | icon: false,
27 | text: false,
28 | textColor: '#000000',
29 | arcColor: '#57E0EA',
30 | bgArcColor: '#000',
31 | opacity: false,
32 | duration: 1600,
33 | easing: 'bounce' // Raphael easing effect. Don't use backIn or Elastic, they mess up animation :/
34 | };
35 |
36 | // merging default options with user options
37 | options = $.extend(options, $scope.config);
38 |
39 | $('#' + options.element).html('');
40 |
41 | // radius is caluculated from element's width
42 | var radius = $('#' + options.element).width();
43 |
44 | // new Raphael canvas
45 | var paper = new Raphael(options.element, radius, radius);
46 |
47 | // Make the SVG canvas fill its container - both initially and after resizing
48 | $('#' + options.element + ' svg').css({ height: '100%', width: '100%'});
49 |
50 | // setting canvas scaling on element resize
51 | paper.setViewBox(0, 0, radius, radius, true );
52 | paper.canvas.setAttribute('preserveAspectRatio', 'none');
53 |
54 | // custom arc attribute for easy arc drawing :)
55 | paper.customAttributes.arc = function (xloc, yloc, value, total, R) {
56 | var alpha = 360 / total * value,
57 | a = (90 - alpha) * Math.PI / 180,
58 | x = xloc + R * Math.cos(a),
59 | y = yloc - R * Math.sin(a),
60 | path;
61 | if (total === value) {
62 | path = [
63 | ["M", xloc, yloc - R],
64 | ["A", R, R, 0, 1, 1, xloc - 0.01, yloc - R]
65 | ];
66 | } else {
67 | path = [
68 | ["M", xloc, yloc - R],
69 | ["A", R, R, 0, +(alpha > 180), 1, x, y]
70 | ];
71 | }
72 | return {
73 | path: path
74 | };
75 | };
76 |
77 | // counter function for timely showing percentage counter upto required value
78 | // el - jQuery element
79 | // n - start value
80 | // max - end value
81 | var counter = function (el, n, max) {
82 | (function loop() {
83 | el.html(n + '%');
84 | if (n++ < max) {
85 | setTimeout(loop, options.duration / max);
86 | }
87 | })();
88 | };
89 |
90 | // function showing text beneath gauge and handling it's resizing
91 | var gaugeText = function(element, name, value)
92 | {
93 | // main text
94 | $('#' + element).html(name);
95 |
96 | // percentage
97 | $('#' + element).prepend('');
98 |
99 | $('#' + element).css('font-size', $('#' + element).width() / 10 + 'px');
100 | $('#' + element + 'Percentage').css('font-size', $('#' + element).width() / 4 + 'px');
101 |
102 | $(window).resize(function() {
103 | $('#' + element).css('font-size', $('#' + element).width() / 10 + 'px');
104 | $('#' + element + 'Percentage').css('font-size', $('#' + element).width() / 4 + 'px');
105 | });
106 |
107 | counter($('#' + element + 'Percentage'), 0, options.value);
108 | };
109 |
110 | // new image - gauge's background (if it is set)
111 | if (options.image) {
112 | var image = paper.image(options.image, 0, 0, radius, radius);
113 | }
114 |
115 | // adding text in the middle (if it is set)
116 | if (options.text) {
117 | var text = paper.text(radius / 2, radius / 2, options.text)
118 | .attr({
119 | 'font-size': radius / 16,
120 | "stroke": options.textColor,
121 | "fill": options.textColor
122 | });
123 | }
124 |
125 | var newArc = false,newArcBg = false;
126 |
127 | $scope.$watch('config.value', function() {
128 |
129 | options.value = $scope.config.value;
130 |
131 | if (newArc) newArc.remove();
132 | if (newArcBg) newArcBg.remove();
133 |
134 | // background arc
135 | newArcBg = paper.path().attr({
136 | "stroke-opacity": (options.opacity) ? options.opacity : "1",
137 | "stroke": options.bgArcColor,
138 | "stroke-width": radius * 0.1,
139 | arc: [radius / 2, radius / 2, 100, 100, radius * 0.425]
140 | });
141 |
142 | // new arc
143 | newArc = paper.path().attr({
144 | "stroke-opacity": (options.opacity) ? options.opacity : "1",
145 | "stroke": options.arcColor,
146 | "stroke-width": radius * 0.15,
147 | arc: [radius / 2, radius / 2, 0, 100, radius * 0.425]
148 | });
149 |
150 | // hover effect (if it is enabled in options)
151 | newArc.hover(
152 | function(event) {
153 | if (options.opacity) {
154 | newArc.animate({ "stroke-opacity": "1" }, 200);
155 | }
156 | },
157 | function(event) {
158 | if (options.opacity) {
159 | newArc.animate({ "stroke-opacity": options.opacity }, 200);
160 | }
161 | }
162 | );
163 |
164 | // rotating new arc
165 | newArc.rotate(0, 100 ,100).animate({
166 | arc: [radius / 2, radius / 2, options.value, 100, radius * 0.425]
167 | }, options.duration, options.easing);
168 |
169 | // adding text under gauge
170 | if (options.name) {
171 | var textName = options.element + 'Text';
172 | $('#' + options.element).append('');
173 | gaugeText(textName, options.name, options.value);
174 | }
175 |
176 | });
177 |
178 | }
179 | };
180 | });
181 | }(Raphael));
182 |
--------------------------------------------------------------------------------