|
|
<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>
|