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