兼容 ios
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,6 +9,7 @@ npm-debug.log*
|
||||
# Editor directories and files
|
||||
.project
|
||||
.idea
|
||||
.hbuilderx
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
|
||||
@@ -6,18 +6,18 @@
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('不同类型')">
|
||||
<view class="flex flex-row flex-wrap mb-2">
|
||||
<view class="flex flex-row flex-wrap mb-2 overflow-visible">
|
||||
<cl-button type="primary">{{ t("主要") }}</cl-button>
|
||||
<cl-button type="success">{{ t("成功") }}</cl-button>
|
||||
<cl-button type="warn">{{ t("警告") }}</cl-button>
|
||||
</view>
|
||||
|
||||
<view class="flex flex-row mb-2">
|
||||
<view class="flex flex-row mb-2 overflow-visible">
|
||||
<cl-button type="error">{{ t("危险") }}</cl-button>
|
||||
<cl-button type="info">{{ t("信息") }}</cl-button>
|
||||
</view>
|
||||
|
||||
<view class="flex flex-row">
|
||||
<view class="flex flex-row overflow-visible">
|
||||
<cl-button type="light">{{ t("浅色") }}</cl-button>
|
||||
<cl-button type="dark">{{ t("深色") }}</cl-button>
|
||||
</view>
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3 overflow-visible">
|
||||
<view class="p-3">
|
||||
<demo-item>
|
||||
<cl-text color="info">
|
||||
{{ t("长按项即可拖动排序") }}
|
||||
</cl-text>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('单列排序')">
|
||||
<cl-draggable v-model="list">
|
||||
<template #item="{ item, index }">
|
||||
@@ -16,6 +22,18 @@
|
||||
</cl-draggable>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('不需要长按')">
|
||||
<cl-draggable v-model="list5" :long-press="false">
|
||||
<template #item="{ item }">
|
||||
<view
|
||||
class="flex flex-row items-center p-3 bg-surface-100 rounded-lg mb-2 dark:!bg-surface-700"
|
||||
>
|
||||
<cl-text>{{ (item as UTSJSONObject).label }}</cl-text>
|
||||
</view>
|
||||
</template>
|
||||
</cl-draggable>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('结合列表使用')">
|
||||
<cl-list border>
|
||||
<cl-draggable v-model="list2">
|
||||
@@ -26,8 +44,12 @@
|
||||
arrow
|
||||
:pt="{
|
||||
inner: {
|
||||
className:
|
||||
dragging && dragIndex == index ? '!bg-surface-100' : ''
|
||||
className: parseClass([
|
||||
[
|
||||
dragging && dragIndex == index,
|
||||
isDark ? '!bg-surface-700' : '!bg-surface-100'
|
||||
]
|
||||
])
|
||||
}
|
||||
}"
|
||||
></cl-list-item>
|
||||
@@ -75,41 +97,57 @@
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { ref } from "vue";
|
||||
import { isDark, parseClass } from "@/cool";
|
||||
|
||||
// list:李白《将进酒》
|
||||
const list = ref<UTSJSONObject[]>([
|
||||
{
|
||||
label: "明月几时有,把酒问青天"
|
||||
label: "君不见黄河之水天上来"
|
||||
},
|
||||
{
|
||||
label: "不知天上宫阙,今夕是何年",
|
||||
label: "奔流到海不复回",
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
label: "我欲乘风归去,又恐琼楼玉宇"
|
||||
label: "君不见高堂明镜悲白发"
|
||||
},
|
||||
{
|
||||
label: "高处不胜寒,起舞弄清影"
|
||||
label: "朝如青丝暮成雪"
|
||||
},
|
||||
{
|
||||
label: "何似在人间"
|
||||
label: "人生得意须尽欢"
|
||||
}
|
||||
]);
|
||||
|
||||
// list5:杜甫《春望》
|
||||
const list5 = ref<UTSJSONObject[]>([
|
||||
{
|
||||
label: "国破山河在"
|
||||
},
|
||||
{
|
||||
label: "城春草木深"
|
||||
},
|
||||
{
|
||||
label: "感时花溅泪"
|
||||
}
|
||||
]);
|
||||
|
||||
// list2:王之涣《登鹳雀楼》
|
||||
const list2 = ref<UTSJSONObject[]>([
|
||||
{
|
||||
label: "明月几时有,把酒问青天"
|
||||
label: "白日依山尽"
|
||||
},
|
||||
{
|
||||
label: "不知天上宫阙,今夕是何年"
|
||||
label: "黄河入海流"
|
||||
},
|
||||
{
|
||||
label: "我欲乘风归去,又恐琼楼玉宇"
|
||||
label: "欲穷千里目"
|
||||
},
|
||||
{
|
||||
label: "高处不胜寒,起舞弄清影"
|
||||
label: "更上一层楼"
|
||||
},
|
||||
{
|
||||
label: "何似在人间"
|
||||
label: "一览众山小"
|
||||
}
|
||||
]);
|
||||
|
||||
|
||||
@@ -19,7 +19,11 @@
|
||||
|
||||
<template #bottom>
|
||||
<view class="py-3">
|
||||
<cl-loadmore :loading="loading" v-if="list.length > 0"></cl-loadmore>
|
||||
<cl-loadmore
|
||||
v-if="list.length > 0"
|
||||
:loading="loading"
|
||||
safe-area-bottom
|
||||
></cl-loadmore>
|
||||
</view>
|
||||
</template>
|
||||
</cl-list-view>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
</template>
|
||||
</cl-waterfall>
|
||||
|
||||
<cl-loadmore :loading="true"></cl-loadmore>
|
||||
<cl-loadmore :loading="true" safe-area-bottom></cl-loadmore>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
20
types/uni-app.d.ts
vendored
20
types/uni-app.d.ts
vendored
@@ -446,6 +446,26 @@ declare interface UniElement {
|
||||
success?: (res: { tempFilePath: string }) => void;
|
||||
fail?: (err: { errCode: number; errMsg: string }) => void;
|
||||
}): void;
|
||||
getDrawableContext(): DrawableContext;
|
||||
animate(
|
||||
keyframes: UniAnimationKeyframe | UniAnimationKeyframe[],
|
||||
options?:
|
||||
| {
|
||||
delay?: number;
|
||||
direction?: "normal" | "reverse" | "alternate" | "alternate-reverse";
|
||||
duration?: number;
|
||||
easing?:
|
||||
| "ease"
|
||||
| "ease-in"
|
||||
| "ease-out"
|
||||
| "ease-in-out"
|
||||
| "linear"
|
||||
| "cubic-bezier";
|
||||
fill?: "backwards" | "forwards" | "both" | "none";
|
||||
iterations?: number;
|
||||
}
|
||||
| number
|
||||
): { id: string; playState: "running" | "paused" | "finished" | "idle" } | null;
|
||||
}
|
||||
|
||||
declare interface CanvasContext extends HTMLCanvasElement {
|
||||
|
||||
5
uni_modules/cool-svg/utssdk/app-harmony/config.json
Normal file
5
uni_modules/cool-svg/utssdk/app-harmony/config.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@ohos/svg": "2.2.1"
|
||||
}
|
||||
}
|
||||
5
uni_modules/cool-svg/utssdk/app-harmony/index.uts
Normal file
5
uni_modules/cool-svg/utssdk/app-harmony/index.uts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { BuilderNode } from "@kit.ArkUI";
|
||||
|
||||
export class CoolSvg {
|
||||
load(src: string, color: string) {}
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
>
|
||||
<!-- 图片容器 - 可拖拽和缩放的图片区域 -->
|
||||
<view class="cl-cropper__image">
|
||||
<!-- @vue-ignore -->
|
||||
<image
|
||||
class="cl-cropper__image-inner"
|
||||
:class="[
|
||||
|
||||
@@ -3,28 +3,35 @@
|
||||
class="cl-draggable"
|
||||
:class="[
|
||||
{
|
||||
'cl-draggable--grid': props.columns > 1
|
||||
'cl-draggable--columns': props.columns > 1
|
||||
},
|
||||
pt.className
|
||||
]"
|
||||
>
|
||||
<!-- @vue-ignore -->
|
||||
<view
|
||||
v-for="(item, index) in list"
|
||||
:key="getItemKey(item, index)"
|
||||
class="cl-draggable__item"
|
||||
:class="[
|
||||
{
|
||||
'cl-draggable__item--disabled': disabled
|
||||
},
|
||||
dragging && dragIndex == index ? `opacity-80 ${pt.ghost?.className}` : ''
|
||||
'cl-draggable__item--disabled': disabled,
|
||||
'cl-draggable__item--dragging': dragging && dragIndex == index,
|
||||
'cl-draggable__item--animating': dragging && dragIndex != index
|
||||
}
|
||||
]"
|
||||
:style="getItemStyle(index)"
|
||||
@touchstart="
|
||||
(event: UniTouchEvent) => {
|
||||
onTouchStart(event, index);
|
||||
onTouchStart(event, index, 'touch');
|
||||
}
|
||||
"
|
||||
@touchmove.stop.prevent="onTouchMove"
|
||||
@longpress="
|
||||
(event: UniTouchEvent) => {
|
||||
onTouchStart(event, index, 'longpress');
|
||||
}
|
||||
"
|
||||
@touchmove="onTouchMove"
|
||||
@touchend="onTouchEnd"
|
||||
>
|
||||
<slot
|
||||
@@ -44,6 +51,7 @@
|
||||
import { computed, ref, getCurrentInstance, type PropType, watch } from "vue";
|
||||
import { isNull, parsePt, uuid } from "@/cool";
|
||||
import type { PassThroughProps } from "../../types";
|
||||
import { vibrate } from "@/uni_modules/cool-vibrate";
|
||||
|
||||
defineOptions({
|
||||
name: "cl-draggable"
|
||||
@@ -89,15 +97,15 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/** 动画持续时间(毫秒) */
|
||||
animation: {
|
||||
type: Number,
|
||||
default: 150
|
||||
},
|
||||
/** 列数:1为单列纵向布局,>1为多列网格布局 */
|
||||
columns: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
// 是否需要长按触发
|
||||
longPress: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
});
|
||||
|
||||
@@ -376,12 +384,6 @@ function getItemStyle(index: number) {
|
||||
return style;
|
||||
}
|
||||
|
||||
// 为非拖拽元素添加过渡动画
|
||||
if (props.animation > 0 && !isCurrent) {
|
||||
style["transition-property"] = "transform";
|
||||
style["transition-duration"] = `${props.animation}ms`;
|
||||
}
|
||||
|
||||
// 拖拽状态下的样式处理
|
||||
if (dragging.value) {
|
||||
if (isCurrent) {
|
||||
@@ -391,11 +393,9 @@ function getItemStyle(index: number) {
|
||||
} else {
|
||||
// 其他元素:显示排序预览位移
|
||||
const translateOffset = getItemTranslateOffset(index);
|
||||
if (translateOffset.x != 0 || translateOffset.y != 0) {
|
||||
style["transform"] = `translate(${translateOffset.x}px, ${translateOffset.y}px)`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
@@ -513,22 +513,31 @@ function checkMovedToOtherElement(): boolean {
|
||||
* @param event 触摸事件对象
|
||||
* @param index 触摸的项目索引
|
||||
*/
|
||||
async function onTouchStart(event: UniTouchEvent, index: number): Promise<void> {
|
||||
async function onTouchStart(event: UniTouchEvent, index: number, type: string) {
|
||||
// 如果是长按触发,但未开启长按功能,则直接返回
|
||||
if (type == "longpress" && !props.longPress) return;
|
||||
// 如果是普通触摸触发,但已开启长按功能,则直接返回
|
||||
if (type == "touch" && props.longPress) return;
|
||||
|
||||
// 检查是否禁用或索引无效
|
||||
if (props.disabled) return;
|
||||
if (getItemDisabled(index)) return;
|
||||
if (index < 0 || index >= list.value.length) return;
|
||||
|
||||
// 获取触摸点
|
||||
const touch = event.touches[0];
|
||||
|
||||
// 初始化拖拽状态
|
||||
dragging.value = true;
|
||||
|
||||
// 初始化拖拽索引
|
||||
dragIndex.value = index;
|
||||
insertIndex.value = index; // 初始插入位置为原位置
|
||||
startX.value = touch.clientX;
|
||||
startY.value = touch.clientY;
|
||||
offsetX.value = 0;
|
||||
offsetY.value = 0;
|
||||
// 初始化拖拽数据项
|
||||
dragItem.value = list.value[index];
|
||||
|
||||
// 先获取所有项目的位置信息,为后续计算做准备
|
||||
@@ -536,6 +545,14 @@ async function onTouchStart(event: UniTouchEvent, index: number): Promise<void>
|
||||
|
||||
// 触发开始事件
|
||||
emit("start", index);
|
||||
|
||||
// 震动
|
||||
vibrate(1);
|
||||
|
||||
// 阻止事件冒泡
|
||||
event.stopPropagation();
|
||||
// 阻止默认行为
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -575,7 +592,7 @@ function onTouchMove(event: TouchEvent): void {
|
||||
}
|
||||
}
|
||||
|
||||
// 阻止默认行为和事件冒泡
|
||||
// 阻止默认行为
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
@@ -585,7 +602,10 @@ function onTouchMove(event: TouchEvent): void {
|
||||
function onTouchEnd(): void {
|
||||
if (!dragging.value) return;
|
||||
|
||||
// 旧索引
|
||||
const oldIndex = dragIndex.value;
|
||||
|
||||
// 新索引
|
||||
const newIndex = insertIndex.value;
|
||||
|
||||
// 如果位置发生变化,立即更新数组
|
||||
@@ -604,15 +624,11 @@ function onTouchEnd(): void {
|
||||
dropping.value = true;
|
||||
dragging.value = false;
|
||||
|
||||
// 让拖拽元素回到自然位置(偏移归零)
|
||||
offsetX.value = 0;
|
||||
offsetY.value = 0;
|
||||
// 重置所有状态
|
||||
reset();
|
||||
|
||||
// 等待放下动画完成后重置所有状态
|
||||
setTimeout(() => {
|
||||
emit("end", newIndex >= 0 ? newIndex : oldIndex);
|
||||
reset();
|
||||
}, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -652,21 +668,30 @@ watch(
|
||||
<style lang="scss" scoped>
|
||||
.cl-draggable {
|
||||
@apply flex-col relative overflow-visible;
|
||||
}
|
||||
|
||||
.cl-draggable--grid {
|
||||
&--columns {
|
||||
@apply flex-row flex-wrap;
|
||||
}
|
||||
|
||||
.cl-draggable__item {
|
||||
@apply relative;
|
||||
&__item {
|
||||
@apply relative z-10;
|
||||
|
||||
// #ifdef APP-IOS
|
||||
@apply transition-none opacity-100;
|
||||
// #endif
|
||||
|
||||
&--dragging {
|
||||
@apply opacity-80 z-20;
|
||||
}
|
||||
|
||||
.cl-draggable__item--dragging {
|
||||
@apply opacity-80;
|
||||
}
|
||||
|
||||
.cl-draggable__item--disabled {
|
||||
&--disabled {
|
||||
@apply opacity-60;
|
||||
}
|
||||
|
||||
&--animating {
|
||||
@apply duration-200;
|
||||
transition-property: transform;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -22,8 +22,6 @@
|
||||
:hold-keyboard="false"
|
||||
:clearable="false"
|
||||
@change="onChange"
|
||||
@focus="animateCursor(true)"
|
||||
@blur="animateCursor(true)"
|
||||
></cl-input>
|
||||
</view>
|
||||
|
||||
@@ -36,12 +34,13 @@
|
||||
{
|
||||
'is-disabled': disabled,
|
||||
'is-dark': isDark,
|
||||
'is-active': value.length == index && isFocus
|
||||
'is-active': value.length >= index && isFocus
|
||||
},
|
||||
pt.item?.className
|
||||
]"
|
||||
>
|
||||
<cl-text
|
||||
:color="value.length >= index && isFocus ? 'primary' : ''"
|
||||
:pt="{
|
||||
className: pt.value?.className
|
||||
}"
|
||||
@@ -50,9 +49,6 @@
|
||||
<view
|
||||
class="cl-input-otp__cursor"
|
||||
:class="[pt.cursor?.className]"
|
||||
:style="{
|
||||
opacity: cursorOpacity
|
||||
}"
|
||||
v-if="value.length == index && isFocus && item == ''"
|
||||
></view>
|
||||
</view>
|
||||
@@ -184,51 +180,6 @@ function onChange(val: string) {
|
||||
emit("done", val);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 光标闪烁透明度值
|
||||
* 范围: 0.3-1.0
|
||||
*/
|
||||
const cursorOpacity = ref(0.3);
|
||||
|
||||
/**
|
||||
* 光标闪烁动画帧ID
|
||||
*/
|
||||
let cursorAnimationId = 0;
|
||||
|
||||
/**
|
||||
* 控制光标闪烁动画
|
||||
* @param isIncreasing 透明度是否递增
|
||||
*/
|
||||
function animateCursor(isIncreasing: boolean) {
|
||||
// #ifdef APP
|
||||
// 未获得焦点时不执行动画
|
||||
if (!isFocus.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 取消上一次动画
|
||||
if (cursorAnimationId != 0) {
|
||||
cancelAnimationFrame(cursorAnimationId);
|
||||
cursorAnimationId = 0;
|
||||
}
|
||||
|
||||
// 执行动画帧
|
||||
cursorAnimationId = requestAnimationFrame(() => {
|
||||
// 根据方向调整透明度值
|
||||
cursorOpacity.value += isIncreasing ? 0.01 : -0.01;
|
||||
|
||||
// 到达边界值时改变方向
|
||||
if (cursorOpacity.value > 1) {
|
||||
animateCursor(false);
|
||||
} else if (cursorOpacity.value <= 0.3) {
|
||||
animateCursor(true);
|
||||
} else {
|
||||
animateCursor(isIncreasing);
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -248,7 +199,7 @@ function animateCursor(isIncreasing: boolean) {
|
||||
}
|
||||
|
||||
&__item {
|
||||
@apply flex flex-row items-center justify-center;
|
||||
@apply flex flex-row items-center justify-center duration-100;
|
||||
@apply border border-solid border-surface-200 rounded-lg bg-white;
|
||||
height: 80rpx;
|
||||
width: 80rpx;
|
||||
@@ -272,10 +223,10 @@ function animateCursor(isIncreasing: boolean) {
|
||||
}
|
||||
|
||||
&__cursor {
|
||||
@apply absolute;
|
||||
@apply absolute duration-100;
|
||||
@apply bg-primary-500;
|
||||
width: 2rpx;
|
||||
height: 36rpx;
|
||||
height: 24rpx;
|
||||
}
|
||||
|
||||
// #ifdef H5 || MP
|
||||
@@ -285,7 +236,7 @@ function animateCursor(isIncreasing: boolean) {
|
||||
|
||||
@keyframes flash {
|
||||
0% {
|
||||
opacity: 0.3;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
50% {
|
||||
@@ -293,7 +244,7 @@ function animateCursor(isIncreasing: boolean) {
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0.3;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
|
||||
@@ -1,33 +1,25 @@
|
||||
<template>
|
||||
<view
|
||||
ref="loadingRef"
|
||||
class="cl-loading"
|
||||
:class="[
|
||||
{
|
||||
'cl-loading--dark': isDark && color == '',
|
||||
'cl-loading--spin': loading,
|
||||
'!border-primary-500': color == 'primary',
|
||||
'!border-green-500': color == 'success',
|
||||
'!border-yellow-500': color == 'warn',
|
||||
'!border-red-500': color == 'error',
|
||||
'!border-surface-500': color == 'info',
|
||||
'!border-surface-700': color == 'dark',
|
||||
'!border-white': color == 'light',
|
||||
'!border-surface-300': color == 'disabled',
|
||||
'!border-r-transparent': true
|
||||
},
|
||||
pt.className
|
||||
]"
|
||||
:style="{
|
||||
// #ifdef APP
|
||||
transform: `rotate(${rotate}deg)`,
|
||||
// #endif
|
||||
height: getRpx(size!),
|
||||
width: getRpx(size!),
|
||||
borderWidth: getRpx(2),
|
||||
height: getPx(size!),
|
||||
width: getPx(size!),
|
||||
// #ifndef APP
|
||||
borderWidth: '1px',
|
||||
borderTopColor: color,
|
||||
borderRightColor: 'transparent',
|
||||
borderBottomColor: color,
|
||||
borderLeftColor: color
|
||||
// #endif
|
||||
}"
|
||||
v-if="loading"
|
||||
>
|
||||
@@ -35,8 +27,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
import { isDark, parsePt } from "@/cool";
|
||||
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";
|
||||
|
||||
@@ -68,7 +60,7 @@ const props = defineProps({
|
||||
}
|
||||
});
|
||||
|
||||
const { getRpx } = useSize();
|
||||
const { getPxValue, getPx } = useSize();
|
||||
|
||||
// 透传样式类型定义
|
||||
type PassThrough = {
|
||||
@@ -79,25 +71,113 @@ type PassThrough = {
|
||||
// 解析透传样式
|
||||
const pt = computed(() => parsePt<PassThrough>(props.pt));
|
||||
|
||||
// 旋转角度
|
||||
const rotate = ref(0);
|
||||
// 组件引用
|
||||
const loadingRef = shallowRef<UniElement | null>(null);
|
||||
|
||||
// 开始旋转动画
|
||||
function start() {
|
||||
requestAnimationFrame(() => {
|
||||
// 增加旋转角度
|
||||
rotate.value += 1;
|
||||
const color = computed<string>(() => {
|
||||
if (props.color == "") {
|
||||
return isDark.value ? "#ffffff" : (ctx.color["surface-700"] as string);
|
||||
}
|
||||
|
||||
// 如果仍在加载中则继续旋转
|
||||
if (props.loading) {
|
||||
start();
|
||||
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-UVUE
|
||||
// #ifdef APP
|
||||
watch(
|
||||
computed(() => props.loading),
|
||||
(val: boolean) => {
|
||||
@@ -110,6 +190,13 @@ onMounted(() => {
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
computed(() => [props.color, props.size, isDark.value]),
|
||||
() => {
|
||||
drawLoading();
|
||||
}
|
||||
);
|
||||
// #endif
|
||||
});
|
||||
</script>
|
||||
@@ -117,7 +204,10 @@ onMounted(() => {
|
||||
<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;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<view class="cl-loadmore-wrapper">
|
||||
<view class="cl-loadmore">
|
||||
<cl-loading
|
||||
:size="30"
|
||||
:size="28"
|
||||
:pt="{
|
||||
className: `mr-2 ${pt.icon?.className}`
|
||||
}"
|
||||
@@ -59,7 +59,7 @@ const props = defineProps({
|
||||
// 是否显示底部安全区
|
||||
safeAreaBottom: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -384,7 +384,7 @@ function onTouchEnd() {
|
||||
angle: currentAngle.value
|
||||
});
|
||||
}
|
||||
vibrate(2); // 震动反馈
|
||||
vibrate(1); // 震动反馈
|
||||
}
|
||||
|
||||
// 监听模式变化,重新初始化
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { computed, type ComputedRef } from "vue";
|
||||
import { config } from "../config";
|
||||
import { rpx2px } from "@/cool";
|
||||
|
||||
/**
|
||||
* 字号管理类
|
||||
@@ -73,6 +74,35 @@ class Size {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取px值
|
||||
* @param val - 需要转换的值 10、10rpx、10px
|
||||
* @returns 转换后的px值
|
||||
*/
|
||||
getPxValue = (val: number | string) => {
|
||||
const scale = this.getScale();
|
||||
|
||||
if (typeof val == "string") {
|
||||
const num = parseFloat(val);
|
||||
const unit = val.replace(`${num}`, "");
|
||||
|
||||
if (unit == "px") {
|
||||
return num * scale;
|
||||
} else {
|
||||
return rpx2px(num * scale);
|
||||
}
|
||||
} else {
|
||||
return rpx2px(val * scale);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取px值
|
||||
*/
|
||||
getPx = (val: number | string) => {
|
||||
return this.getPxValue(val) + "px";
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取当前字号在预设中的索引
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user