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