├── 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 | 16 | 17 | 47 | 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ringGradient 2 | SVG环形渐变 3 | 4 | 5 | 6 | 效果图如下 7 | 8 | ![image](https://github.com/xswei/ringGradient/blob/master/1.gif) 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 | })) --------------------------------------------------------------------------------