├── 1.gif
├── index.html
├── README.md
├── ringGradient.js
└── src
└── ringGradient.js
/1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xswei/ringGradient/HEAD/1.gif
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Ring Gradient
6 |
7 |
8 |
13 |
14 |
15 |
17 |
47 |
48 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ringGradient
2 | SVG环形渐变
3 |
4 |
5 |
6 | 效果图如下
7 |
8 | 
9 |
10 |
11 |
12 | [在线演示](https://bl.ocks.org/xswei/e1933d50b10e808fbb7b7b32c84adcd9)
13 |
14 | ### 使用方法
15 |
16 | #### 引入脚本
17 |
18 | ```js
19 |
20 |
21 |
22 |
23 | ```
24 |
25 | 这个脚本依赖d3v4,因此在引入之前首先要引入`d3`库,准确来说本插件依赖`d3-selection`、`d3-scale`和`d3-format`.因此如果不想引入整个`d3`的话,可以单独引入这几个模块也可以。但是要在本插件之前。
26 |
27 | #### 初始化
28 |
29 | 本插件会在`d3`全局变量下创建一个`RingGradient`的属性,这个属性是一个构造函数,可以`new`一个环形渐变对象:
30 |
31 | ```js
32 |
33 | var ring = new d3.RingGradient(options)
34 |
35 | ```
36 |
37 | options为选项:
38 |
39 | ```js
40 | var options = {
41 | svg:"#svg",
42 | cx:300,
43 | cy:300,
44 | dotStrokeWidth:5
45 | }
46 |
47 | ```
48 |
49 | options完整属性以及说明如下:
50 |
51 |
52 | | 参数 | 描述 | 类型 | 必须 | 默认值
53 | --- | --- | --- | --- | ---
54 | svg | SVG容器描述 | 合法的CSS选择器字符串或者d3-selection实例 | 是 | -
55 | cx | 圆环中心x坐标 | 数值 | 否 | 0
56 | cy | 圆环中心y坐标 | 数值 | 否 | 0
57 | r | 圆环半径 | 数值 | 否 | 100
58 | color | 颜色插值器 | 插值器,输入范围[0,1] | 否 | 默认"blue"->"red"
59 | ringWidth | 圆环宽度 | 数值 | 否 | 5
60 | dotRadius | 圆环末端圆半径 | 数值 | 否 | 10
61 | dotStrokeWidth | 圆环末端圆边宽度 | 数值 | 否 | 3
62 | dotFill | 圆环末端圆填充色 | CSS颜色字符串 | 否 | "#fff"
63 | textFormat | 圆环中字体格式 | d3-fotmat | 否 | 12.3%
64 | t | 初始化值 | 数值,介于[0,1] | 否 | 0
65 |
66 | #### 更新
67 |
68 | 返回的对象包含一个`update`方法用来更新圆环:
69 |
70 | ```js
71 |
72 | ring.update(t)
73 |
74 | ```
75 |
76 | 其中参数`t`必须,且范围为[0,1]
77 |
78 | #### 移除
79 |
80 | ```js
81 |
82 | ring.delete()
83 |
84 | ```
--------------------------------------------------------------------------------
/ringGradient.js:
--------------------------------------------------------------------------------
1 | (function(global, factory) {
2 | typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3 | typeof define === 'function' && define.amd ? define(['exports'], factory) :
4 | (factory((global.d3 = global.d3 || {})));
5 | }(this,function(exports){
6 | function RingGradient(options){
7 | if(options.svg instanceof d3.selection){
8 | this.container = options.svg;
9 | }else{
10 | if(d3.select(options.svg) instanceof d3.selection){
11 | this.container = d3.select(options.svg)
12 | }else{
13 | throw new Error("container unavailable !");
14 | }
15 | }
16 | this.cx = options.cx?options.cx:0;
17 | this.cy = options.cy?options.cy:0;
18 | this.r = options.radius?options.radius:100;
19 | this.color = options.color?options.color:d3.scaleSequential(d3.interpolate("blue","red"));
20 | this.ringWidth = options.ringWidth?options.ringWidth:5;
21 | this.dotR = options.dotRadius?options.dotRadius:10;
22 | this.dotW = options.dotStrokeWidth?options.dotStrokeWidth:3;
23 | this.dotFill = options.dotFill?options.dotFill:"#FFF";
24 | this.textFormat = options.textFormat?options.textFormat:d3.format(".1%");
25 | this.t = 0;
26 |
27 | this.init = function(){
28 | var defs = this.container.append("defs"),
29 | linear1 = defs.append("linearGradient").attr("id","ringGradient_linear_1")
30 | .attr("x1","0%").attr("x2","0%").attr("y1","0%").attr("y2","100%"),
31 | linear2 = defs.append("linearGradient").attr("id","ringGradient_linear_2")
32 | .attr("x1","0%").attr("x2","0%").attr("y1","100%").attr("y2","0%");
33 | linear1.append("stop").attr("offset","0%").attr("stop-color",this.color(0));
34 | linear1.append("stop").attr("offset","100%").attr("class","ringGradient_linear_1_stop")
35 | .attr("stop-color",this.color(this.t));
36 | linear2.append("stop").attr("offset","0%").attr("stop-color",this.color(0.5));
37 | linear2.append("stop").attr("offset","100%").attr("class","ringGradient_linear_2_stop")
38 | .attr("stop-color",this.color(0.5));
39 | this.g = this.container.append("g")
40 | .attr("class","ringGradient_g")
41 | .attr("transform","translate("+this.cx+","+this.cy+")")
42 | this.ring1 = this.g.append("path").attr("class","ringGradient_ring_1")
43 | .style("stroke","url(#ringGradient_linear_1)")
44 | .attr("stroke-width",this.ringWidth)
45 | .attr("fill","none")
46 | this.ring2 = this.g.append("path").attr("class","ringGradient_ring_2")
47 | .style("stroke","url(#ringGradient_linear_2)")
48 | .attr("stroke-width",this.ringWidth)
49 | .attr("fill","none")
50 | this.dot = this.g.append("circle")
51 | .attr("class","ringGradient_dot")
52 | .attr("r",this.dotR)
53 | .attr("cx",0)
54 | .attr("cy",-this.r)
55 | .attr("stroke-width",this.dotW)
56 | .attr("fill",this.dotFill)
57 | .attr("stroke",this.color(0))
58 | this.text = this.g.append("text")
59 | .attr("class","ringGradient_text")
60 | .attr("font-size",this.r/3)
61 | .attr("font-weight","bold")
62 | .attr("dy",".3em")
63 | .attr("text-anchor","middle")
64 | .text(this.textFormat(this.t))
65 | }
66 | this.update = function(t){
67 | var deltaAngle = Math.PI*2*t,
68 | dx = Math.sin(deltaAngle)*this.r,
69 | dy = Math.cos(deltaAngle)*this.r;
70 | if(t>=1){
71 | this.container.select("#ringGradient_linear_1")
72 | .select(".ringGradient_linear_1_stop")
73 | .attr("stop-color",this.color(0.5))
74 | this.container.select("#ringGradient_linear_2")
75 | .select(".ringGradient_linear_2_stop")
76 | .attr("stop-color",this.color(1))
77 | this.ring2.attr("d","M"+0+" "+this.r+"A"+this.r+" "+this.r+" 180 0 1 "+0+" "+(-this.r));
78 | this.ring1.attr("d","M"+0+" "+(-this.r)+"A"+this.r+" "+this.r+" 180 0 1 0 "+this.r);
79 | this.dot.attr("cx",0).attr("cy",-this.r).attr("stroke",this.color(1));
80 | this.text.text(this.textFormat(t));
81 | }else if(t>=0.5){
82 | this.container.select("#ringGradient_linear_1")
83 | .select(".ringGradient_linear_1_stop")
84 | .attr("stop-color",this.color(0.5))
85 | this.container.select("#ringGradient_linear_2")
86 | .select(".ringGradient_linear_2_stop")
87 | .attr("stop-color",this.color(t))
88 | this.ring1.attr("d","M"+0+" "+(-this.r)+"A"+this.r+" "+this.r+" 180 0 1 0 "+this.r);
89 | this.ring2.attr("d","M"+0+" "+this.r+"A"+this.r+" "+this.r+" "+360*(t-0.5)+" 0 1 "+dx+" "+(-dy));
90 | this.dot.attr("cx",dx)
91 | .attr("cy",-dy)
92 | .attr("stroke",this.color(t))
93 | this.text.text(this.textFormat(t));
94 | }else{
95 | this.container.select("#ringGradient_linear_1")
96 | .select(".ringGradient_linear_1_stop")
97 | .attr("stop-color",this.color(t))
98 | this.container.select("#ringGradient_linear_2")
99 | .select(".ringGradient_linear_2_stop")
100 | .attr("stop-color",this.color(0.5))
101 | this.ring2.attr("d","")
102 | this.ring1.attr("d","M0 "+(-this.r)+"A"+this.r+" "+this.r+" "+ (360*t) +" 0"+" 1 "+(dx)+" "+(-dy));
103 | this.dot.attr("cx",dx)
104 | .attr("cy",-dy)
105 | .attr("stroke",this.color(t))
106 | this.text.text(this.textFormat(t));
107 | }
108 | }
109 | this.delete = function(){
110 | if(this.container){
111 | this.container.select(".ringGradient_g").remove();
112 | }
113 | }
114 | this.init();
115 | return this;
116 | }
117 | exports.RingGradient = RingGradient;
118 | }))
--------------------------------------------------------------------------------
/src/ringGradient.js:
--------------------------------------------------------------------------------
1 | (function(global, factory) {
2 | typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3 | typeof define === 'function' && define.amd ? define(['exports'], factory) :
4 | (factory((global.d3 = global.d3 || {})));
5 | }(this,function(exports){
6 | function RingGradient(options){
7 | if(options.svg instanceof d3.selection){
8 | this.container = options.svg;
9 | }else{
10 | if(d3.select(options.svg) instanceof d3.selection){
11 | this.container = d3.select(options.svg)
12 | }else{
13 | throw new Error("container unavailable !");
14 | }
15 | }
16 | this.cx = options.cx?options.cx:0;
17 | this.cy = options.cy?options.cy:0;
18 | this.r = options.radius?options.radius:100;
19 | this.color = options.color?options.color:d3.scaleSequential(d3.interpolate("blue","red"));
20 | this.ringWidth = options.ringWidth?options.ringWidth:5;
21 | this.dotR = options.dotRadius?options.dotRadius:10;
22 | this.dotW = options.dotStrokeWidth?options.dotStrokeWidth:3;
23 | this.dotFill = options.dotFill?options.dotFill:"#FFF";
24 | this.textFormat = options.textFormat?options.textFormat:d3.format(".1%");
25 | this.t = options.t?options.t:0;
26 |
27 | this.defs = this.container.select("defs").empty()?this.container.append("defs"):this.container.select("defs");
28 | this.uuid = "s_"+uuid();
29 |
30 |
31 |
32 | this.init = function(){
33 | var linear1 = this.defs.append("linearGradient")
34 | .attr("x1","0%").attr("x2","0%").attr("y1","0%").attr("y2","100%")
35 | .attr("class",this.uuid)
36 | .attr("id",this.uuid+"_1"),
37 | linear2 = this.defs.append("linearGradient")
38 | .attr("x1","0%").attr("x2","0%").attr("y1","100%").attr("y2","0%")
39 | .attr("class",this.uuid)
40 | .attr("id",this.uuid+"_2");
41 | linear1.append("stop").attr("offset","0%").attr("stop-color",this.color(0));
42 | linear1.append("stop").attr("offset","100%").attr("class","ringGradient_linear_1_stop")
43 | .attr("stop-color",this.color(this.t));
44 | linear2.append("stop").attr("offset","0%").attr("stop-color",this.color(0.5));
45 | linear2.append("stop").attr("offset","100%").attr("class","ringGradient_linear_2_stop")
46 | .attr("stop-color",this.color(0.5));
47 | this.g = this.container.append("g")
48 | .attr("class","ringGradient_g")
49 | .attr("transform","translate("+this.cx+","+this.cy+")")
50 | this.ring1 = this.g.append("path").attr("class","ringGradient_ring_1")
51 | .style("stroke","url(#"+this.uuid+"_1)")
52 | .attr("stroke-width",this.ringWidth)
53 | .attr("fill","none")
54 | this.ring2 = this.g.append("path").attr("class","ringGradient_ring_2")
55 | .style("stroke","url(#"+this.uuid+"_2)")
56 | .attr("stroke-width",this.ringWidth)
57 | .attr("fill","none")
58 | this.dot = this.g.append("circle")
59 | .attr("class","ringGradient_dot")
60 | .attr("r",this.dotR)
61 | .attr("cx",0)
62 | .attr("cy",-this.r)
63 | .attr("stroke-width",this.dotW)
64 | .attr("fill",this.dotFill)
65 | .attr("stroke",this.color(0))
66 | this.text = this.g.append("text")
67 | .attr("class","ringGradient_text")
68 | .attr("font-size",this.r/3)
69 | .attr("font-weight","bold")
70 | .attr("dy",".3em")
71 | .attr("text-anchor","middle")
72 | .text(this.textFormat(this.t))
73 |
74 | this.update(this.t)
75 | }
76 | this.update = function(t){
77 | this.t = t;
78 | var deltaAngle = Math.PI*2*t,
79 | dx = Math.sin(deltaAngle)*this.r,
80 | dy = Math.cos(deltaAngle)*this.r,
81 | selector2 = "#"+this.uuid+"_2",
82 | selector1 = "#"+this.uuid+"_1";
83 | if(t>=1){
84 | this.container.select(selector1)
85 | .select(".ringGradient_linear_1_stop")
86 | .attr("stop-color",this.color(0.5))
87 | this.container.select(selector2)
88 | .select(".ringGradient_linear_2_stop")
89 | .attr("stop-color",this.color(1))
90 | this.ring2.attr("d","M"+0+" "+this.r+"A"+this.r+" "+this.r+" 180 0 1 "+0+" "+(-this.r));
91 | this.ring1.attr("d","M"+0+" "+(-this.r)+"A"+this.r+" "+this.r+" 180 0 1 0 "+this.r);
92 | this.dot.attr("cx",0).attr("cy",-this.r).attr("stroke",this.color(1));
93 | this.text.text(this.textFormat(t));
94 | }else if(t>=0.5){
95 | this.container.select(selector1)
96 | .select(".ringGradient_linear_1_stop")
97 | .attr("stop-color",this.color(0.5))
98 | this.container.select(selector2)
99 | .select(".ringGradient_linear_2_stop")
100 | .attr("stop-color",this.color(t))
101 | this.ring1.attr("d","M"+0+" "+(-this.r)+"A"+this.r+" "+this.r+" 180 0 1 0 "+this.r);
102 | this.ring2.attr("d","M"+0+" "+this.r+"A"+this.r+" "+this.r+" "+360*(t-0.5)+" 0 1 "+dx+" "+(-dy));
103 | this.dot.attr("cx",dx)
104 | .attr("cy",-dy)
105 | .attr("stroke",this.color(t))
106 | this.text.text(this.textFormat(t));
107 | }else{
108 | this.container.select(selector1)
109 | .select(".ringGradient_linear_1_stop")
110 | .attr("stop-color",this.color(t))
111 | this.container.select(selector2)
112 | .select(".ringGradient_linear_2_stop")
113 | .attr("stop-color",this.color(0.5))
114 | this.ring2.attr("d","")
115 | this.ring1.attr("d","M0 "+(-this.r)+"A"+this.r+" "+this.r+" "+ (360*t) +" 0"+" 1 "+(dx)+" "+(-dy));
116 | this.dot.attr("cx",dx)
117 | .attr("cy",-dy)
118 | .attr("stroke",this.color(t))
119 | this.text.text(this.textFormat(t));
120 | }
121 | }
122 | this.delete = function(){
123 | if(this.container){
124 | this.defs.selectAll(("."+this.uuid)).remove()
125 | this.g.remove();
126 | }
127 | }
128 | this.init();
129 | return this;
130 | }
131 |
132 | function uuid() {
133 | var s = [];
134 | var hexDigits = "0123456789abcdef";
135 | for (var i = 0; i < 36; i++) {
136 | s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
137 | }
138 | s[14] = "4";
139 | s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
140 | s[8] = s[13] = s[18] = s[23] = "_";
141 |
142 | var uuid = s.join("");
143 | return uuid;
144 | }
145 |
146 | exports.RingGradient = RingGradient;
147 | }))
--------------------------------------------------------------------------------