feat: expand square loader set and tune bouncing

This commit is contained in:
JOJO 2026-04-05 01:22:50 +08:00
parent dc60c7a123
commit 3b521e9616
9 changed files with 396 additions and 17 deletions

View File

@ -12,43 +12,53 @@
<style scoped>
.bouncing-squares-loader {
--square-size: 3px;
--square-gap: 10px;
display: grid;
grid-template-columns: repeat(3, var(--square-size));
column-gap: var(--square-gap);
align-items: end;
width: calc(3 * var(--square-size) + 2 * var(--square-gap));
--loader-scale: 0.45;
--square-size: 7.6px; /* 7.6 * 0.45 ≈ 3.42px,略大一点 */
--square-gap: 8.9px; /* 固定间距,缩放后约 4px */
--start-x: 3px;
position: relative;
width: 40px;
height: 20px;
transform: scale(var(--loader-scale));
transform-origin: center;
}
.square {
position: absolute;
width: var(--square-size);
height: var(--square-size);
left: 15%;
background-color: var(--claude-text-tertiary);
transform-origin: center bottom;
animation: bounce-square .5s alternate infinite ease;
transform-origin: 50%;
animation: bounce-square 0.5s alternate infinite ease;
}
@keyframes bounce-square {
0% {
transform: translateY(0) scaleX(1.7) scaleY(0.7);
top: 20px;
height: calc(var(--square-size) * 0.667);
transform: scaleX(1.7);
}
40% {
transform: translateY(-8px) scaleX(1) scaleY(1);
height: var(--square-size);
transform: scaleX(1);
}
100% {
transform: translateY(-17px) scaleX(1) scaleY(1);
top: 0%;
}
}
.square:nth-child(1) {
left: var(--start-x);
}
.square:nth-child(2) {
animation-delay: .2s;
left: calc(var(--start-x) + var(--square-size) + var(--square-gap));
animation-delay: 0.2s;
}
.square:nth-child(3) {
animation-delay: .3s;
left: calc(var(--start-x) + (var(--square-size) + var(--square-gap)) * 2);
animation-delay: 0.3s;
}
</style>

View File

