Source: progress.js

import { ClassList } from "./classList.js";
import { accFunction } from "./accFunction.js";
import { createProgressHtml } from "./createProgressHtml.js";
import { startTransition } from "./startTransition.js";
import { SmartProgressBar } from "./index.js";

/**
 * @typedef {SmartProgressBar}
 */

/**
 * @class
 * Create instance for main progressbar
 */
export class SmartProgressElement {
    #animationData = {};
    #animationSpeed = 1000;

    /**
     * @param settings
     * @param {string} [settings.name] - name of progress
     * @param {SmartProgressBar} [settings.context] - main bar
     */
    constructor({ context, name }) {
        this.context = context;

        const { container, progressEl, nameEl } = createProgressHtml(this);
        this.HtmlContainer = container;
        this.HtmlProgress = progressEl;
        this.HtmlName = nameEl;

        this.name = name;
        this.show();
    }

    #animationDuration() {
        return this.#animationSpeed;
    }
    // events
    onclick() {}
    ondone() {}

    // progress
    _progress = 0;
    /**
     * Setter and getter for progress
     * @type {number}
     * @param {number}
     */
    set progress(n) {
        this.#setProgress(n);
        if (n === 100) {
            this.ondone();
        }
    }
    get progress() {
        return this._progress;
    }

    _name;
    set name(n) {
        this._name = n;
        this.HtmlName.innerText = n;
    }
    get name() {
        return this._name;
    }

    #changeMainProgress(v) {
        this.context.tryChangeProgress(this, v);
    }
    #setProgress(num) {
        num = parseInt(num);
        if (num === this.progress) return;
        const Ad = this.#animationData;

        this.#stopAnimation(Ad);

        Ad.oldVal = Ad.oldVal ? Ad.oldVal : 0;

        this._progress = num;
        Ad.needVal = num;

        if (num === 100) {
            this.#startFastAnimation(Ad);
            return;
        } else {
            this.#startSmoothAnimation(Ad);
        }
    }
    #changeHtml(val) {
        this.HtmlProgress.innerText = val + "%";
        this.#changeMainProgress(val);
    }
    #startFastAnimation(Ad) {
        let step = Math.ceil((100 - Ad.oldVal) / 8);
        Ad.timerId = setInterval(() => {
            Ad.oldVal += step;

            if (Ad.needVal <= Ad.oldVal) {
                clearInterval(Ad.timerId);
                this.#changeHtml(100);
                this.ondone();
            } else this.#changeHtml(Math.ceil(Ad.oldVal));
        }, 80);
    }
    #startSmoothAnimation(Ad) {
        const start = Ad.oldVal,
            max = Ad.needVal;
        const difference = max - start;

        const animationDurationMilli = this.#animationDuration(),
            animationSpeed = 10;

        const animationEnd = animationDurationMilli / animationSpeed;

        let i = 0;
        // таймер плавной анимации
        Ad.timerId = setInterval(() => {
            if (i === animationEnd) {
                clearInterval(Ad.timerId);
                return;
            }
            const val = Math.ceil(
                start + difference * accFunction(i++ / animationEnd)
            );
            Ad.oldVal = val;
            this.#changeHtml(val);
        }, animationSpeed);
    }
    #stopAnimation(Ad) {
        clearInterval(Ad.timerId);
    }
    /**
     * Add active class
     */
    makeActive() {
        this.HtmlContainer.classList.add(ClassList.liActive);
    }
    /**
     * Remove active class
     */
    removeActive() {
        this.HtmlContainer.classList.remove(ClassList.liActive);
    }
    /**
     * Start hide animation with callback
     * @param {function} afterAnimationDone - callback
     */
    hide(afterAnimationDone = () => {}) {
        startTransition(
            this.HtmlContainer,
            ClassList.liHidden,
            false,
            afterAnimationDone
        );
    }
    /**
     * Start show animation with callback
     * @param {function} afterAnimationDone - callback
     */
    show(afterAnimationDone = () => {}) {
        startTransition(
            this.HtmlContainer,
            ClassList.liHidden,
            true,
            afterAnimationDone
        );
    }
}