Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/arcodesign-vue/components/components.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import badgeDefault from './badge';
import cellDefault from './cell';
import contextProviderDefault from './context-provider';
import loadingDefault from './loading';
import notifyDefault from './notify';
import transitionDefault from './transition';

export * from './badge';
export * from './cell';
export * from './context-provider';
export * from './loading';
export * from './notify';
export * from './transition';

export const allCompInstall = [
badgeDefault.install,
cellDefault.install,
contextProviderDefault.install,
loadingDefault.install,
notifyDefault.install,
transitionDefault.install,
];
258 changes: 258 additions & 0 deletions packages/arcodesign-vue/components/loading/Loading.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
<!-- Note: Generated by AI, needs verification -->
<script lang="ts" setup>
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
import { isOneOf } from '@arco-design/mobile-utils';
import { getPrefixCls } from '../context-provider';
import { LoadingProps } from './type';

defineOptions({
name: 'Loading',
});

const props = withDefaults(defineProps<LoadingProps>(), {
type: 'dot',
duration: 1000,
radius: 9,
stroke: 2,
filleted: true,
svgKey: '',
});

const prefix = getPrefixCls('loading');
const statusList = ref<number[]>([]);
const timerId = ref<number>(-1);

// 计算属性
const halfCircle = computed(() => Math.PI * props.radius);
const circlePos = computed(() => 0.5 * props.stroke + props.radius);
const circleSize = computed(() => props.radius * 2 + props.stroke);
const actualSvgKey = computed(() => props.svgKey || `${Date.now()}-${Math.random()}`);

// 获取样式与浏览器前缀
function getStyleWithVendor(style: Record<string, any>) {
Copy link

Copilot AI Jun 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getStyleWithVendor function currently just returns the input style. Either implement vendor-prefix logic here or remove this stub to simplify the code.

Copilot uses AI. Check for mistakes.
return style;
}

// 动画逻辑
function startAnimation() {
if (props.type === 'dot' && statusList.value.length) {
const interval = props.duration / statusList.value.length;
timerId.value = window.setInterval(() => {
const newList = [...statusList.value];
const item = newList.pop();
if (item !== undefined) {
newList.unshift(item);
}
statusList.value = newList;
}, interval);
}
}

function stopAnimation() {
if (timerId.value >= 0) {
clearInterval(timerId.value);
timerId.value = -1;
}
}

// 初始化状态列表
function initStatusList() {
let newList: number[];
if (props.list && props.list.length) {
newList = props.list;
} else {
switch (props.type) {
case 'spin':
newList = [1, 0.1, 0.2286, 0.3572, 0.4858, 0.6144, 0.743, 0.8716];
break;
case 'dot':
newList = [0.2, 0.6, 1];
break;
default:
newList = [];
break;
}
}
statusList.value = newList;
}

// 渲染函数
const renderSpin = () => {
const len = statusList.value.length;
return statusList.value.map((opacity, index) => ({
opacity,
transform: `rotate(${index / len}turn)`,
width: `${props.stroke}px`,
backgroundColor: props.color,
borderRadius: props.filleted ? `${props.stroke}px` : undefined,
}));
};

const renderDot = () => {
return statusList.value.map(opacity => ({
opacity,
backgroundColor: props.color,
}));
};

// 计算样式
const loadingStyle = computed(() => {
const circleStyle = isOneOf(props.type, ['circle', 'arc'])
? {
width: `${circleSize.value}px`,
height: `${circleSize.value}px`,
}
: {};
return getStyleWithVendor({
animationDuration: `${props.duration}ms`,
...circleStyle,
...(props.style || {}),
});
});

// 生命周期
onMounted(() => {
initStatusList();
startAnimation();
});

onUnmounted(() => {
stopAnimation();
});

// 监听器
watch([() => props.list, () => props.type], () => {
stopAnimation();
initStatusList();
startAnimation();
});

watch([() => props.type, () => statusList.value, () => props.duration], () => {
stopAnimation();
startAnimation();
});
Comment on lines +124 to +133
Copy link

