Skip to content

Commit 99de88c

Browse files
author
Guillaume Martigny
committed
v0.2.0
1 parent e622763 commit 99de88c

File tree

8 files changed

+7914
-36
lines changed

8 files changed

+7914
-36
lines changed

boiler.js

Lines changed: 0 additions & 10 deletions
This file was deleted.

circular-progress-bar.js

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
import "./circular-progress-bar.less";
2+
3+
const classesPrefix = "circular-progress-bar";
4+
5+
/**
6+
* Create a new html element
7+
* @param {String} classes - Some css classes
8+
* @param {HTMLElement} [parent] - Parent to append the element
9+
* @return {HTMLDivElement}
10+
*/
11+
const wrap = (classes, parent) => {
12+
const element = document.createElement("div");
13+
element.className = classesPrefix + (classes ? `-${classes}` : "");
14+
if (parent) {
15+
parent.appendChild(element);
16+
}
17+
return element;
18+
};
19+
20+
const getLooped = (array, index) => array[index % array.length];
21+
22+
const toDeg = percent => percent * (360 / 100);
23+
24+
/**
25+
* Class for CircularProgressBar's Bar
26+
* @class
27+
*/
28+
class Bar {
29+
/**
30+
* Bar constructor
31+
*/
32+
constructor () {
33+
this.html = wrap("bar");
34+
35+
this._nodes = (new Array(2)).fill().map(() => {
36+
const clip = wrap("clip", this.html);
37+
return {
38+
clip,
39+
part: wrap("part", clip),
40+
};
41+
});
42+
43+
/**
44+
* @private
45+
*/
46+
this._value = 0;
47+
}
48+
49+
/**
50+
* Returns this bar's value
51+
* @return {Number}
52+
*/
53+
get value () {
54+
return this._value;
55+
}
56+
57+
/**
58+
* Change the bar value and look
59+
* @param {Number} value - New value in %
60+
* @param {Number} time - Time in ms to change
61+
* @param {String} color - Color to use
62+
* @param {Number} [offset=0] - Starting position in %
63+
*/
64+
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+
});
72+
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;
76+
}
77+
78+
/**
79+
* Delete the bar from the DOM
80+
*/
81+
remove () {
82+
this.html.remove();
83+
}
84+
}
85+
86+
/**
87+
* Class for CircularProgressBar
88+
* @class
89+
*/
90+
export default class CircularProgressBar {
91+
/**
92+
* CircularProgressBar constructor
93+
* @param {Number|Array<Number>} [value=0] - Starting value or a set of values
94+
* @param {CPBOptions} [options] - Some options
95+
*/
96+
constructor (value = 0, options) {
97+
this.options = Object.assign(CircularProgressBar.defaultOptions, options);
98+
99+
this.html = wrap();
100+
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;
104+
105+
this.wrapper = wrap("wrapper", this.html);
106+
107+
this.valueNode = wrap("value", this.html);
108+
this.valueNode.style.backgroundColor = this.options.valueBackground;
109+
const valueSize = `${this.options.size - (this.options.barsWidth * 2)}px`;
110+
this.valueNode.style.width = valueSize;
111+
this.valueNode.style.height = valueSize;
112+
this.valueNode.style.lineHeight = valueSize;
113+
const valueOffset = `${this.options.barsWidth}px`;
114+
this.valueNode.style.top = valueOffset;
115+
this.valueNode.style.left = valueOffset;
116+
this.valueTextNode = wrap("text", this.valueNode);
117+
this.valueTextNode.style.fontSize = `${this.options.size / 5}px`;
118+
119+
/**
120+
* @type {Array<Bar>}
121+
* @private
122+
*/
123+
this._bars = [];
124+
125+
this.values = Array.isArray(value) ? value : [value];
126+
}
127+
128+
/**
129+
* Change value with only one bar
130+
* @param {Number} value - Any value
131+
*/
132+
set value (value) {
133+
this.values = [value];
134+
}
135+
136+
/**
137+
* Change values with multiple bars
138+
* @param {Array<Number>} values - Any set of value
139+
*/
140+
set values (values) {
141+
if (this.options.showValue) {
142+
this.valueNode.style.visibility = "";
143+
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";
150+
}
151+
152+
let offset = 0;
153+
let lastIndex = 0;
154+
values.forEach((value, index) => {
155+
let bar = this._bars[index];
156+
if (!bar) {
157+
bar = new Bar(this.options.colors[index]);
158+
this._bars.push(bar);
159+
this.wrapper.appendChild(bar.html);
160+
}
161+
const percentage = (value / this.options.max) * 100;
162+
bar.update(percentage, this.options.transitionTime, getLooped(this.options.colors, index), offset);
163+
offset += percentage;
164+
lastIndex = index;
165+
});
166+
this._bars.splice(lastIndex + 1, this._bars.length).forEach(bar => bar.remove());
167+
}
168+
169+
/**
170+
* Returns current value with only one bar
171+
* @return {Number}
172+
*/
173+
get value () {
174+
return this._bars[0].value;
175+
}
176+
177+
/**
178+
* Returns current value of all bars
179+
* @return {Array<Number>}
180+
*/
181+
get values () {
182+
return this._bars.map(bar => bar.value);
183+
}
184+
185+
/**
186+
* Append the component to another element
187+
* @param {HTMLElement} parent - Another DOM element
188+
*/
189+
appendTo (parent) {
190+
parent.appendChild(this.html);
191+
}
192+
193+
194+
/**
195+
* @typedef {Object} CPBOptions
196+
* @prop {Number} [size=150] - Component diameter in pixels
197+
* @prop {Number} [barsWidth=10] - Width of bars
198+
* @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)
200+
* @prop {Number} [valueDecimals=0] - Number of decimals to display
201+
* @prop {String} [valueUnit="%"] - Unit used for display (if set to "%", value is calculated over max)
202+
* @prop {String} [valueBackground="#333"] - Background color for value
203+
* @prop {Array<String>} [colors] - Set of colors to use for bars
204+
* @prop {String} [background="#666"] - Background color where there's no bar
205+
* @prop {Number} [transitionTime=500] - Transition duration
206+
*/
207+
/**
208+
* Returns the default options of the component
209+
* @return {CPBOptions}
210+
*/
211+
static get defaultOptions () {
212+
return {
213+
size: 150,
214+
barsWidth: 10,
215+
max: 100,
216+
showValue: true,
217+
valueDecimals: 0,
218+
valueUnit: "%",
219+
valueBackground: "#333",
220+
colors: ["#ffa114", "#4714ff", "#ff14c8", "#c8ff14", "#ff203a", "#3aff20", "#204dff"],
221+
background: "rgba(0, 0, 0, .3)",
222+
transitionTime: 500,
223+
};
224+
}
225+
}

