Scripts/resources/[standalone]/rprogress/ui/js/class.RadialProgress.js

445 lines
12 KiB
JavaScript
Raw Normal View History

2024-12-29 20:02:43 +00:00
const NS = "http://www.w3.org/2000/svg";
/**
* Svg Class
*/
class Svg {
constructor(w, h) {
this.width = w;
this.height = h;
this.node = document.createElementNS(NS, "svg");
this.render();
}
render() {
this.node.setAttributeNS(null, "width", this.width);
this.node.setAttributeNS(null, "height", this.height);
}
getNode() {
return this.node;
}
}
/**
* Circle Class
*/
class Circle {
constructor(r, s, min = 0, max = 360, bg = false, zone = false) {
this.radius = r;
this.stroke = s;
this.minAngle = min;
this.maxAngle = max;
this.px = this.radius;
this.py = this.radius;
this.bg = bg;
this.zone = zone;
this.offset = 0;
this.progress = 0;
this.node = document.createElementNS(NS, "circle");
this.render();
}
render() {
this.setArc();
this.node.setAttributeNS(null, "r", this.radius - (this.stroke / 2));
this.node.setAttributeNS(null, "cx", this.px);
this.node.setAttributeNS(null, "cy", this.py);
this.node.setAttributeNS(null, "stroke-width", this.stroke);
}
setArc() {
this.arc = 2 * Math.PI * (this.radius - (this.stroke / 2));
this.gap = this.arc - this.arc * ((this.maxAngle - this.minAngle) / 360);
this.node.setAttributeNS(
null,
"stroke-dasharray",
this.bg ? `${this.arc - this.gap}, ${this.gap}` : this.arc
);
this.setProgress();
}
getNode() {
return this.node;
}
setProgress(progress) {
if (progress === undefined) {
progress = this.progress;
}
let offset = this.arc - this.arc * progress;
if (!this.bg) {
offset = this.arc - (this.arc - this.gap) * progress;
}
this.node.setAttributeNS(null, "data-progress", progress);
this.node.setAttributeNS(null, "stroke-dashoffset", offset);
this.progress = progress;
this.offset = offset;
}
}
class RProgress {
start(to, from, duration) {
this.frame = false;
if (from === undefined) {
from = this.config.progress;
}
if (duration === undefined) {
duration = 5000;
}
let st = Date.now();
// Scroll function
const animate = () => {
let progress,
now = Date.now(),
ct = now - st;
// Cancel after allotted interval
if (ct > duration) {
cancelAnimationFrame(this.frame);
this.setProgress(to, false);
this.config.onChange.call(this, 100, ct, duration);
this.config.onComplete.call(this, to);
return;
}
progress = easings[this.config.easing](ct, from, to - from, duration);
this.setProgress(progress, false);
this.config.onChange.call(this, progress, ct, duration);
// requestAnimationFrame
this.frame = requestAnimationFrame(animate);
};
this.config.onStart.call(this);
animate();
}
pause() {
cancelAnimationFrame(this.frame);
}
stop() {
cancelAnimationFrame(this.frame);
this.remove();
}
render(element) {
if (!this.rendered) {
element =
typeof element === "string" ? document.querySelector(element) : element;
element.appendChild(this.container);
this.setPosition();
this.rendered = true;
}
}
remove() {
const parent = this.container.parentNode;
if (this.rendered && parent) {
parent.removeChild(this.container);
this.rendered = false;
}
}
show() {
this.container.classList.remove("done");
}
hide() {
this.container.classList.add("done");
}
}
/**
* RadialProgress Class
*/
class RadialProgress extends RProgress {
constructor(config) {
super();
const defaultConfig = {
r: 50,
s: 10,
x: 0,
y: 0,
cap: "butt",
padding: 0,
progress: 0,
minAngle: 0,
maxAngle: 360,
rotation: 0,
color: "rgba(255, 255, 255, 1.0)",
zoneColor: "rgba(51, 105, 30, 1)",
bgColor: "rgba(0, 0, 0, 0.4)",
easing: "easeLinear",
onStart: () => {},
onChange: () => {},
onComplete: () => {},
onTimeout: () => {},
};
this.config = Object.assign({}, defaultConfig, config);
this.init();
return this;
}
init() {
const arc = this.config.maxAngle - this.config.minAngle;
this.config.padding *= 2;
this.config.w = (this.config.r * 2) + this.config.padding;
this.config.h = (this.config.r * 2) + this.config.padding;
this.svg = new Svg(this.config.w, this.config.h);
this.dials = {
bg: new Circle(
this.config.r + (this.config.padding / 2),
this.config.s + this.config.padding,
this.config.minAngle,
this.config.maxAngle,
true
),
fg: new Circle(
this.config.r,
this.config.s,
this.config.minAngle,
this.config.maxAngle
)
};
if ( this.config.zone ) {
this.zoneArc = (this.config.zone / 100) * arc;
this.zonePos = getRandomInt(0, arc - this.zoneArc);
const startPos = (this.zonePos / arc) * 100;
this.zoneMin = startPos;
this.zoneMax = startPos + this.config.zone;
this.dials.zone = new Circle(
this.config.r,
this.config.s,
0,
this.zoneArc,
true
)
}
this.svg.getNode().appendChild(this.dials.bg.getNode());
this.svg.getNode().appendChild(this.dials.fg.getNode());
if ( this.dials.zone ) {
this.svg.getNode().appendChild(this.dials.zone.getNode());
this.dials.zone.getNode().setAttributeNS(null, "stroke", this.config.zoneColor);
this.dials.zone.getNode().style.transform = `rotate(${
this.zonePos
}deg)`;
this.dials.zone.getNode().style.transformOrigin = `50% 50% 0`;
}
const fgNode = this.dials.fg.getNode();
fgNode.setAttributeNS(null, "cx", (this.config.w / 2));
fgNode.setAttributeNS(null, "cy", (this.config.h / 2));
fgNode.setAttributeNS(null, "stroke-linecap", this.config.cap);
fgNode.setAttributeNS(null, "stroke", this.config.color);
this.dials.bg.getNode().setAttributeNS(null, "stroke", this.config.bgColor);
const container = document.createElement("div");
container.classList.add("ui-dial");
const indicator = document.createElement("div");
indicator.classList.add("ui-indicator");
const label = document.createElement("div");
label.classList.add("ui-label");
this.setRotation(this.config.rotation);
container.appendChild(this.svg.getNode());
container.appendChild(indicator);
container.appendChild(label);
this.container = container;
this.indicator = indicator;
this.label = label;
this.setPosition(this.config.x, this.config.y);
this.setProgress();
}
setPosition(x, y) {
this.config.x = x;
this.config.y = y;
const size = (this.config.r * 2);
this.container.style.width = `${(this.config.r * 2) + this.config.padding}px`;
this.container.style.height = `${(this.config.r * 2) + this.config.padding}px`;
this.container.style.left = `${
(this.config.x * window.innerWidth) - (size / 2) - (this.config.padding / 2)
}px`;
this.container.style.top = `${
(this.config.y * window.innerHeight) - (size / 2) - (this.config.padding / 2)
}px`;
}
setEndAngle(angle) {
this.config.maxAngle = angle;
this.dials.bg.maxAngle = angle;
this.dials.bg.setArc();
this.dials.fg.maxAngle = angle;
this.dials.fg.setArc();
this.dials.bg.setProgress();
this.dials.fg.setProgress();
}
setRotation(angle) {
this.config.rotation = angle;
this.svg.getNode().style.transform = `rotate(${
this.config.rotation - 90
}deg)`;
}
setProgress(progress, e) {
if (progress === undefined) {
progress = this.config.progress;
}
this.dials.fg.setProgress(progress / 100);
this.progress = progress;
if (e === undefined) {
this.config.onChange.call(this, progress);
}
}
}
class LinearProgress extends RProgress {
constructor(config) {
super();
const defaultConfig = {
w: 300,
h: 10,
s: 10,
x: 0,
y: 0,
cap: "butt",
padding: 0,
progress: 0,
color: "rgba(255, 255, 255, 1.0)",
bgColor: "rgba(0, 0, 0, 0.4)",
easing: "easeLinear",
onStart: () => {},
onChange: () => {},
onComplete: () => {}
};
this.config = Object.assign({}, defaultConfig, config);
this.progress = 0;
this.init();
return this;
}
init() {
this.container = document.createElement("div");
this.container.classList.add("linear-progress");
this.bg = document.createElement("div");
this.fg = document.createElement("div");
this.bg.classList.add("linear-progress-bg");
this.fg.classList.add("linear-progress-fg");
this.label = document.createElement("div");
this.label.classList.add("linear-progress-label");
this.container.appendChild(this.bg);
this.container.appendChild(this.fg);
this.container.appendChild(this.label);
}
setPosition() {
let contCss = `width:${this.config.w}px;height:${this.config.h}px;left:${this.config.x * window.innerWidth - this.config.w / 2}px;top:${this.config.y * window.innerHeight - this.config.h / 2}px;`;
let bgCss = `background-color:${this.config.bgColor};padding:${this.config.padding}px;left:${-this.config.padding}px;top:${-this.config.padding}px;`;
this.container.style.cssText = contCss;
this.fg.style.backgroundColor = this.config.color;
if ( this.config.cap == 'round' ) {
this.fg.style.borderRadius = `${this.config.h / 2}px`
this.fg.style.transform = "scale(1, 1)";
bgCss += `border-radius:${this.config.h / 2 + this.config.padding}px;`;
} else {
this.fg.style.width = "100%";
}
this.bg.style.cssText = bgCss;
}
setProgress(progress, e) {
if (progress === undefined) {
progress = this.config.progress;
}
const p = (progress / 100);
if ( this.config.cap == 'round' ) {
this.fg.style.width = `${progress}%`;
} else {
this.fg.style.transform = `scale(${p}, 1)`;
}
this.progress = progress;
if (e === undefined) {
this.config.onChange.call(this, progress);
}
}
}
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min) + min);
}