Copilot AI Jun 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two separate watchers restarting the animation; these can trigger redundant intervals. Consider combining them into one watcher or extracting a single restart handler.

Suggested change
watch([() => props.list, () => props.type], () => {
stopAnimation();
initStatusList();
startAnimation();
});
watch([() => props.type, () => statusList.value, () => props.duration], () => {
stopAnimation();
startAnimation();
});
watch([() => props.list, () => props.type, () => statusList.value, () => props.duration], () => {
stopAnimation();
initStatusList();
startAnimation();
});

Copilot uses AI. Check for mistakes.
</script>

<template>
<div :class="[`${prefix}`, 'all-border-box', props.type, props.class]" :style="loadingStyle">
<!-- Spin 类型 -->
<template v-if="props.type === 'spin'">
<span
v-for="(item, index) in renderSpin()"
:key="index"
class="spin-cell"
:style="{
opacity: item.opacity,
transform: item.transform,
width: item.width,
}"
>
<span
class="spin-cell-inner bg-color-with-config"
:style="{
backgroundColor: item.backgroundColor,
borderRadius: item.borderRadius,
}"
/>
</span>
</template>

<!-- Dot 类型 -->
<template v-if="props.type === 'dot'">
<span
v-for="(item, index) in renderDot()"
:key="index"
:class="['dot-cell', 'bg-color-with-config', { filleted: props.filleted }]"
:style="{
opacity: item.opacity,
backgroundColor: item.backgroundColor,
}"
/>
</template>

<!-- Circle 类型 -->
<template v-if="props.type === 'circle'">
<svg :viewBox="`0 0 ${circleSize} ${circleSize}`">
<defs>
<linearGradient :id="`grad1-${actualSvgKey}`" x1="0%" y1="0%" x2="100%" y2="0%">
<stop
offset="0%"
class="loading-circle-middle stop-color-with-config"
:style="{ stopColor: props.color }"
/>
<stop
offset="100%"
class="loading-circle-start stop-color-with-config"
:style="{ stopColor: props.color }"
/>
</linearGradient>
<linearGradient :id="`grad2-${actualSvgKey}`" x1="0%" y1="0%" x2="100%" y2="0%">
<stop
offset="0%"
class="loading-circle-middle stop-color-with-config"
:style="{ stopColor: props.color }"
/>
<stop
offset="100%"
class="loading-circle-end stop-color-with-config"
:style="{ stopColor: props.color }"
/>
</linearGradient>
</defs>
<circle
:cx="circlePos"
:cy="circlePos"
:r="props.radius"
:stroke="`url(#grad1-${actualSvgKey})`"
:stroke-width="props.stroke"
:stroke-dasharray="halfCircle"
:stroke-dashoffset="halfCircle"
fill="none"
/>
<circle
:cx="circlePos"
:cy="circlePos"
:r="props.radius"
:stroke="`url(#grad2-${actualSvgKey})`"
:stroke-width="props.stroke"
:stroke-dasharray="halfCircle"
fill="none"
/>
<circle
v-if="props.filleted"
:cx="circlePos * 2 - props.stroke / 2"
:cy="circlePos"
:r="props.stroke / 2"
class="loading-circle-filleted fill-color-with-config"
:style="{ fill: props.color }"
/>
</svg>
</template>

<!-- Arc 类型 -->
<template v-if="props.type === 'arc'">
<svg :viewBox="`0 0 ${circleSize} ${circleSize}`">
<circle
class="arc-bg"
:cx="circlePos"
:cy="circlePos"
:r="props.radius"
:stroke-width="props.stroke"
fill="none"
/>
<circle
class="arc-line stroke-color-with-config"
:cx="circlePos"
:cy="circlePos"
:r="props.radius"
:style="{ stroke: props.color }"
:stroke-width="props.stroke"
:stroke-dashoffset="halfCircle * 0.5"
:stroke-dasharray="`${halfCircle * 0.5} ${halfCircle * 1.5}`"
fill="none"
:stroke-linecap="props.filleted ? 'round' : undefined"
/>
</svg>
</template>
</div>
</template>
36 changes: 36 additions & 0 deletions packages/arcodesign-vue/components/loading/README.en-US.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!-- Note: Generated by AI, needs verification -->