circular-progress-bar.less

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
@prefix: circular-progress-bar;
2+
3+
.@{prefix} {
4+
position: relative;
5+
display: inline-block;
6+
border-radius: 50%;
7+
cursor: default;
8+
9+
.full(@s:100%) {
10+
position: absolute;
11+
width: @s;
12+
height: @s;
13+
}
14+
15+
// wrap all bars
16+
.@{prefix}-wrapper {
17+
.full();
18+
19+
// one for every bar
20+
.@{prefix}-bar {
21+
.full();
22+
23+
.@{prefix}-clip {
24+
.full();
25+
clip-path: inset(0 0 0 50%);
26+
transition: transform ease-out;
27+
28+
.@{prefix}-part {
29+
.full();
30+
clip-path: inset(0 0 0 50%);
31+
transition-timing-function: ease-out;
32+
transition-property: transform, background-color;
33+
// Bar's color is here
34+
}
35+
}
36+
}
37+
}
38+
39+
.@{prefix}-value {
40+
position: absolute;
41+
border-radius: 50%;
42+
text-align: center;
43+
font-family: monospace;
44+
45+
.@{prefix}-text {
46+
color: #FFF;
47+
mix-blend-mode: difference;
48+
}
49+
}
50+
}

documentations.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,20 @@
11
# Documentations
22

3+
## CircularProgressBar
4+
5+
**CircularProgressBar** constructor (value, options)
6+
7+
8+
9+
## CPBOptions
10+
11+
* @prop {Number} [size=150] - Component diameter in pixels
12+
* @prop {Number} [barsWidth=10] - Width of bars
13+
* @prop {Number} [max=100] - Value for a full 360° rotation
14+
* @prop {Boolean} [showValue=true] - Whether or not to display current value inside (if multiple value, sum is displayed)
15+
* @prop {Number} [valueDecimals=0] - Number of decimals to display
16+
* @prop {String} [valueUnit="%"] - Unit used for display (if set to "%", value is calculated over max)
17+
* @prop {String} [valueBackground="#333"] - Background color for value
18+
* @prop {Array<String>} [colors] - Set of colors to use for bars
19+
* @prop {String} [background="#666"] - Background color where there's no bar
20+
* @prop {Number} [transitionTime=500] - Transition duration

0 commit comments

Comments
 (0)