环形进度条(从0到100%)效果
最近公司项目中要用到这种类似环形进度条的效果,初始就从0开始动画到100%结束。动画结果始终会停留在100%上,并不会到因为数据的关系停留在一半。
如图
代码如下
demo.html
1 <!doctype html> 2 <html lang="zh"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>demo</title> 8 <style> 9 .rad-prg{ 10 position: relative; 11 } 12 .rad-con{ 13 position: absolute; 14 z-index: 1; 15 top:0; 16 left: 0; 17 text-align: center; 18 width:90px; 19 height: 90px; 20 padding: 10px; 21 font-family: "microsoft yahei"; 22 } 23 </style> 24 </head> 25 <body> 26 <div class="prg-cont rad-prg" id="indicatorContainer"> 27 <div class="rad-con"> 28 <p>¥4999</p> 29 <p>账户总览</p> 30 </div> 31 </div> 32 <script type="text/javascript" src="js/jquery.min.js"></script> 33 <script src="js/radialIndicator.js"></script> 34 <script> 35 36 $('#indicatorContainer').radialIndicator({ 37 barColor: '#007aff', 38 barWidth: 5, 39 initValue: 0, 40 roundCorner : true, 41 percentage: true, 42 displayNumber: false, 43 radius: 50 44 }); 45 46 setTimeout(function(){ 47 var radObj = $('#indicatorContainer2').data('radialIndicator'); 48 radObj.animate(100); 49 },300); 50 51 </script> 52 </body> 53 </html>
radialIndicator.js 这是jquery的插件
1 /* 2 radialIndicator.js v 1.0.0 3 Author: Sudhanshu Yadav 4 Copyright (c) 2015 Sudhanshu Yadav - ignitersworld.com , released under the MIT license. 5 Demo on: ignitersworld.com/lab/radialIndicator.html 6 */ 7 8 ;(function ($, window, document) { 9 "use strict"; 10 //circumfence and quart value to start bar from top 11 var circ = Math.PI * 2, 12 quart = Math.PI / 2; 13 14 //function to convert hex to rgb 15 16 function hexToRgb(hex) { 17 // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") 18 var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; 19 hex = hex.replace(shorthandRegex, function (m, r, g, b) { 20 return r + r + g + g + b + b; 21 }); 22 23 var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); 24 return result ? [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)] : null; 25 } 26 27 function getPropVal(curShift, perShift, bottomRange, topRange) { 28 return Math.round(bottomRange + ((topRange - bottomRange) * curShift / perShift)); 29 } 30 31 32 //function to get current color in case of 33 function getCurrentColor(curPer, bottomVal, topVal, bottomColor, topColor) { 34 var rgbAryTop = topColor.indexOf('#') != -1 ? hexToRgb(topColor) : topColor.match(/\d+/g), 35 rgbAryBottom = bottomColor.indexOf('#') != -1 ? hexToRgb(bottomColor) : bottomColor.match(/\d+/g), 36 perShift = topVal - bottomVal, 37 curShift = curPer - bottomVal; 38 39 if (!rgbAryTop || !rgbAryBottom) return null; 40 41 return 'rgb(' + getPropVal(curShift, perShift, rgbAryBottom[0], rgbAryTop[0]) + ',' + getPropVal(curShift, perShift, rgbAryBottom[1], rgbAryTop[1]) + ',' + getPropVal(curShift, perShift, rgbAryBottom[2], rgbAryTop[2]) + ')'; 42 } 43 44 //to merge object 45 function merge() { 46 var arg = arguments, 47 target = arg[0]; 48 for (var i = 1, ln = arg.length; i < ln; i++) { 49 var obj = arg[i]; 50 for (var k in obj) { 51 if (obj.hasOwnProperty(k)) { 52 target[k] = obj[k]; 53 } 54 } 55 } 56 return target; 57 } 58 59 //function to apply formatting on number depending on parameter 60 function formatter(pattern) { 61 return function (num) { 62 if(!pattern) return num.toString(); 63 num = num || 0 64 var numRev = num.toString().split('').reverse(), 65 output = pattern.split("").reverse(), 66 i = 0, 67 lastHashReplaced = 0; 68 69 //changes hash with numbers 70 for (var ln = output.length; i < ln; i++) { 71 if (!numRev.length) break; 72 if (output[i] == "#") { 73 lastHashReplaced = i; 74 output[i] = numRev.shift(); 75 } 76 } 77 78 //add overflowing numbers before prefix 79 output.splice(lastHashReplaced+1, output.lastIndexOf('#') - lastHashReplaced, numRev.reverse().join("")); 80 81 return output.reverse().join(''); 82 } 83 } 84 85 86 //circle bar class 87 function Indicator(container, indOption) { 88 indOption = indOption || {}; 89 indOption = merge({}, radialIndicator.defaults, indOption); 90 91 this.indOption = indOption; 92 93 //create a queryselector if a selector string is passed in container 94 if (typeof container == "string") 95 container = document.querySelector(container); 96 97 //get the first element if container is a node list 98 if (container.length) 99 container = container[0]; 100 101 this.container = container; 102 103 //create a canvas element 104 var canElm = document.createElement("canvas"); 105 container.appendChild(canElm); 106 107 this.canElm = canElm; // dom object where drawing will happen 108 109 this.ctx = canElm.getContext('2d'); //get 2d canvas context 110 111 //add intial value 112 this.current_value = indOption.initValue || indOption.minValue || 0; 113 114 } 115 116 117 Indicator.prototype = { 118 constructor: radialIndicator, 119 init: function () { 120 var indOption = this.indOption, 121 canElm = this.canElm, 122 ctx = this.ctx, 123 dim = (indOption.radius + indOption.barWidth) * 2, //elm width and height 124 center = dim / 2; //center point in both x and y axis 125 126 127 //create a formatter function 128 this.formatter = typeof indOption.format == "function" ? indOption.format : formatter(indOption.format); 129 130 //maximum text length; 131 this.maxLength = indOption.percentage ? 4 : this.formatter(indOption.maxValue).length; 132 133 canElm.width = dim; 134 canElm.height = dim; 135 136 //draw a grey circle 137 ctx.strokeStyle = indOption.barBgColor; //background circle color 138 ctx.lineWidth = indOption.barWidth; 139 ctx.beginPath(); 140 ctx.arc(center, center, indOption.radius, 0, 2 * Math.PI); 141 ctx.stroke(); 142 143 //store the image data after grey circle draw 144 this.imgData = ctx.getImageData(0, 0, dim, dim); 145 146 //put the initial value if defined 147 this.value(this.current_value); 148 149 return this; 150 }, 151 //update the value of indicator without animation 152 value: function (val) { 153 //return the val if val is not provided 154 if (val === undefined || isNaN(val)) { 155 return this.current_value; 156 } 157 158 val = parseInt(val); 159 160 var ctx = this.ctx, 161 indOption = this.indOption, 162 curColor = indOption.barColor, 163 dim = (indOption.radius + indOption.barWidth) * 2, 164 minVal = indOption.minValue, 165 maxVal = indOption.maxValue, 166 center = dim / 2; 167 168 //limit the val in range of 0 to 100 169 val = val < minVal ? minVal : val > maxVal ? maxVal : val; 170 171 var perVal = Math.round(((val - minVal) * 100 / (maxVal - minVal)) * 100) / 100, //percentage value tp two decimal precision 172 dispVal = indOption.percentage ? perVal + '%' : this.formatter(val); //formatted value 173 174 //save val on object 175 this.current_value = val; 176 177 178 //draw the bg circle 179 ctx.putImageData(this.imgData, 0, 0); 180 181 //get current color if color range is set 182 if (typeof curColor == "object") { 183 var range = Object.keys(curColor); 184 185 for (var i = 1, ln = range.length; i < ln; i++) { 186 var bottomVal = range[i - 1], 187 topVal = range[i], 188 bottomColor = curColor[bottomVal], 189 topColor = curColor[topVal], 190 newColor = val == bottomVal ? bottomColor : val == topVal ? topColor : val > bottomVal && val < topVal ? indOption.interpolate ? getCurrentColor(val, bottomVal, topVal, bottomColor, topColor) : topColor : false; 191 192 if (newColor != false) { 193 curColor = newColor; 194 break; 195 } 196 } 197 } 198 199 //draw th circle value 200 ctx.strokeStyle = curColor; 201 202 //add linecap if value setted on options 203 if (indOption.roundCorner) ctx.lineCap = "round"; 204 205 ctx.beginPath(); 206 ctx.arc(center, center, indOption.radius, -(quart), ((circ) * perVal / 100) - quart, false); 207 ctx.stroke(); 208 209 //add percentage text 210 if (indOption.displayNumber) { 211 var cFont = ctx.font.split(' '), 212 weight = indOption.fontWeight, 213 fontSize = indOption.fontSize || (dim / (this.maxLength - (Math.floor(this.maxLength*1.4/4)-1))); 214 215 cFont = indOption.fontFamily || cFont[cFont.length - 1]; 216 217 218 ctx.fillStyle = indOption.fontColor || curColor; 219 ctx.font = weight +" "+ fontSize + "px " + cFont; 220 ctx.textAlign = "center"; 221 ctx.textBaseline = 'middle'; 222 ctx.fillText(dispVal, center, center); 223 } 224 225 return this; 226 }, 227 //animate progressbar to the value 228 animate: function (val) { 229 230 var indOption = this.indOption, 231 counter = this.current_value || indOption.minValue, 232 self = this, 233 incBy = Math.ceil((indOption.maxValue - indOption.minValue) / (indOption.frameNum || (indOption.percentage ? 100 : 500))), //increment by .2% on every tick and 1% if showing as percentage 234 back = val < counter; 235 236 //clear interval function if already started 237 if (this.intvFunc) clearInterval(this.intvFunc); 238 239 this.intvFunc = setInterval(function () { 240 241 if ((!back && counter >= val) || (back && counter <= val)) { 242 if (self.current_value == counter) { 243 clearInterval(self.intvFunc); 244 return; 245 } else { 246 counter = val; 247 } 248 } 249 250 self.value(counter); //dispaly the value 251 252 if (counter != val) { 253 counter = counter + (back ? -incBy : incBy) 254 }; //increment or decrement till counter does not reach to value 255 }, indOption.frameTime); 256 257 return this; 258 }, 259 //method to update options 260 option: function (key, val) { 261 if (val === undefined) return this.option[key]; 262 263 if (['radius', 'barWidth', 'barBgColor', 'format', 'maxValue', 'percentage'].indexOf(key) != -1) { 264 this.indOption[key] = val; 265 this.init().value(this.current_value); 266 } 267 this.indOption[key] = val; 268 } 269 270 }; 271 272 /** Initializer function **/ 273 function radialIndicator(container, options) { 274 var progObj = new Indicator(container, options); 275 progObj.init(); 276 return progObj; 277 } 278 279 //radial indicator defaults 280 radialIndicator.defaults = { 281 radius: 50, //inner radius of indicator 282 barWidth: 5, //bar width 283 barBgColor: '#eeeeee', //unfilled bar color 284 barColor: '#99CC33', //filled bar color , can be a range also having different colors on different value like {0 : "#ccc", 50 : '#333', 100: '#000'} 285 format: null, //format indicator numbers, can be a # formator ex (##,###.##) or a function 286 frameTime: 10, //miliseconds to move from one frame to another 287 frameNum: null, //Defines numbers of frame in indicator, defaults to 100 when showing percentage and 500 for other values 288 fontColor: null, //font color 289 fontFamily: null, //defines font family 290 fontWeight: 'bold', //defines font weight 291 fontSize : null, //define the font size of indicator number 292 interpolate: true, //interpolate color between ranges 293 percentage: false, //show percentage of value 294 displayNumber: true, //display indicator number 295 roundCorner: false, //have round corner in filled bar 296 minValue: 0, //minimum value 297 maxValue: 100, //maximum value 298 initValue: 0 //define initial value of indicator 299 }; 300 301 window.radialIndicator = radialIndicator; 302 303 //add as a jquery plugin 304 if ($) { 305 $.fn.radialIndicator = function (options) { 306 return this.each(function () { 307 var newPCObj = radialIndicator(this, options); 308 $.data(this, 'radialIndicator', newPCObj); 309 }); 310 }; 311 } 312 313 }(window.jQuery, window, document, void 0));