### Feedback

# Loading

Loading component, divided into four types, `circle` is a ring, `arc` is an arc `spin` is a rotation, and `dot` is a dot. All types can be customized in color, ring and arc types can be customized with coil radius and thickness, and rotation and dot types can be customized with internal element transparency.

======

> Props

| Property | Description | Type | DefaultValue |
| -------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ------------ |
| style | Custom stylesheet | CSSProperties | - |
| class | Custom classname | string | - |
| color | The main color, if you want to use css to control the main color, you can use the public mixin \`\.set\-loading\-color(@color)\` | string | - |
| type | Loading type | "spin" \| "circle" \| "arc" \| "dot" | "dot" |
| list | Valid when the type is \`dot\` or \`spin\`, defines the transparency of each element inside | number\[\] | - |
| duration | A loading cycle in millisecond | number | 1000 |
| svgKey | Distinguish the \`\<def\>\` content of different svg | string | - |
| radius | Circle radius, available when type is \`circle\` or \`arc\` | number | 9 |
| stroke | Circle stroke width, available when type is \`circle\` or \`arc\` or \`spin\` | number | 2 |
| filleted | Whether the edges are rounded | boolean | true |

> Refs

| Property | Description | Type |
| -------- | ------------------------- | -------------- |
| dom | The outermost element DOM | HTMLDivElement |

> LoadingType

```
"spin" | "circle" | "arc" | "dot"
```
36 changes: 36 additions & 0 deletions packages/arcodesign-vue/components/loading/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!-- Note: Generated by AI, needs verification -->

### 反馈

# 加载 Loading

加载中组件,分为四种类型,`circle`为环形,`arc`为弧线,`spin`为旋转,`dot`为圆点。所有类型均可定制颜色,环形和弧线类型可定制线圈半径及粗细,旋转和圆点类型可定制内部元素透明度。

======

> 属性/Props

| 参数 | 描述 | 类型 | 默认值 |
| -------- | ------------------------------------------------------------------------------------- | ------------------------------------ | ------ |
| style | 自定义样式 | CSSProperties | - |
| class | 自定义类名 | string | - |
| color | 主颜色,如果想使用 css 控制主颜色,可使用公共 mixin \`\.set\-loading\-color(@color)\` | string | - |
| type | loading 类型 | "spin" \| "circle" \| "arc" \| "dot" | "dot" |
| list | 当类型为\`dot\`或\`spin\`时有效,定义内部各元素的透明度 | number\[\] | - |
| duration | 一次 loading 周期的毫秒数 | number | 1000 |
| svgKey | 区分不同 loading 组件间的\`\<def\>\`内容 | string | - |
| radius | 圆圈半径,类型为\`circle\`或\`arc\`时可用 | number | 9 |
| stroke | 圆圈描边宽度,类型为\`circle\`或\`arc\`或\`spin\`时可用 | number | 2 |
| filleted | 边缘是否为圆角 | boolean | true |

> 引用/Refs

| 参数 | 描述 | 类型 |
| ---- | -------------- | -------------- |
| dom | 最外层元素 DOM | HTMLDivElement |

> LoadingType

```
"spin" | "circle" | "arc" | "dot"
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!-- Note: Generated by AI, needs verification -->

## 自定义加载颜色/透明度 @en{Custom loading color/transparent}

#### 3

旋转和圆点类型可定制内部元素透明度。@en{Inner element transparency of spin and dot types can be customized.}

```vue
<template>
<div class="loading-demo-basic">
<arco-loading type="arc" color="#ff5722" />
<arco-loading type="circle" color="#ff5722" />
<arco-loading type="spin" color="#ff5722" :list="[1, 0, 0.1, 0.25, 0.4, 0.55, 0.7, 0.85]" />
<arco-loading type="dot" color="#606a78" :list="[0.1, 0.3, 0.5]" />
</div>
</template>
```

```less
.loading-demo-basic {
.@{prefix}-loading {
.rem(margin-right, 20);
vertical-align: middle;
}
}
```
Loading