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