Created
June 1, 2021 13:15
-
-
Save MohammadMD1383/ebe65c5cb28bb8ab8cdcf34e5de99000 to your computer and use it in GitHub Desktop.
an Android like progress ring/bar for html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class AndroidProgressBar extends HTMLElement { | |
#root: ShadowRoot; | |
#svg: SVGSVGElement; | |
#circle: SVGCircleElement; | |
static #MIN_SPEED_RATE: number = 1.5; | |
static #MAX_SPEED_RATE: number = 3; | |
constructor() { | |
super(); | |
this.#root = this.attachShadow({mode: "open"}); | |
this.#root.innerHTML = ` | |
<style> | |
div { | |
height: 100%; | |
width: 100%; | |
} | |
/*noinspection CssUnresolvedCustomProperty*/svg { | |
animation: round var(--min-speed) infinite linear; | |
} | |
/*noinspection CssUnresolvedCustomProperty*/circle { | |
transition: stroke-dashoffset 0.3s; | |
stroke-linecap: round; | |
stroke-dasharray: var(--offset) var(--offset); | |
animation: progress-bar var(--max-speed) infinite ease-in-out; | |
transform-origin: 50% 50%; | |
} | |
/*noinspection CssUnresolvedCustomProperty*/@keyframes progress-bar { | |
0% { stroke-dashoffset: var(--max-offset); } | |
45% { stroke-dashoffset: var(--min-offset); } | |
55% { stroke-dashoffset: var(--min-offset); transform: rotate(0deg); } | |
100% { stroke-dashoffset: var(--max-offset); transform: rotate(360deg); } | |
} | |
@keyframes round { | |
from { transform: rotate(0deg); } | |
to { transform: rotate(360deg); } | |
} | |
</style> | |
<div> | |
<svg height="100%" width="100%"> | |
<circle fill="transparent" cx="50%" cy="50%"/> | |
</svg> | |
</div> | |
`; | |
this.#circle = this.#root.querySelector("circle")!; | |
this.#svg = this.#root.querySelector("svg")!; | |
} | |
static get observedAttributes(): Array<string> { | |
return ["color", "thickness", "speed"]; | |
} | |
applyDefaultStyles(): void { | |
this.style.display = "inline-block"; | |
} | |
drawCircle(thickness: number) { | |
const r = Math.min(this.clientWidth, this.clientHeight) / 2 - thickness; | |
if (r > 0) this.#circle.setAttribute("r", r.toString()); | |
const circumference = r * 2 * Math.PI; | |
this.#svg.style.setProperty("--offset", circumference.toString()); | |
this.#svg.style.setProperty("--min-offset", (circumference * 20 / 100).toString()); | |
this.#svg.style.setProperty("--max-offset", (circumference * 90 / 100).toString()); | |
} | |
set thickness(t: string) { | |
const thickness = parseFloat(t); | |
if (thickness < 0) throw new Error("thickness property cannot be a negative value"); | |
this.#circle.style.strokeWidth = thickness.toString(); | |
this.drawCircle(thickness); | |
} | |
set color(c: string) { | |
this.#circle.style.stroke = c; | |
} | |
set speed(s: string) { | |
const speed = parseFloat(s); | |
if (speed < 0) throw new Error("speed property cannot be a negative value"); | |
this.#svg.style.setProperty("--min-speed", `${AndroidProgressBar.#MIN_SPEED_RATE / speed}s`); | |
this.#svg.style.setProperty("--max-speed", `${AndroidProgressBar.#MAX_SPEED_RATE / speed}s`); | |
} | |
connectedCallback(): void { | |
this.applyDefaultStyles(); | |
new ResizeObserver(this.dimenChangedCallBack).observe(this); | |
const color = this.getAttribute("color"); | |
if (color) this.color = color; else throw new Error("color property is not defined"); | |
const thickness = this.getAttribute("thickness"); | |
if (thickness) this.thickness = thickness; else throw new Error("thickness property is not defined"); | |
const speed = this.getAttribute("speed"); | |
this.speed = speed || "1"; | |
} | |
attributeChangedCallback(name: string, oldValue: string, newValue: string): void { | |
if (name === "color") this.color = newValue; | |
if (name === "thickness") this.thickness = newValue; | |
if (name === "speed") this.speed = newValue; | |
} | |
dimenChangedCallBack(entries: ResizeObserverEntry[]) { | |
const _this = entries[0].target as AndroidProgressBar; | |
const thickness = parseFloat(_this.getAttribute("thickness")!); | |
if (thickness < 0) throw new Error("thickness property cannot be a negative value"); | |
_this.drawCircle(thickness); | |
} | |
} | |
customElements.define("android-progress-bar", AndroidProgressBar); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment