You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
378 lines
9.0 KiB
378 lines
9.0 KiB
<template> |
|
<div class="hd-loading" v-show="visiable" @touchmove.stop.prevent="preventTouchMove" @click="click"> |
|
<view :class="['loading', innerBackground ? '' : 'loading--white']"> |
|
<view :class="['loading-icon', innerType === 'dot' ? '' : 'rotate']"> |
|
<!-- 如果为圆点形状只需显示三个点,花瓣形状显示12个花瓣 --> |
|
<view :class="['loading-icon-item-' + innerType]" v-for="(n, index) in innerType === 'dot' ? 3 : 12" :key="index"></view> |
|
</view> |
|
<text v-if="innerTitle && innerBackground" class="title">{{ innerTitle }}</text> |
|
</view> |
|
</div> |
|
</template> |
|
|
|
<script setup lang="ts"> |
|
import { inject, nextTick, ref, watch } from 'vue' |
|
import { defaultLoadingOptions, loadingDefaultKey, loadingDefaultOptionKey, RegUtil } from '../../index' |
|
import { LoadingOptions, LoadingType } from './types' |
|
// Loading 加载 |
|
|
|
interface Props { |
|
// 是否展示 |
|
modelValue?: boolean |
|
/** |
|
* 提示的内容 |
|
*/ |
|
title?: string |
|
/** |
|
* 动画类型 |
|
*/ |
|
type?: LoadingType |
|
/** |
|
* 是否显示黑色底色,默认:true |
|
*/ |
|
background?: boolean |
|
/** |
|
* 延时展示时间,默认0,单位毫秒 |
|
*/ |
|
delayTime?: number |
|
/** |
|
* loading唯一标识 |
|
*/ |
|
id?: string |
|
} |
|
|
|
const props = withDefaults(defineProps<Props>(), { |
|
// 是否展示 |
|
modelValue: false, |
|
/** |
|
* 动画类型 |
|
*/ |
|
type: 'flower', |
|
/** |
|
* 是否显示黑色底色,默认:true |
|
*/ |
|
background: true, |
|
/** |
|
* 延时展示时间,默认0,单位毫秒 |
|
*/ |
|
delayTime: 0, |
|
/** |
|
* loading唯一标识 |
|
*/ |
|
id: '' |
|
}) |
|
|
|
const acitve = ref<boolean>(false) // loading是否激活 |
|
const visiable = ref<boolean>(false) // loading是否展示 |
|
// eslint-disable-next-line @typescript-eslint/ban-types |
|
const onOpened = ref<Function | null>(null) // 打开时触发 |
|
// eslint-disable-next-line @typescript-eslint/ban-types |
|
const onClose = ref<Function | null>(null) // 关闭时触发 |
|
// eslint-disable-next-line @typescript-eslint/ban-types |
|
const onClick = ref<Function | null>(null) // 点击时触发 |
|
const innerTitle = ref<string>('') // loading提示内容 |
|
const innerType = ref<LoadingType>('flower') // loading动画类型 |
|
const innerBackground = ref<boolean>(true) // loading是否显示黑色底色,默认:true |
|
const innerdelayTime = ref<number>(0) // loading延时展示时间 |
|
|
|
const loadingKey = props.id ? '__LOADING__' + props.id : loadingDefaultKey |
|
const loadingOptionKey = props.id ? '__LOADING_OPTION__' + props.id : loadingDefaultOptionKey |
|
const loadingShow = inject(loadingKey) || ref<boolean>(false) // 是否展示loading组件 |
|
const loadingOption = inject(loadingOptionKey) || ref<LoadingOptions>(defaultLoadingOptions) // loading选项 |
|
|
|
// 监听options变化展示 |
|
watch( |
|
() => loadingOption.value, |
|
(newVal: LoadingOptions) => { |
|
reset(newVal) |
|
} |
|
) |
|
|
|
// 监听函数式调用是否打开 |
|
watch( |
|
() => loadingShow.value, |
|
(newVal) => { |
|
if (newVal) { |
|
show() |
|
} else { |
|
hide() |
|
} |
|
} |
|
) |
|
|
|
watch( |
|
() => props.modelValue, |
|
(newVal) => { |
|
if (newVal) { |
|
reset({}) |
|
show() |
|
} else { |
|
hide() |
|
} |
|
} |
|
) |
|
|
|
const emit = defineEmits(['update:modelValue']) |
|
|
|
/** |
|
* 阻止tounch默认事件 |
|
*/ |
|
function preventTouchMove() { |
|
return |
|
} |
|
// 打开 |
|
function show() { |
|
acitve.value = true |
|
if (innerdelayTime.value) { |
|
const timer = setTimeout(() => { |
|
if (acitve.value) { |
|
visiable.value = true |
|
change(visiable.value) |
|
} |
|
clearTimeout(timer) |
|
}, innerdelayTime.value) |
|
} else { |
|
visiable.value = true |
|
change(visiable.value) |
|
} |
|
} |
|
function change(show) { |
|
if (show) { |
|
/** |
|
* 消息展示状态变更时触发 |
|
* @arg value:Boolean 消息展示状态,建议通过v-model双向绑定输入值,而不是监听此事件的形式 |
|
*/ |
|
emit('update:modelValue', show) |
|
nextTick(() => { |
|
onOpened.value && onOpened.value() |
|
}) |
|
} else { |
|
emit('update:modelValue', visiable.value) |
|
nextTick(() => { |
|
onClose.value && onClose.value() |
|
}) |
|
} |
|
} |
|
// 关闭 |
|
function hide() { |
|
visiable.value = false |
|
acitve.value = false |
|
loadingShow.value = false |
|
change(visiable.value) |
|
reset({ |
|
background: true, |
|
title: '', |
|
delayTime: 0, |
|
type: 'flower' |
|
}) |
|
} |
|
/** |
|
* 重置参数 |
|
*/ |
|
function reset(option) { |
|
if (option) { |
|
innerTitle.value = RegUtil.isDef(option.title) ? option.title : props.title |
|
innerType.value = RegUtil.isDef(option.type) ? option.type : props.type |
|
innerBackground.value = RegUtil.isDef(option.background) ? option.background : props.background |
|
innerdelayTime.value = RegUtil.isDef(option.delayTime) ? option.delayTime : props.delayTime |
|
onOpened.value = RegUtil.isDef(option.onOpened) ? option.onOpened : null |
|
onClose.value = RegUtil.isDef(option.onClose) ? option.onClose : null |
|
onClick.value = RegUtil.isDef(option.onClick) ? option.onClick : null |
|
} |
|
} |
|
// 点击 |
|
function click(event) { |
|
if (onClick.value) { |
|
onClick.value(event.detail) |
|
} |
|
} |
|
</script> |
|
|
|
<style lang="scss" scoped> |
|
@import '../../libs/css/index.scss'; |
|
|
|
.hd-loading { |
|
width: 100%; |
|
height: 100vh; |
|
position: fixed; |
|
left: 0; |
|
top: 0; |
|
z-index: 10000; |
|
.loading { |
|
position: absolute; |
|
display: flex; |
|
flex-direction: column; |
|
justify-content: center; |
|
top: 50vh; |
|
left: 375rpx; |
|
transform: translate(-50%, -50%); |
|
padding: 60rpx 24rpx; |
|
box-sizing: border-box; |
|
width: 240rpx; |
|
height: 240rpx; |
|
background: $color-text-primary; |
|
box-shadow: 0px 4rpx 10rpx 0rpx rgba(83, 80, 80, 0.24); |
|
border-radius: 16rpx; |
|
opacity: 0.72; |
|
image { |
|
width: 80rpx; |
|
height: 80rpx; |
|
vertical-align: middle; |
|
} |
|
&-icon { |
|
position: relative; |
|
display: flex; |
|
flex-direction: row; |
|
align-items: center; |
|
margin: 0 auto; |
|
height: 64rpx; |
|
// flower类型时的动画 |
|
&-item-flower { |
|
position: absolute; |
|
height: 100%; |
|
width: 100%; |
|
top: 0; |
|
left: 0; |
|
} |
|
&-item-flower::after { |
|
background: $color-white; |
|
display: block; |
|
width: 6rpx; |
|
height: 16rpx; |
|
margin: 0 auto; |
|
border-radius: 6rpx; |
|
overflow: hidden; |
|
content: ' '; |
|
} |
|
|
|
@for $i from 1 through 12 { |
|
&-item-flower:nth-of-type(#{$i}) { |
|
transform: rotate($i * 30deg); |
|
opacity: 1 - 0.0625 * ($i - 1); |
|
} |
|
} |
|
// flower类型时的动画结束 |
|
|
|
// dot类型时的动画 |
|
&-item-dot { |
|
background: $color-white; |
|
display: inline-block; |
|
width: 32rpx; |
|
height: 32rpx; |
|
margin: 0 auto; |
|
border-radius: 50%; |
|
overflow: hidden; |
|
transform: scale(0.5); |
|
} |
|
|
|
@for $i from 1 through 3 { |
|
&-item-dot:nth-of-type(#{$i}) { |
|
opacity: 0; |
|
-webkit-transition-property: -webkit-transform; |
|
-webkit-transition-duration: 0.9s; |
|
-moz-transition-property: -moz-transform; |
|
-moz-transition-duration: 0.9s; |
|
transition-property: -moz-transform; |
|
transition-duration: 0.9s; |
|
-webkit-animation: shine 0.9s linear #{($i - 1) * 100}ms infinite; |
|
-moz-animation: shine 0.9s linear #{($i - 1) * 100}ms infinite; |
|
-o-animation: shine 0.9s linear #{($i - 1) * 100}ms infinite; |
|
animation: shine 0.9s linear #{($i - 1) * 100}ms infinite; |
|
} |
|
} |
|
// dot类型时的动画结束 |
|
} |
|
} |
|
.loading--white { |
|
background: none; |
|
box-shadow: none; |
|
opacity: 1; |
|
.loading-icon-item-flower::after { |
|
background: #333333; |
|
} |
|
.loading-icon-item-dot { |
|
background: #333333; |
|
} |
|
} |
|
|
|
/* 无限旋转动画 */ |
|
.rotate { |
|
-webkit-transition-property: -webkit-transform; |
|
-webkit-transition-duration: 1.8s; |
|
-moz-transition-property: -moz-transform; |
|
-moz-transition-duration: 1.8s; |
|
transition-property: -moz-transform; |
|
transition-duration: 1.8s; |
|
-webkit-animation: rotate 1.8s linear infinite; |
|
-moz-animation: rotate 1.8s linear infinite; |
|
-o-animation: rotate 1.8s linear infinite; |
|
animation: rotate 1.8s linear infinite; |
|
} |
|
|
|
@-webkit-keyframes rotate { |
|
from { |
|
-webkit-transform: rotate(359deg); |
|
} |
|
|
|
to { |
|
-webkit-transform: rotate(0deg); |
|
} |
|
} |
|
|
|
@-moz-keyframes rotate { |
|
from { |
|
-moz-transform: rotate(359deg); |
|
} |
|
|
|
to { |
|
-moz-transform: rotate(0deg); |
|
} |
|
} |
|
|
|
@-o-keyframes rotate { |
|
from { |
|
-o-transform: rotate(359deg); |
|
} |
|
|
|
to { |
|
-o-transform: rotate(0deg); |
|
} |
|
} |
|
|
|
@keyframes rotate { |
|
from { |
|
transform: rotate(359deg); |
|
} |
|
|
|
to { |
|
transform: rotate(0deg); |
|
} |
|
} |
|
|
|
// 圆点闪烁动画 |
|
@keyframes shine { |
|
0% { |
|
opacity: 1; |
|
} |
|
88% { |
|
opacity: 0; |
|
} |
|
100% { |
|
opacity: 1; |
|
} |
|
} |
|
.title { |
|
margin-top: 20rpx; |
|
text-align: center; |
|
width: 192rpx; |
|
font-size: 26rpx; |
|
font-family: PingFangSC-Regular, PingFang SC; |
|
font-weight: 400; |
|
color: $color-white; |
|
line-height: 36rpx; |
|
@include ellipsis(); |
|
} |
|
|
|
/* 无限旋转动画 end */ |
|
} |
|
</style>
|
|
|