1- import "./circular-progress-bar.less " ;
1+ import "./circular-progress-bar.css " ;
22
33const classesPrefix = "circular-progress-bar" ;
44
55/**
66 * Create a new html element
7- * @param {String } classes - Some css classes
7+ * @param {String } [ classes] - Some css classes
88 * @param {HTMLElement } [parent] - Parent to append the element
99 * @return {HTMLDivElement }
1010 */
@@ -17,9 +17,28 @@ const wrap = (classes, parent) => {
1717 return element ;
1818} ;
1919
20+ /**
21+ * Get item from an array without overflow
22+ * @param {Array } array - Any array
23+ * @param {Number } index - Any positive number
24+ * @return {* }
25+ */
2026const getLooped = ( array , index ) => array [ index % array . length ] ;
2127
22- const toDeg = percent => percent * ( 360 / 100 ) ;
28+ /**
29+ * Tell if two number are equals
30+ * @param {Number } n1 - Any positive number
31+ * @param {Number } n2 - Any positive number
32+ * @return {Boolean }
33+ */
34+ const equals = ( n1 , n2 ) => Math . abs ( n1 - n2 ) < Number . EPSILON ;
35+
36+ /**
37+ * Change percentage into degree
38+ * @param {Number } percent - Any percentage
39+ * @return {Number }
40+ */
41+ const toDegree = percent => percent * ( 360 / 100 ) ;
2342
2443/**
2544 * Class for CircularProgressBar's Bar
@@ -30,10 +49,10 @@ class Bar {
3049 * Bar constructor
3150 */
3251 constructor ( ) {
33- this . html = wrap ( "bar" ) ;
52+ this . node = wrap ( "bar" ) ;
3453
3554 this . _nodes = ( new Array ( 2 ) ) . fill ( ) . map ( ( ) => {
36- const clip = wrap ( "clip" , this . html ) ;
55+ const clip = wrap ( "clip" , this . node ) ;
3756 return {
3857 clip,
3958 part : wrap ( "part" , clip ) ,
@@ -43,7 +62,7 @@ class Bar {
4362 /**
4463 * @private
4564 */
46- this . _value = 0 ;
65+ this . _value = undefined ;
4766 }
4867
4968 /**
@@ -62,24 +81,27 @@ class Bar {
6281 * @param {Number } [offset=0] - Starting position in %
6382 */
6483 update ( value , time , color , offset = 0 ) {
65- const rotate = `rotate3d(0, 0, 1, ${ toDeg ( value / 2 ) - 179 } deg)` ;
66- this . _nodes . forEach ( ( node ) => {
67- node . clip . style . transitionDuration = `${ time } ms` ;
68- node . part . style . transitionDuration = `${ time } ms` ;
69- node . part . style . transform = rotate ;
70- node . part . style . backgroundColor = color ;
71- } ) ;
84+ if ( ! equals ( value , this . _value ) ) {
85+ const half = value / 2 ;
86+ const rotate = `rotate3d(0,0,1,${ toDegree ( half ) - 180 } deg)` ;
87+ this . _nodes . forEach ( ( node ) => {
88+ node . clip . style . transitionDuration = `${ time } ms` ;
89+ node . part . style . transitionDuration = `${ time } ms` ;
90+ node . part . style . transform = rotate ;
91+ node . part . style . backgroundColor = color ;
92+ } ) ;
7293
73- this . _nodes [ 0 ] . clip . style . transform = `rotate3d(0, 0, 1, ${ toDeg ( offset ) } deg)` ;
74- this . _nodes [ 1 ] . clip . style . transform = `rotate3d(0, 0, 1, ${ toDeg ( ( value / 2 ) + offset ) } deg)` ;
75- this . _value = value ;
94+ this . _nodes [ 0 ] . clip . style . transform = `rotate3d(0,0,1,${ toDegree ( offset ) + 0.3 } deg)` ;
95+ this . _nodes [ 1 ] . clip . style . transform = `rotate3d(0,0,1,${ toDegree ( half + offset ) } deg)` ;
96+ this . _value = value ;
97+ }
7698 }
7799
78100 /**
79101 * Delete the bar from the DOM
80102 */
81103 remove ( ) {
82- this . html . remove ( ) ;
104+ this . node . remove ( ) ;
83105 }
84106}
85107
@@ -90,27 +112,28 @@ class Bar {
90112export default class CircularProgressBar {
91113 /**
92114 * CircularProgressBar constructor
93- * @param {Number|Array<Number> } [value=0] - Starting value or a set of values
94- * @param {CPBOptions } [options] - Some options
115+ * @param {Number|Array<Number> } [value=0] - Starting value or set of values
116+ * @param {CPBOptions } [options] - Component's options
95117 */
96118 constructor ( value = 0 , options ) {
97119 this . options = Object . assign ( CircularProgressBar . defaultOptions , options ) ;
98120
99- this . html = wrap ( ) ;
121+ this . node = wrap ( ) ;
100122 const size = `${ this . options . size } px` ;
101- this . html . style . width = size ;
102- this . html . style . height = size ;
103- this . html . style . backgroundColor = this . options . background ;
123+ this . node . style . width = size ;
124+ this . node . style . height = size ;
125+ this . node . style . backgroundColor = this . options . background ;
104126
105- this . wrapper = wrap ( "wrapper" , this . html ) ;
127+ this . wrapper = wrap ( "wrapper" , this . node ) ;
106128
107- this . valueNode = wrap ( "value" , this . html ) ;
129+ this . valueNode = wrap ( "value" , this . node ) ;
108130 this . valueNode . style . backgroundColor = this . options . valueBackground ;
109- const valueSize = `${ this . options . size - ( this . options . barsWidth * 2 ) } px` ;
131+ const borderWidth = this . options . size * ( this . options . barsWidth / 100 ) ;
132+ const valueSize = `${ this . options . size - ( borderWidth * 2 ) } px` ;
110133 this . valueNode . style . width = valueSize ;
111134 this . valueNode . style . height = valueSize ;
112135 this . valueNode . style . lineHeight = valueSize ;
113- const valueOffset = `${ this . options . barsWidth } px` ;
136+ const valueOffset = `${ borderWidth } px` ;
114137 this . valueNode . style . top = valueOffset ;
115138 this . valueNode . style . left = valueOffset ;
116139 this . valueTextNode = wrap ( "text" , this . valueNode ) ;
@@ -126,40 +149,45 @@ export default class CircularProgressBar {
126149 }
127150
128151 /**
129- * Change value with only one bar
152+ * Change value using only one bar
130153 * @param {Number } value - Any value
131154 */
132155 set value ( value ) {
133156 this . values = [ value ] ;
134157 }
135158
136159 /**
137- * Change values with multiple bars
160+ * Change values using multiple bars
138161 * @param {Array<Number> } values - Any set of value
139162 */
140163 set values ( values ) {
141- if ( this . options . showValue ) {
142- this . valueNode . style . visibility = "" ;
164+ const opts = this . options ;
165+ this . valueNode . style . visibility = opts . showValue ? "" : "hidden" ;
166+ if ( opts . showValue ) {
143167 const sum = values . reduce ( ( acc , value ) => acc + value , 0 ) ;
144- const used = ( values . length === 1 ? values [ 0 ] : sum ) ;
145- const displayed = this . options . valueUnit === "%" ? ( used / this . options . max ) * 100 : used ;
146- this . valueTextNode . textContent = displayed . toFixed ( this . options . valueDecimals ) + this . options . valueUnit ;
147- }
148- else {
149- this . valueNode . style . visibility = "hidden" ;
168+ const used = Math . min ( values . length === 1 ? values [ 0 ] : sum , opts . max ) ;
169+ if ( used === opts . max && opts . valueWhenDone ) {
170+ if ( this . valueTextNode . textContent !== opts . valueWhenDone ) {
171+ setTimeout ( ( ) => this . valueTextNode . textContent = opts . valueWhenDone , opts . transitionTime ) ;
172+ }
173+ }
174+ else {
175+ const displayed = opts . valueUnit === "%" ? ( used / opts . max ) * 100 : used ;
176+ this . valueTextNode . textContent = displayed . toFixed ( opts . valueDecimals ) + opts . valueUnit ;
177+ }
150178 }
151179
152180 let offset = 0 ;
153181 let lastIndex = 0 ;
154182 values . forEach ( ( value , index ) => {
155183 let bar = this . _bars [ index ] ;
156184 if ( ! bar ) {
157- bar = new Bar ( this . options . colors [ index ] ) ;
185+ bar = new Bar ( opts . colors [ index ] ) ;
158186 this . _bars . push ( bar ) ;
159- this . wrapper . appendChild ( bar . html ) ;
187+ this . wrapper . appendChild ( bar . node ) ;
160188 }
161- const percentage = ( value / this . options . max ) * 100 ;
162- bar . update ( percentage , this . options . transitionTime , getLooped ( this . options . colors , index ) , offset ) ;
189+ const percentage = ( Math . min ( value , opts . max ) / opts . max ) * 100 ;
190+ bar . update ( percentage , opts . transitionTime , getLooped ( opts . colors , index ) , offset ) ;
163191 offset += percentage ;
164192 lastIndex = index ;
165193 } ) ;
@@ -183,26 +211,34 @@ export default class CircularProgressBar {
183211 }
184212
185213 /**
186- * Append the component to another element
214+ * Append the component to the DOM
187215 * @param {HTMLElement } parent - Another DOM element
188216 */
189217 appendTo ( parent ) {
190- parent . appendChild ( this . html ) ;
218+ parent . appendChild ( this . node ) ;
219+ }
220+
221+ /**
222+ * Remove it from the DOM
223+ */
224+ remove ( ) {
225+ this . node . remove ( ) ;
191226 }
192227
193228
194229 /**
195230 * @typedef {Object } CPBOptions
196231 * @prop {Number } [size=150] - Component diameter in pixels
197- * @prop {Number } [barsWidth=10 ] - Width of bars
232+ * @prop {Number } [barsWidth=7 ] - Width of bars in % of size
198233 * @prop {Number } [max=100] - Value for a full 360° rotation
199- * @prop {Boolean } [showValue=true] - Whether or not to display current value inside (if multiple value, sum is displayed)
234+ * @prop {Boolean } [showValue=true] - Whether or not to display current value (if multiple value, sum is displayed)
200235 * @prop {Number } [valueDecimals=0] - Number of decimals to display
201236 * @prop {String } [valueUnit="%"] - Unit used for display (if set to "%", value is calculated over max)
202237 * @prop {String } [valueBackground="#333"] - Background color for value
203238 * @prop {Array<String> } [colors] - Set of colors to use for bars
204- * @prop {String } [background="#666 "] - Background color where there's no bar
239+ * @prop {String } [background="rgba(0, 0, 0, .3) "] - Background color where there's no bar
205240 * @prop {Number } [transitionTime=500] - Transition duration
241+ * @prop {String } [valueWhenDone=""] - Text to display when at 100%
206242 */
207243 /**
208244 * Returns the default options of the component
@@ -211,15 +247,16 @@ export default class CircularProgressBar {
211247 static get defaultOptions ( ) {
212248 return {
213249 size : 150 ,
214- barsWidth : 10 ,
250+ barsWidth : 7 ,
215251 max : 100 ,
216252 showValue : true ,
217253 valueDecimals : 0 ,
218254 valueUnit : "%" ,
219255 valueBackground : "#333" ,
220- colors : [ "#ffa114" , "#4714ff" , "#ff14c8" , "#c8ff14" , "#ff203a " , "#3aff20 " , "#204dff " ] ,
256+ colors : [ "#0095ff" , "# ffa114", "#4714ff" , "#ff14c8" , "#c8ff14" , "#204dff " , "#ff203a " , "#3aff20 " ] ,
221257 background : "rgba(0, 0, 0, .3)" ,
222258 transitionTime : 500 ,
259+ valueWhenDone : "" ,
223260 } ;
224261 }
225262}
0 commit comments