UNPKG

vue3-radial-progress

Version:

A smart and light radial progress bar component for Vue 3.

382 lines (354 loc) 12.9 kB
'use strict';var vue=require('vue');function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }function randomString() { return Math.random().toString(16).substring(2); }var script = vue.defineComponent({ props: { // Sets width/diameter of the inner stroke. diameter: { type: Number, required: false, default: 200 }, // Sets the total steps/progress to the end. totalSteps: { type: Number, required: true, default: 10 }, // Sets the current progress of the inner stroke. completedSteps: { type: Number, required: true, default: 0 }, // Sets the start color of the inner stroke (gradient). startColor: { type: String, required: false, default: "#00C58E" }, // Sets the end color of the inner stroke (gradient). stopColor: { type: String, required: false, default: "#00E0A1" }, // Sets the color of the inner stroke to be applied to the shape. innerStrokeColor: { type: String, required: false, default: "#2F495E" }, // Sets the width of the stroke to be applied to the shape. // Read more: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-width strokeWidth: { type: Number, required: false, default: 10 }, // Sets the width of the inner stroke to be applied to the shape. innerStrokeWidth: { type: Number, required: false, default: 10 }, // Sets the shape to be used at the end of stroked. // Read more: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-linecap strokeLinecap: { type: String, required: false, default: "round" }, // Sets how long the animation should take to complete one cycle. // Read more: https://www.w3schools.com/cssref/css3_pr_animation-duration.asp animateSpeed: { type: Number, required: false, default: 1000 }, // Sets the frames per seconds to update inner stroke animation. fps: { type: Number, required: false, default: 60 }, // Sets how the animation progresses through the duration of each cycle. // Read more: https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timing-function timingFunc: { type: String, required: false, default: "linear" }, // Sets the inner stroke direction. isClockwise: { type: Boolean, required: false, default: true } }, setup: function setup(props) { var gradient = vue.reactive({ fx: 0.99, fy: 0.5, cx: 0.5, cy: 0.5, r: 0.65 }); var radialGradientId = "rg-".concat(randomString()); var strokeDashoffset = vue.ref(0); var currentAngle = vue.ref(0); var gradientAnimation = vue.ref(null); var radius = vue.computed(function () { return props.diameter / 2; }); var innerCircleDiameter = vue.computed(function () { return props.diameter - props.innerStrokeWidth * 2; }); var circumference = vue.computed(function () { return Math.PI * innerCircleDiameter.value; }); var stepSize = vue.computed(function () { return props.totalSteps === 0 ? 0 : 100 / props.totalSteps; }); var finishedPercentage = vue.computed(function () { return stepSize.value * props.completedSteps; }); var circleSlice = vue.computed(function () { return 2 * Math.PI / props.totalSteps; }); var animationIncrements = vue.computed(function () { return 100 / props.fps; }); var totalPoints = vue.computed(function () { return props.animateSpeed / animationIncrements.value; }); var animateSlice = vue.computed(function () { return circleSlice.value / totalPoints.value; }); var innerCircleRadius = vue.computed(function () { return innerCircleDiameter.value / 2; }); var containerStyle = vue.computed(function () { return { height: "".concat(props.diameter, "px"), width: "".concat(props.diameter, "px") }; }); var progressStyle = vue.computed(function () { return { height: "".concat(props.diameter, "px"), width: "".concat(props.diameter, "px"), strokeWidth: "".concat(props.strokeWidth, "px"), strokeDashoffset: strokeDashoffset.value, transition: "stroke-dashoffset ".concat(props.animateSpeed, "ms ").concat(props.timingFunc) }; }); var strokeStyle = vue.computed(function () { return { height: "".concat(props.diameter, "px"), width: "".concat(props.diameter, "px"), strokeWidth: "".concat(props.innerStrokeWidth, "px") }; }); var innerCircleStyle = vue.computed(function () { return { width: "".concat(innerCircleDiameter.value, "px") }; }); vue.watch(function () { return [props.diameter, props.totalSteps, props.completedSteps, props.strokeWidth]; }, changeProgress, { immediate: true }); function getPointOfCircle(angle) { var radius = 0.5; var x = radius + radius * Math.cos(angle); var y = radius + radius * Math.sin(angle); return { x: x, y: y }; } function gotoPoint() { var point = getPointOfCircle(currentAngle.value); if (point.x && point.y) { gradient.fx = point.x; gradient.fy = point.y; } } function direction() { return props.isClockwise ? 1 : -1; } function changeProgress() { strokeDashoffset.value = (100 - finishedPercentage.value) / 100 * circumference.value * direction(); if (gradientAnimation.value) { clearInterval(gradientAnimation.value); } var angleOffset = (props.completedSteps - 1) * circleSlice.value; var i = (currentAngle.value - angleOffset) / animateSlice.value; var incrementer = Math.abs(i - totalPoints.value) / totalPoints.value; var isMoveForward = i < totalPoints.value; gradientAnimation.value = setInterval(function () { if (isMoveForward && i >= totalPoints.value || !isMoveForward && i < totalPoints.value) { gradientAnimation.value && clearInterval(gradientAnimation.value); return; } currentAngle.value = angleOffset + animateSlice.value * i; gotoPoint(); i += isMoveForward ? incrementer : -incrementer; }, animationIncrements.value); } return { gradientAnimation: gradientAnimation, innerCircleRadius: innerCircleRadius, radialGradientId: radialGradientId, strokeDashoffset: strokeDashoffset, innerCircleStyle: innerCircleStyle, containerStyle: containerStyle, circumference: circumference, progressStyle: progressStyle, currentAngle: currentAngle, strokeStyle: strokeStyle, gradient: gradient, radius: radius }; } });var _hoisted_1 = ["width", "height"]; var _hoisted_2 = ["id", "fx", "fy", "cx", "cy", "r"]; var _hoisted_3 = ["stop-color"]; var _hoisted_4 = ["stop-color"]; var _hoisted_5 = ["r", "cx", "cy", "stroke", "stroke-dasharray", "stroke-linecap"]; var _hoisted_6 = ["transform", "r", "cx", "cy", "stroke", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap"]; function render(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("div", { class: "vrp__wrapper", style: vue.normalizeStyle(_ctx.containerStyle) }, [vue.createElementVNode("div", { class: "vrp__inner", style: vue.normalizeStyle(_ctx.innerCircleStyle) }, [vue.renderSlot(_ctx.$slots, "default")], 4), (vue.openBlock(), vue.createElementBlock("svg", { width: _ctx.diameter, height: _ctx.diameter, version: "1.1", xmlns: "http://www.w3.org/2000/svg" }, [vue.createElementVNode("defs", null, [vue.createElementVNode("radialGradient", { id: _ctx.radialGradientId, fx: _ctx.gradient.fx, fy: _ctx.gradient.fy, cx: _ctx.gradient.cx, cy: _ctx.gradient.cy, r: _ctx.gradient.r }, [vue.createElementVNode("stop", { offset: "30%", "stop-color": _ctx.startColor }, null, 8, _hoisted_3), vue.createElementVNode("stop", { offset: "100%", "stop-color": _ctx.stopColor }, null, 8, _hoisted_4)], 8, _hoisted_2)]), vue.createElementVNode("circle", { r: _ctx.innerCircleRadius, cx: _ctx.radius, cy: _ctx.radius, fill: "transparent", stroke: _ctx.innerStrokeColor, "stroke-dasharray": _ctx.circumference, "stroke-dashoffset": "0", "stroke-linecap": _ctx.strokeLinecap, style: vue.normalizeStyle(_ctx.strokeStyle) }, null, 12, _hoisted_5), vue.createElementVNode("circle", { transform: 'rotate(270, ' + _ctx.radius + ',' + _ctx.radius + ')', r: _ctx.innerCircleRadius, cx: _ctx.radius, cy: _ctx.radius, fill: "transparent", stroke: "url('#".concat(_ctx.radialGradientId, "')"), "stroke-dasharray": _ctx.circumference, "stroke-dashoffset": _ctx.circumference, "stroke-linecap": _ctx.strokeLinecap, style: vue.normalizeStyle(_ctx.progressStyle) }, null, 12, _hoisted_6)], 8, _hoisted_1))], 4); }function styleInject(css, ref) { if ( ref === void 0 ) ref = {}; var insertAt = ref.insertAt; if (!css || typeof document === 'undefined') { return; } var head = document.head || document.getElementsByTagName('head')[0]; var style = document.createElement('style'); style.type = 'text/css'; if (insertAt === 'top') { if (head.firstChild) { head.insertBefore(style, head.firstChild); } else { head.appendChild(style); } } else { head.appendChild(style); } if (style.styleSheet) { style.styleSheet.cssText = css; } else { style.appendChild(document.createTextNode(css)); } }var css_248z = "\n.vrp__wrapper[data-v-6a0cf1f6] {\r\n position: relative;\n}\n.vrp__inner[data-v-6a0cf1f6] {\r\n position: absolute;\r\n top: 0;\r\n right: 0;\r\n bottom: 0;\r\n left: 0;\r\n border-radius: 50%;\r\n margin: 0 auto;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\n}\r\n"; styleInject(css_248z);script.render = render; script.__scopeId = "data-v-6a0cf1f6";// Default export is installable instance of component. // IIFE injects install function into component, allowing component // to be registered via Vue.use() as well as Vue.component(), var component = /*#__PURE__*/(function () { // Assign InstallableComponent type var installable = script; // Attach install function executed by Vue.use() installable.install = function (app) { app.component("RadialProgressBar", installable); }; return installable; })(); // It's possible to expose named exports when writing components that can // also be used as directives, etc. - eg. import { RollupDemoDirective } from 'rollup-demo'; // export const RollupDemoDirective = directive; var namedExports=/*#__PURE__*/Object.freeze({__proto__:null,'default':component});Object.entries(namedExports).forEach(function (_ref) { var _ref2 = _slicedToArray(_ref, 2), exportName = _ref2[0], exported = _ref2[1]; if (exportName !== "default") component[exportName] = exported; });module.exports=component;