Files
WAI_Project_UNIX/uni_modules/cool-ui/components/cl-loading/cl-loading.uvue

233 lines
4.2 KiB
Plaintext
Raw Normal View History

2025-07-21 16:47:04 +08:00
<template>
<view
2025-09-04 20:18:18 +08:00
ref="loadingRef"
2025-07-21 16:47:04 +08:00
class="cl-loading"
:class="[
{
2025-08-08 11:28:28 +08:00
'cl-loading--dark': isDark && color == '',
2025-07-21 16:47:04 +08:00
'cl-loading--spin': loading,
'!border-r-transparent': true
},
pt.className
]"
:style="{
2025-09-04 20:18:18 +08:00
height: getPx(size!),
width: getPx(size!),
// #ifndef APP
borderWidth: '1px',
2025-08-08 11:28:28 +08:00
borderTopColor: color,
borderRightColor: 'transparent',
borderBottomColor: color,
borderLeftColor: color
2025-09-04 20:18:18 +08:00
// #endif
2025-07-21 16:47:04 +08:00
}"
v-if="loading"
>
</view>
</template>
<script setup lang="ts">
2025-09-04 20:18:18 +08:00
import { computed, nextTick, onMounted, ref, shallowRef, watch } from "vue";
import { ctx, isDark, parsePt } from "@/cool";
2025-07-21 16:47:04 +08:00
import type { ClIconProps } from "../cl-icon/props";
import { useSize } from "../../hooks";
2025-07-21 16:47:04 +08:00
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: ""
}
});
2025-09-04 20:18:18 +08:00
const { getPxValue, getPx } = useSize();
2025-07-21 16:47:04 +08:00
// 透传样式类型定义
type PassThrough = {
className?: string;
icon?: ClIconProps;
};
// 解析透传样式
const pt = computed(() => parsePt<PassThrough>(props.pt));
2025-09-04 20:18:18 +08:00
// 组件引用
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
}
2025-07-21 16:47:04 +08:00
// 开始旋转动画
2025-09-04 20:18:18 +08:00
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
2025-07-21 16:47:04 +08:00
}
2025-09-04 20:18:18 +08:00
);
// #endif
2025-07-21 16:47:04 +08:00
}
// 组件挂载后监听loading状态
onMounted(() => {
2025-09-04 20:18:18 +08:00
// #ifdef APP
2025-07-21 16:47:04 +08:00
watch(
computed(() => props.loading),
(val: boolean) => {
// 当loading为true时开始旋转
if (val) {
start();
}
},
{
immediate: true
}
);
2025-09-04 20:18:18 +08:00
watch(
computed(() => [props.color, props.size, isDark.value]),
() => {
drawLoading();
}
);
2025-07-21 16:47:04 +08:00
// #endif
});
</script>
<style lang="scss" scoped>
.cl-loading {
@apply flex flex-row items-center justify-center rounded-full;
2025-09-04 20:18:18 +08:00
// #ifndef APP
2025-07-21 16:47:04 +08:00
@apply border-surface-700 border-solid;
2025-09-04 20:18:18 +08:00
// #endif
2025-07-21 16:47:04 +08:00
2025-08-08 11:28:28 +08:00
&--dark {
border-color: white !important;
border-right-color: transparent !important;
2025-07-21 16:47:04 +08:00
}
// #ifdef H5 || MP
&--spin {
animation: spin 2.5s linear infinite;
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
// #endif
}
</style>