@ -0,0 +1,44 @@
<template>
<div class="chaotic-orbit-loader"></div>
</template>
<script setup lang="ts">
// Based on Uiverse (dovatgabriel), square-styled
</script>
<style scoped>
.chaotic-orbit-loader {
--size: 24px;
--speed: 1.5s;
position: relative;
width: var(--size);
height: var(--size);
display: flex;
align-items: center;
justify-content: center;
animation: rotate calc(var(--speed) * 1.667) linear infinite;
}
.chaotic-orbit-loader::before,
.chaotic-orbit-loader::after {
content: "";
position: absolute;
width: 3px;
height: 3px;
background: var(--claude-text-tertiary);
will-change: transform;
}
.chaotic-orbit-loader::before { animation: orbit var(--speed) linear infinite; }
.chaotic-orbit-loader::after { animation: orbit var(--speed) linear calc(var(--speed) / -2) infinite; }
@keyframes rotate { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
@keyframes orbit {
0% { transform: translate(12px) scale(.74); opacity:.65; }
25% { transform: translate(0) scale(.47); opacity:.3; }
50% { transform: translate(-12px) scale(.74); opacity:.65; }
75% { transform: translate(0) scale(1); opacity:1; }
100% { transform: translate(12px) scale(.74); opacity:.65; }
}
</style>

View File

@ -0,0 +1,70 @@
<template>
<div class="dot-spinner-loader">
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
</div>
</template>
<script setup lang="ts">
// Based on Uiverse (abrahamcalsin), square-styled
</script>
<style scoped>
.dot-spinner-loader {
--size: 22px;
--speed: .9s;
position: relative;
display: flex;
align-items: center;
justify-content: flex-start;
width: var(--size);
height: var(--size);
}
.dot {
position: absolute;
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: flex-start;
width: 100%;
height: 100%;
}
.dot::before {
content: "";
width: 3px;
height: 3px;
background: var(--claude-text-tertiary);
transform: scale(0);
opacity: .5;
animation: pulse calc(var(--speed) * 1.111) ease-in-out infinite;
}
.dot:nth-child(2) { transform: rotate(45deg); }
.dot:nth-child(2)::before { animation-delay: calc(var(--speed) * -0.875); }
.dot:nth-child(3) { transform: rotate(90deg); }
.dot:nth-child(3)::before { animation-delay: calc(var(--speed) * -0.75); }
.dot:nth-child(4) { transform: rotate(135deg); }
.dot:nth-child(4)::before { animation-delay: calc(var(--speed) * -0.625); }
.dot:nth-child(5) { transform: rotate(180deg); }
.dot:nth-child(5)::before { animation-delay: calc(var(--speed) * -0.5); }
.dot:nth-child(6) { transform: rotate(225deg); }
.dot:nth-child(6)::before { animation-delay: calc(var(--speed) * -0.375); }
.dot:nth-child(7) { transform: rotate(270deg); }
.dot:nth-child(7)::before { animation-delay: calc(var(--speed) * -0.25); }
.dot:nth-child(8) { transform: rotate(315deg); }
.dot:nth-child(8)::before { animation-delay: calc(var(--speed) * -0.125); }
@keyframes pulse {
0%, 100% { transform: scale(0); opacity: .5; }
50% { transform: scale(1); opacity: 1; }
}
</style>

View File

@ -0,0 +1,38 @@
<template>
<div class="jumping-seq-loader">
<div class="sq"></div>
<div class="sq"></div>
<div class="sq"></div>
<div class="sq"></div>
<div class="sq"></div>
</div>
</template>
<script setup lang="ts">
// Based on Uiverse (EddyBel), square-styled
</script>
<style scoped>
.jumping-seq-loader {
position: relative;
display: flex;
gap: 5px;
}
.sq {
width: 3px;
height: 3px;
background: var(--claude-text-tertiary);
animation: jump 2s ease infinite;
}
.sq:nth-child(2n) { animation-delay: 300ms; }
.sq:nth-child(3n) { animation-delay: 600ms; }
@keyframes jump {
0%, 100% { transform: translateY(0); }
25% { transform: translateY(-10px) scale(0.66); }
50% { transform: translateY(0); }
75% { transform: translateY(3px) scale(0.9); }
}
</style>

View File

@ -0,0 +1,61 @@
<template>
<div class="leap-frog-loader">
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
</div>
</template>
<script setup lang="ts">
// Based on Uiverse (dovatgabriel), square-styled
</script>
<style scoped>
.leap-frog-loader {
--size: 24px;
--speed: 2s;
position: relative;
width: var(--size);
height: var(--size);
display: flex;
align-items: center;
justify-content: space-between;
}
.dot {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: flex-start;
animation: leap var(--speed) ease infinite;
}
.dot::before {
content: "";
width: 3px;
height: 3px;
background: var(--claude-text-tertiary);
}
.dot:nth-child(2) {
transform: translateX(calc(var(--size) * 0.4));
animation-delay: calc(var(--speed) / -1.5);
}
.dot:nth-child(3) {
transform: translateX(calc(var(--size) * 0.8));
animation-delay: calc(var(--speed) / -3);
}
@keyframes leap {
0% { transform: translateX(0) rotate(0deg); }
33.333% { transform: translateX(0) rotate(180deg); }
66.666% { transform: translateX(calc(var(--size) * -0.4)) rotate(180deg); }
99.999% { transform: translateX(calc(var(--size) * -0.8)) rotate(180deg); }
100% { transform: translateX(0) rotate(0deg); }
}
</style>

View File

@ -0,0 +1,61 @@
<template>
<div class="newton-loader">
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
</div>
</template>
<script setup lang="ts">
// Based on Uiverse (dovatgabriel), square-styled
</script>
<style scoped>
.newton-loader {
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
}
.dot {
position: relative;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
width: 25%;
transform-origin: center top;
}
.dot::after {
content: "";
position: absolute;
top: 75%;
width: 3px;
height: 3px;
background: var(--claude-text-tertiary);
}
.dot:first-child {
animation: swing 1.2s linear infinite;
}
.dot:last-child {
animation: swing2 1.2s linear infinite;
}
@keyframes swing {
0% { transform: rotate(0deg); animation-timing-function: ease-out; }
25% { transform: rotate(70deg); animation-timing-function: ease-in; }
50% { transform: rotate(0deg); animation-timing-function: linear; }
}
@keyframes swing2 {
0% { transform: rotate(0deg); animation-timing-function: linear; }
50% { transform: rotate(0deg); animation-timing-function: ease-out; }
75% { transform: rotate(-70deg); animation-timing-function: ease-in; }
}
</style>

View File

@ -0,0 +1,50 @@
<template>
<div class="orbit-loader">
<div class="orbe" style="--index: 0"></div>
<div class="orbe" style="--index: 1"></div>
<div class="orbe" style="--index: 2"></div>
<div class="orbe" style="--index: 3"></div>
<div class="orbe" style="--index: 4"></div>
</div>
</template>
<script setup lang="ts">
// Based on Uiverse (krlozCJ), square + blur, no glow
</script>
<style scoped>
.orbit-loader {
--size-loader: 24px;
--size-orbe: 3px;
width: var(--size-loader);
height: var(--size-loader);
position: relative;
transform: rotate(45deg);
}
.orbe {
position: absolute;
width: 100%;
height: 100%;
--delay: calc(var(--index) * 0.1s);
opacity: calc(1 - calc(0.18 * var(--index)));
animation: orbit 1.5s var(--delay) ease-in-out infinite;
}
.orbe::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: var(--size-orbe);
height: var(--size-orbe);
background: var(--claude-text-tertiary);
filter: blur(0.35px);
}
@keyframes orbit {
0% {}
80% { transform: rotate(360deg); }
100% { transform: rotate(360deg); }
}
</style>

