-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHealthRing.js
More file actions
97 lines (75 loc) · 3.12 KB
/
HealthRing.js
File metadata and controls
97 lines (75 loc) · 3.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
class HealthRing {
constructor(selector, maxRadius = null, strokeWidth = null) {
const DEFAULT_STROKE_WIDTH = 7;
this.container = document.querySelector(selector);
this.maxRadius = maxRadius;
this.strokeWidth = strokeWidth ?? DEFAULT_STROKE_WIDTH;
if (this.maxRadius !== null) {
this.initContainer();
}
}
initContainer() {
if (this.maxRadius === null) {
console.error(`HealthRing.initContainer() should not be called until maxRadius is set.`);
}
let containerSizePx = 2*this.maxRadius + 2*this.strokeWidth, // the container is a square with sides of this length
svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
this.container.style.width = `${containerSizePx}px`;
this.container.style.height = `${containerSizePx}px`;
svg.style.width = `${containerSizePx}px`;
svg.style.height = `${containerSizePx}px`;
this.container.appendChild(svg);
this.svg = svg;
// startX and startY are the coordinates of the starting point for a ring with radius == maxRadius
// (This is needed to compute the starting coordinates for all rings)
this.startX = this.maxRadius + this.strokeWidth;
this.startY = this.strokeWidth;
}
add (radius, percentage, color) {
if (this.maxRadius === null) {
// If the max radius was not specified in the constructor, assume
// the first health ring being added is the largest
this.maxRadius = radius;
this.initContainer();
}
let grayPath = this.defineArcPath(radius, 1, "#ccc");
let ringPath = this.defineArcPath(radius, percentage, color);
this.svg.appendChild(grayPath);
this.svg.appendChild(ringPath);
return this; // for chaining
}
/**
* defineArcPath
* Returns an SVG <path> element for an arc with a given radius, color, and width.
* The arc represents part of a circle. If percentage = 1, it is a complete circle.
*/
defineArcPath(radius, percentage, color) {
let largeArcFlag = (percentage <= .5) ? 0 : 1, // 0 for small arc, 1 for large
sweepFlag = 1; // 0 for counter-clockwise, 1 for clockwise
if (percentage >= 1) percentage = .97; // exactly 1 does weird things
let startX = this.startX;
let startY = this.startY + this.maxRadius - radius;
let path = document.createElementNS("http://www.w3.org/2000/svg", "path");
path.setAttributeNS(null, "fill", "none");
path.setAttributeNS(null, "stroke", color);
path.setAttributeNS(null, "stroke-width", this.strokeWidth);
path.setAttributeNS(null, "stroke-linecap", "round");
path.setAttributeNS(null, "stroke-miterlimit", "10");
// assuming the path starts at (radius, 0), find the point (px, py) along the
// circle where the path should end
let angleDegrees = percentage * 360,
angleRadians = angleDegrees * (Math.PI/180),
px = radius * Math.sin(angleRadians),
py = radius * (1 - Math.cos(angleRadians));
// See: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d
path.setAttributeNS(
null,
"d",
`M ${startX}, ${startY}
a ${radius}, ${radius}
90, ${largeArcFlag}, ${sweepFlag}
${px}, ${py}
`);
return path;
}
}