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.
343 lines
12 KiB
343 lines
12 KiB
<template> |
|
<view |
|
class="u-loading-icon" |
|
:style="[$u.addStyle(customStyle)]" |
|
:class="[vertical && 'u-loading-icon--vertical']" |
|
v-if="show" |
|
> |
|
<view |
|
v-if="!webviewHide" |
|
class="u-loading-icon__spinner" |
|
:class="[`u-loading-icon__spinner--${mode}`]" |
|
ref="ani" |
|
:style="{ |
|
color: color, |
|
width: $u.addUnit(size), |
|
height: $u.addUnit(size), |
|
borderTopColor: color, |
|
borderBottomColor: otherBorderColor, |
|
borderLeftColor: otherBorderColor, |
|
borderRightColor: otherBorderColor, |
|
'animation-duration': `${duration}ms`, |
|
'animation-timing-function': mode === 'semicircle' || mode === 'circle' ? timingFunction : '' |
|
}" |
|
> |
|
<block v-if="mode === 'spinner'"> |
|
<!-- #ifndef APP-NVUE --> |
|
<view |
|
v-for="(item, index) in array12" |
|
:key="index" |
|
class="u-loading-icon__dot" |
|
> |
|
</view> |
|
<!-- #endif --> |
|
<!-- #ifdef APP-NVUE --> |
|
<!-- 此组件内部图标部分无法设置宽高,即使通过width和height配置了也无效 --> |
|
<loading-indicator |
|
v-if="!webviewHide" |
|
class="u-loading-indicator" |
|
:animating="true" |
|
:style="{ |
|
color: color, |
|
width: $u.addUnit(size), |
|
height: $u.addUnit(size) |
|
}" |
|
/> |
|
<!-- #endif --> |
|
</block> |
|
</view> |
|
<text |
|
v-if="text" |
|
class="u-loading-icon__text" |
|
:style="{ |
|
fontSize: $u.addUnit(textSize), |
|
color: textColor, |
|
}" |
|
>{{text}}</text> |
|
</view> |
|
</template> |
|
|
|
<script> |
|
import props from './props.js'; |
|
// #ifdef APP-NVUE |
|
const animation = weex.requireModule('animation'); |
|
// #endif |
|
/** |
|
* loading 加载动画 |
|
* @description 警此组件为一个小动画,目前用在uView的loadmore加载更多和switch开关等组件的正在加载状态场景。 |
|
* @tutorial https://www.uviewui.com/components/loading.html |
|
* @property {Boolean} show 是否显示组件 (默认 true) |
|
* @property {String} color 动画活动区域的颜色,只对 mode = flower 模式有效(默认color['u-tips-color']) |
|
* @property {String} textColor 提示文本的颜色(默认color['u-tips-color']) |
|
* @property {Boolean} vertical 文字和图标是否垂直排列 (默认 false ) |
|
* @property {String} mode 模式选择,见官网说明(默认 'circle' ) |
|
* @property {String | Number} size 加载图标的大小,单位px (默认 24 ) |
|
* @property {String | Number} textSize 文字大小(默认 15 ) |
|
* @property {String | Number} text 文字内容 |
|
* @property {String} timingFunction 动画模式 (默认 'ease-in-out' ) |
|
* @property {String | Number} duration 动画执行周期时间(默认 1200) |
|
* @property {String} inactiveColor mode=circle时的暗边颜色 |
|
* @property {Object} customStyle 定义需要用到的外部样式 |
|
* @example <u-loading mode="circle"></u-loading> |
|
*/ |
|
export default { |
|
name: 'u-loading-icon', |
|
mixins: [uni.$u.mpMixin, uni.$u.mixin, props], |
|
data() { |
|
return { |
|
// Array.form可以通过一个伪数组对象创建指定长度的数组 |
|
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/from |
|
array12: Array.from({ |
|
length: 12 |
|
}), |
|
// 这里需要设置默认值为360,否则在安卓nvue上,会延迟一个duration周期后才执行 |
|
// 在iOS nvue上,则会一开始默认执行两个周期的动画 |
|
aniAngel: 360, // 动画旋转角度 |
|
webviewHide: false, // 监听webview的状态,如果隐藏了页面,则停止动画,以免性能消耗 |
|
loading: false, // 是否运行中,针对nvue使用 |
|
} |
|
}, |
|
computed: { |
|
// 当为circle类型时,给其另外三边设置一个更轻一些的颜色 |
|
// 之所以需要这么做的原因是,比如父组件传了color为红色,那么需要另外的三个边为浅红色 |
|
// 而不能是固定的某一个其他颜色(因为这个固定的颜色可能浅蓝,导致效果没有那么细腻良好) |
|
otherBorderColor() { |
|
const lightColor = uni.$u.colorGradient(this.color, '#ffffff', 100)[80] |
|
if (this.mode === 'circle') { |
|
return this.inactiveColor ? this.inactiveColor : lightColor |
|
} else { |
|
return 'transparent' |
|
} |
|
// return this.mode === 'circle' ? this.inactiveColor ? this.inactiveColor : lightColor : 'transparent' |
|
} |
|
}, |
|
watch: { |
|
show(n) { |
|
// nvue中,show为true,且为非loading状态,就重新执行动画模块 |
|
// #ifdef APP-NVUE |
|
if (n && !this.loading) { |
|
setTimeout(() => { |
|
this.startAnimate() |
|
}, 30) |
|
} |
|
// #endif |
|
} |
|
}, |
|
mounted() { |
|
this.init() |
|
}, |
|
methods: { |
|
init() { |
|
setTimeout(() => { |
|
// #ifdef APP-NVUE |
|
this.show && this.nvueAnimate() |
|
// #endif |
|
// #ifdef APP-PLUS |
|
this.show && this.addEventListenerToWebview() |
|
// #endif |
|
}, 20) |
|
}, |
|
// 监听webview的显示与隐藏 |
|
addEventListenerToWebview() { |
|
// webview的堆栈 |
|
const pages = getCurrentPages() |
|
// 当前页面 |
|
const page = pages[pages.length - 1] |
|
// 当前页面的webview实例 |
|
const currentWebview = page.$getAppWebview() |
|
// 监听webview的显示与隐藏,从而停止或者开始动画(为了性能) |
|
currentWebview.addEventListener('hide', () => { |
|
this.webviewHide = true |
|
}) |
|
currentWebview.addEventListener('show', () => { |
|
this.webviewHide = false |
|
}) |
|
}, |
|
// #ifdef APP-NVUE |
|
nvueAnimate() { |
|
// nvue下,非spinner类型时才需要旋转,因为nvue的spinner类型,使用了weex的 |
|
// loading-indicator组件,自带旋转功能 |
|
this.mode !== 'spinner' && this.startAnimate() |
|
}, |
|
// 执行nvue的animate模块动画 |
|
startAnimate() { |
|
this.loading = true |
|
const ani = this.$refs.ani |
|
if (!ani) return |
|
animation.transition(ani, { |
|
// 进行角度旋转 |
|
styles: { |
|
transform: `rotate(${this.aniAngel}deg)`, |
|
transformOrigin: 'center center' |
|
}, |
|
duration: this.duration, |
|
timingFunction: this.timingFunction, |
|
// delay: 10 |
|
}, () => { |
|
// 每次增加360deg,为了让其重新旋转一周 |
|
this.aniAngel += 360 |
|
// 动画结束后,继续循环执行动画,需要同时判断webviewHide变量 |
|
// nvue安卓,页面隐藏后依然会继续执行startAnimate方法 |
|
this.show && !this.webviewHide ? this.startAnimate() : this.loading = false |
|
}) |
|
} |
|
// #endif |
|
} |
|
} |
|
</script> |
|
|
|
<style lang="scss" scoped> |
|
@import "../../libs/css/components.scss"; |
|
$u-loading-icon-color: #c8c9cc !default; |
|
$u-loading-icon-text-margin-left:4px !default; |
|
$u-loading-icon-text-color:$u-content-color !default; |
|
$u-loading-icon-text-font-size:14px !default; |
|
$u-loading-icon-text-line-height:20px !default; |
|
$u-loading-width:30px !default; |
|
$u-loading-height:30px !default; |
|
$u-loading-max-width:100% !default; |
|
$u-loading-max-height:100% !default; |
|
$u-loading-semicircle-border-width: 2px !default; |
|
$u-loading-semicircle-border-color:transparent !default; |
|
$u-loading-semicircle-border-top-right-radius: 100px !default; |
|
$u-loading-semicircle-border-top-left-radius: 100px !default; |
|
$u-loading-semicircle-border-bottom-left-radius: 100px !default; |
|
$u-loading-semicircle-border-bottom-right-radiu: 100px !default; |
|
$u-loading-semicircle-border-style: solid !default; |
|
$u-loading-circle-border-top-right-radius: 100px !default; |
|
$u-loading-circle-border-top-left-radius: 100px !default; |
|
$u-loading-circle-border-bottom-left-radius: 100px !default; |
|
$u-loading-circle-border-bottom-right-radiu: 100px !default; |
|
$u-loading-circle-border-width:2px !default; |
|
$u-loading-circle-border-top-color:#e5e5e5 !default; |
|
$u-loading-circle-border-right-color:$u-loading-circle-border-top-color !default; |
|
$u-loading-circle-border-bottom-color:$u-loading-circle-border-top-color !default; |
|
$u-loading-circle-border-left-color:$u-loading-circle-border-top-color !default; |
|
$u-loading-circle-border-style:solid !default; |
|
$u-loading-icon-host-font-size:0px !default; |
|
$u-loading-icon-host-line-height:1 !default; |
|
$u-loading-icon-vertical-margin:6px 0 0 !default; |
|
$u-loading-icon-dot-top:0 !default; |
|
$u-loading-icon-dot-left:0 !default; |
|
$u-loading-icon-dot-width:100% !default; |
|
$u-loading-icon-dot-height:100% !default; |
|
$u-loading-icon-dot-before-width:2px !default; |
|
$u-loading-icon-dot-before-height:25% !default; |
|
$u-loading-icon-dot-before-margin:0 auto !default; |
|
$u-loading-icon-dot-before-background-color:currentColor !default; |
|
$u-loading-icon-dot-before-border-radius:40% !default; |
|
|
|
.u-loading-icon { |
|
/* #ifndef APP-NVUE */ |
|
// display: inline-flex; |
|
/* #endif */ |
|
flex-direction: row; |
|
align-items: center; |
|
justify-content: center; |
|
color: $u-loading-icon-color; |
|
|
|
&__text { |
|
margin-left: $u-loading-icon-text-margin-left; |
|
color: $u-loading-icon-text-color; |
|
font-size: $u-loading-icon-text-font-size; |
|
line-height: $u-loading-icon-text-line-height; |
|
} |
|
|
|
&__spinner { |
|
width: $u-loading-width; |
|
height: $u-loading-height; |
|
position: relative; |
|
/* #ifndef APP-NVUE */ |
|
box-sizing: border-box; |
|
max-width: $u-loading-max-width; |
|
max-height: $u-loading-max-height; |
|
animation: u-rotate 1s linear infinite; |
|
/* #endif */ |
|
} |
|
|
|
&__spinner--semicircle { |
|
border-width: $u-loading-semicircle-border-width; |
|
border-color: $u-loading-semicircle-border-color; |
|
border-top-right-radius: $u-loading-semicircle-border-top-right-radius; |
|
border-top-left-radius: $u-loading-semicircle-border-top-left-radius; |
|
border-bottom-left-radius: $u-loading-semicircle-border-bottom-left-radius; |
|
border-bottom-right-radius: $u-loading-semicircle-border-bottom-right-radiu; |
|
border-style: $u-loading-semicircle-border-style; |
|
} |
|
|
|
&__spinner--circle { |
|
border-top-right-radius: $u-loading-circle-border-top-right-radius; |
|
border-top-left-radius: $u-loading-circle-border-top-left-radius; |
|
border-bottom-left-radius: $u-loading-circle-border-bottom-left-radius; |
|
border-bottom-right-radius: $u-loading-circle-border-bottom-right-radiu; |
|
border-width: $u-loading-circle-border-width; |
|
border-top-color: $u-loading-circle-border-top-color; |
|
border-right-color: $u-loading-circle-border-right-color; |
|
border-bottom-color: $u-loading-circle-border-bottom-color; |
|
border-left-color: $u-loading-circle-border-left-color; |
|
border-style: $u-loading-circle-border-style; |
|
} |
|
|
|
&--vertical { |
|
flex-direction: column |
|
} |
|
} |
|
|
|
/* #ifndef APP-NVUE */ |
|
:host { |
|
font-size: $u-loading-icon-host-font-size; |
|
line-height: $u-loading-icon-host-line-height; |
|
} |
|
|
|
.u-loading-icon { |
|
&__spinner--spinner { |
|
animation-timing-function: steps(12) |
|
} |
|
|
|
&__text:empty { |
|
display: none |
|
} |
|
|
|
&--vertical &__text { |
|
margin: $u-loading-icon-vertical-margin; |
|
color: $u-content-color; |
|
} |
|
|
|
&__dot { |
|
position: absolute; |
|
top: $u-loading-icon-dot-top; |
|
left: $u-loading-icon-dot-left; |
|
width: $u-loading-icon-dot-width; |
|
height: $u-loading-icon-dot-height; |
|
|
|
&:before { |
|
display: block; |
|
width: $u-loading-icon-dot-before-width; |
|
height: $u-loading-icon-dot-before-height; |
|
margin: $u-loading-icon-dot-before-margin; |
|
background-color: $u-loading-icon-dot-before-background-color; |
|
border-radius: $u-loading-icon-dot-before-border-radius; |
|
content: " " |
|
} |
|
} |
|
} |
|
|
|
@for $i from 1 through 12 { |
|
.u-loading-icon__dot:nth-of-type(#{$i}) { |
|
transform: rotate($i * 30deg); |
|
opacity: 1 - 0.0625 * ($i - 1); |
|
} |
|
} |
|
|
|
@keyframes u-rotate { |
|
0% { |
|
transform: rotate(0deg) |
|
} |
|
|
|
to { |
|
transform: rotate(1turn) |
|
} |
|
} |
|
|
|
/* #endif */ |
|
</style>
|
|
|