View File

@ -0,0 +1,31 @@
<template>
<div class="shadow-rolling-loader"></div>
</template>
<script setup lang="ts">
// Based on Uiverse (alexruix), square-styled
</script>
<style scoped>
.shadow-rolling-loader {
--sq: 3px;
width: var(--sq);
height: var(--sq);
position: relative;
left: -10px;
color: var(--claude-text-tertiary);
animation: roll 2s linear infinite;
}
@keyframes roll {
0% { box-shadow: 0 0 transparent, 0 0 transparent, 0 0 transparent, 0 0 transparent; }
12% { box-shadow: 6px 0 currentColor, 0 0 transparent, 0 0 transparent, 0 0 transparent; }
25% { box-shadow: 12px 0 currentColor, 6px 0 currentColor, 0 0 transparent, 0 0 transparent; }
36% { box-shadow: 18px 0 currentColor, 12px 0 currentColor, 6px 0 currentColor, 0 0 transparent; }
50% { box-shadow: 24px 0 currentColor, 18px 0 currentColor, 12px 0 currentColor, 6px 0 currentColor; }
62% { box-shadow: 36px 0 transparent, 24px 0 currentColor, 18px 0 currentColor, 12px 0 currentColor; }
75% { box-shadow: 36px 0 transparent, 36px 0 transparent, 24px 0 currentColor, 18px 0 currentColor; }
87% { box-shadow: 36px 0 transparent, 36px 0 transparent, 36px 0 transparent, 24px 0 currentColor; }
100% { box-shadow: 36px 0 transparent, 36px 0 transparent, 36px 0 transparent, 36px 0 transparent; }
}
</style>

View File

@ -6,6 +6,13 @@ import BouncingSquaresLoader from './BouncingSquaresLoader.vue';
import FiveSquaresLoader from './FiveSquaresLoader.vue';
import NineGridLoader from './NineGridLoader.vue';
import RippleLoader from './RippleLoader.vue';
import NewtonCradleSquaresLoader from './NewtonCradleSquaresLoader.vue';
import DotSpinnerSquaresLoader from './DotSpinnerSquaresLoader.vue';
import OrbitBlurSquaresLoader from './OrbitBlurSquaresLoader.vue';
import ShadowRollingSquaresLoader from './ShadowRollingSquaresLoader.vue';
import JumpingSequenceSquaresLoader from './JumpingSequenceSquaresLoader.vue';
import ChaoticOrbitSquaresLoader from './ChaoticOrbitSquaresLoader.vue';
import LeapFrogSquaresLoader from './LeapFrogSquaresLoader.vue';
// 加载动画池
export const loaderPool: Component[] = [
@ -15,7 +22,14 @@ export const loaderPool: Component[] = [
BouncingSquaresLoader,
FiveSquaresLoader,
NineGridLoader,
RippleLoader
RippleLoader,
NewtonCradleSquaresLoader,
DotSpinnerSquaresLoader,
OrbitBlurSquaresLoader,
ShadowRollingSquaresLoader,
JumpingSequenceSquaresLoader,
ChaoticOrbitSquaresLoader,
LeapFrogSquaresLoader
];
// 至少间隔多少次后才允许重复(例如 4 表示最近 4 次不重复)