Files
WAI_Project_UNIX/uni_modules/cool-ui/components/cl-loading/cl-loading.uvue
2025-09-04 20:18:18 +08:00

233 lines
4.2 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view
ref="loadingRef"
class="cl-loading"
:class="[
{
'cl-loading--dark': isDark && color == '',
'cl-loading--spin': loading,
'!border-r-transparent': true
},
pt.className
]"
:style="{
height: getPx(size!),
width: getPx(size!),
// #ifndef APP
borderWidth: '1px',
borderTopColor: color,
borderRightColor: 'transparent',
borderBottomColor: color,
borderLeftColor: color
// #endif
}"
v-if="loading"
>
</view>
</template>
<script setup lang="ts">
import { computed, nextTick, onMounted, ref, shallowRef, watch } from "vue";
import { ctx, isDark, parsePt } from "@/cool";
import type { ClIconProps } from "../cl-icon/props";
import { useSize } from "../../hooks";
defineOptions({
name: "cl-loading"
});
// 定义组件属性
const props = defineProps({
// 透传样式
pt: {
type: Object,
default: () => ({})
},
// 是否加载中
loading: {
type: Boolean,
default: true
},
// 图标大小
size: {
type: [Number, String],
default: 24
},
// 图标颜色
color: {
type: String,
default: ""
}
});
const { getPxValue, getPx } = useSize();
// 透传样式类型定义
type PassThrough = {
className?: string;
icon?: ClIconProps;
};
// 解析透传样式
const pt = computed(() => parsePt<PassThrough>(props.pt));
// 组件引用
const loadingRef = shallowRef<UniElement | null>(null);
const color = computed<string>(() => {
if (props.color == "") {
return isDark.value ? "#ffffff" : (ctx.color["surface-700"] as string);
}
switch (props.color) {
case "primary":
return ctx.color["primary-500"] as string;
case "success":
return "#22c55e";
case "warn":
return "#eab308";
case "error":
return "#ef4444";
case "info":
return "#71717a";
case "dark":
return "#3f3f46";
case "light":
return "#ffffff";
case "disabled":
return "#d4d4d8";
default:
return props.color;
}
});
async function drawLoading() {
// #ifdef APP
await nextTick();
if (loadingRef.value == null) {
return;
}
const drawContext = loadingRef.value!.getDrawableContext();
// 重置画布准备绘制新的loading图形
drawContext!.reset();
drawContext!.beginPath();
// 获取loading图标的尺寸和半径
const size = getPxValue(props.size!);
const radius = size / 2;
const centerX = radius;
const centerY = radius;
// 设置线宽
const lineWidth = 1;
// 缺口角度为60度Math.PI / 3用于形成loading的缺口效果
const gapAngle = Math.PI / 3; // 缺口60度
// 起始角度为顶部(-90度
const startAngle = -Math.PI / 2; // 从顶部开始
// 结束角度为起始角度加上300度360-60形成环形缺口
const endAngle = startAngle + (2 * Math.PI - gapAngle); // 画300度
// 绘制圆弧形成loading环
drawContext!.arc(centerX, centerY, radius - lineWidth, startAngle, endAngle, false);
// 设置描边颜色和线宽
drawContext!.strokeStyle = color.value;
drawContext!.lineWidth = lineWidth;
// 执行描边操作
drawContext!.stroke();
// 更新画布显示
drawContext!.update();
// #endif
}
// 开始旋转动画
async function start() {
// #ifdef APP
await drawLoading();
if (loadingRef.value == null) {
return;
}
loadingRef.value!.animate(
[
{
transform: "rotate(0deg)"
},
{
transform: "rotate(360deg)"
}
],
{
duration: 2500,
easing: "linear",
iterations: 999999
}
);
// #endif
}
// 组件挂载后监听loading状态
onMounted(() => {
// #ifdef APP
watch(
computed(() => props.loading),
(val: boolean) => {
// 当loading为true时开始旋转
if (val) {
start();
}
},
{
immediate: true
}
);
watch(
computed(() => [props.color, props.size, isDark.value]),
() => {
drawLoading();
}
);
// #endif
});
</script>
<style lang="scss" scoped>
.cl-loading {
@apply flex flex-row items-center justify-center rounded-full;
// #ifndef APP
@apply border-surface-700 border-solid;
// #endif
&--dark {
border-color: white !important;
border-right-color: transparent !important;
}
// #ifdef H5 || MP
&--spin {
animation: spin 2.5s linear infinite;
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
// #endif
}
</style>