珠宝小程序前端代码
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.

347 lines
12 KiB

5 months ago
  1. <template>
  2. <view
  3. class="uv-loading-icon"
  4. :style="[$uv.addStyle(customStyle)]"
  5. :class="[vertical && 'uv-loading-icon--vertical']"
  6. v-if="show"
  7. >
  8. <view
  9. v-if="!webviewHide"
  10. class="uv-loading-icon__spinner"
  11. :class="[`uv-loading-icon__spinner--${mode}`]"
  12. ref="ani"
  13. :style="{
  14. color: color,
  15. width: $uv.addUnit(size),
  16. height: $uv.addUnit(size),
  17. borderTopColor: color,
  18. borderBottomColor: otherBorderColor,
  19. borderLeftColor: otherBorderColor,
  20. borderRightColor: otherBorderColor,
  21. 'animation-duration': `${duration}ms`,
  22. 'animation-timing-function': mode === 'semicircle' || mode === 'circle' ? timingFunction : ''
  23. }"
  24. >
  25. <block v-if="mode === 'spinner'">
  26. <!-- #ifndef APP-NVUE -->
  27. <view
  28. v-for="(item, index) in array12"
  29. :key="index"
  30. class="uv-loading-icon__dot"
  31. >
  32. </view>
  33. <!-- #endif -->
  34. <!-- #ifdef APP-NVUE -->
  35. <!-- 此组件内部图标部分无法设置宽高即使通过width和height配置了也无效 -->
  36. <loading-indicator
  37. v-if="!webviewHide"
  38. class="uv-loading-indicator"
  39. :animating="true"
  40. :style="{
  41. color: color,
  42. width: $uv.addUnit(size),
  43. height: $uv.addUnit(size)
  44. }"
  45. />
  46. <!-- #endif -->
  47. </block>
  48. </view>
  49. <text
  50. v-if="text"
  51. class="uv-loading-icon__text"
  52. :style="[{
  53. fontSize: $uv.addUnit(textSize),
  54. color: textColor,
  55. },$uv.addStyle(textStyle)]"
  56. >{{text}}</text>
  57. </view>
  58. </template>
  59. <script>
  60. import { colorGradient } from '@/uni_modules/uv-ui-tools/libs/function/colorGradient.js'
  61. import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
  62. import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
  63. import props from './props.js';
  64. // #ifdef APP-NVUE
  65. const animation = weex.requireModule('animation');
  66. // #endif
  67. /**
  68. * loading 加载动画
  69. * @description 警此组件为一个小动画目前用在uvui的loadmore加载更多和switch开关等组件的正在加载状态场景
  70. * @tutorial https://www.uvui.cn/components/loading.html
  71. * @property {Boolean} show 是否显示组件 (默认 true)
  72. * @property {String} color 动画活动区域的颜色只对 mode = flower 模式有效默认#909193
  73. * @property {String} textColor 提示文本的颜色默认#909193
  74. * @property {Boolean} vertical 文字和图标是否垂直排列 (默认 false )
  75. * @property {String} mode 模式选择见官网说明默认 'circle'
  76. * @property {String | Number} size 加载图标的大小单位px 默认 24
  77. * @property {String | Number} textSize 文字大小默认 15
  78. * @property {String | Number} text 文字内容
  79. * @property {Object} textStyle 文字样式
  80. * @property {String} timingFunction 动画模式 默认 'ease-in-out'
  81. * @property {String | Number} duration 动画执行周期时间默认 1200
  82. * @property {String} inactiveColor mode=circle时的暗边颜色
  83. * @property {Object} customStyle 定义需要用到的外部样式
  84. * @example <uv-loading mode="circle"></uv-loading>
  85. */
  86. export default {
  87. name: 'uv-loading-icon',
  88. mixins: [mpMixin, mixin, props],
  89. data() {
  90. return {
  91. // Array.form可以通过一个伪数组对象创建指定长度的数组
  92. // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/from
  93. array12: Array.from({
  94. length: 12
  95. }),
  96. // 这里需要设置默认值为360,否则在安卓nvue上,会延迟一个duration周期后才执行
  97. // 在iOS nvue上,则会一开始默认执行两个周期的动画
  98. aniAngel: 360, // 动画旋转角度
  99. webviewHide: false, // 监听webview的状态,如果隐藏了页面,则停止动画,以免性能消耗
  100. loading: false, // 是否运行中,针对nvue使用
  101. }
  102. },
  103. computed: {
  104. // 当为circle类型时,给其另外三边设置一个更轻一些的颜色
  105. // 之所以需要这么做的原因是,比如父组件传了color为红色,那么需要另外的三个边为浅红色
  106. // 而不能是固定的某一个其他颜色(因为这个固定的颜色可能浅蓝,导致效果没有那么细腻良好)
  107. otherBorderColor() {
  108. const lightColor = colorGradient(this.color, '#ffffff', 100)[80]
  109. if (this.mode === 'circle') {
  110. return this.inactiveColor ? this.inactiveColor : lightColor
  111. } else {
  112. return 'transparent'
  113. }
  114. }
  115. },
  116. watch: {
  117. show(n) {
  118. // nvue中,show为true,且为非loading状态,就重新执行动画模块
  119. // #ifdef APP-NVUE
  120. if (n && !this.loading) {
  121. setTimeout(() => {
  122. this.startAnimate()
  123. }, 30)
  124. }
  125. // #endif
  126. }
  127. },
  128. mounted() {
  129. this.init()
  130. },
  131. methods: {
  132. init() {
  133. setTimeout(() => {
  134. // #ifdef APP-NVUE
  135. this.show && this.nvueAnimate()
  136. // #endif
  137. // #ifdef APP-PLUS
  138. this.show && this.addEventListenerToWebview()
  139. // #endif
  140. }, 20)
  141. },
  142. // 监听webview的显示与隐藏
  143. addEventListenerToWebview() {
  144. // webview的堆栈
  145. const pages = getCurrentPages()
  146. // 当前页面
  147. const page = pages[pages.length - 1]
  148. // 当前页面的webview实例
  149. const currentWebview = page.$getAppWebview()
  150. // 监听webview的显示与隐藏,从而停止或者开始动画(为了性能)
  151. currentWebview.addEventListener('hide', () => {
  152. this.webviewHide = true
  153. })
  154. currentWebview.addEventListener('show', () => {
  155. this.webviewHide = false
  156. })
  157. },
  158. // #ifdef APP-NVUE
  159. nvueAnimate() {
  160. // nvue下,非spinner类型时才需要旋转,因为nvue的spinner类型,使用了weex的
  161. // loading-indicator组件,自带旋转功能
  162. this.mode !== 'spinner' && this.startAnimate()
  163. },
  164. // 执行nvue的animate模块动画
  165. startAnimate() {
  166. this.loading = true
  167. const ani = this.$refs.ani
  168. if (!ani) return
  169. animation.transition(ani, {
  170. // 进行角度旋转
  171. styles: {
  172. transform: `rotate(${this.aniAngel}deg)`,
  173. transformOrigin: 'center center'
  174. },
  175. duration: this.duration,
  176. timingFunction: this.timingFunction,
  177. // delay: 10
  178. }, () => {
  179. // 每次增加360deg,为了让其重新旋转一周
  180. this.aniAngel += 360
  181. // 动画结束后,继续循环执行动画,需要同时判断webviewHide变量
  182. // nvue安卓,页面隐藏后依然会继续执行startAnimate方法
  183. this.show && !this.webviewHide ? this.startAnimate() : this.loading = false
  184. })
  185. }
  186. // #endif
  187. }
  188. }
  189. </script>
  190. <style lang="scss" scoped>
  191. @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
  192. @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
  193. $uv-loading-icon-color: #c8c9cc !default;
  194. $uv-loading-icon-text-margin-left:4px !default;
  195. $uv-loading-icon-text-color:$uv-content-color !default;
  196. $uv-loading-icon-text-font-size:14px !default;
  197. $uv-loading-icon-text-line-height:20px !default;
  198. $uv-loading-width:30px !default;
  199. $uv-loading-height:30px !default;
  200. $uv-loading-max-width:100% !default;
  201. $uv-loading-max-height:100% !default;
  202. $uv-loading-semicircle-border-width: 2px !default;
  203. $uv-loading-semicircle-border-color:transparent !default;
  204. $uv-loading-semicircle-border-top-right-radius: 100px !default;
  205. $uv-loading-semicircle-border-top-left-radius: 100px !default;
  206. $uv-loading-semicircle-border-bottom-left-radius: 100px !default;
  207. $uv-loading-semicircle-border-bottom-right-radiu: 100px !default;
  208. $uv-loading-semicircle-border-style: solid !default;
  209. $uv-loading-circle-border-top-right-radius: 100px !default;
  210. $uv-loading-circle-border-top-left-radius: 100px !default;
  211. $uv-loading-circle-border-bottom-left-radius: 100px !default;
  212. $uv-loading-circle-border-bottom-right-radiu: 100px !default;
  213. $uv-loading-circle-border-width:2px !default;
  214. $uv-loading-circle-border-top-color:#e5e5e5 !default;
  215. $uv-loading-circle-border-right-color:$uv-loading-circle-border-top-color !default;
  216. $uv-loading-circle-border-bottom-color:$uv-loading-circle-border-top-color !default;
  217. $uv-loading-circle-border-left-color:$uv-loading-circle-border-top-color !default;
  218. $uv-loading-circle-border-style:solid !default;
  219. $uv-loading-icon-host-font-size:0px !default;
  220. $uv-loading-icon-host-line-height:1 !default;
  221. $uv-loading-icon-vertical-margin:6px 0 0 !default;
  222. $uv-loading-icon-dot-top:0 !default;
  223. $uv-loading-icon-dot-left:0 !default;
  224. $uv-loading-icon-dot-width:100% !default;
  225. $uv-loading-icon-dot-height:100% !default;
  226. $uv-loading-icon-dot-before-width:2px !default;
  227. $uv-loading-icon-dot-before-height:25% !default;
  228. $uv-loading-icon-dot-before-margin:0 auto !default;
  229. $uv-loading-icon-dot-before-background-color:currentColor !default;
  230. $uv-loading-icon-dot-before-border-radius:40% !default;
  231. .uv-loading-icon {
  232. /* #ifndef APP-NVUE */
  233. // display: inline-flex;
  234. /* #endif */
  235. flex-direction: row;
  236. align-items: center;
  237. justify-content: center;
  238. color: $uv-loading-icon-color;
  239. &__text {
  240. margin-left: $uv-loading-icon-text-margin-left;
  241. color: $uv-loading-icon-text-color;
  242. font-size: $uv-loading-icon-text-font-size;
  243. line-height: $uv-loading-icon-text-line-height;
  244. }
  245. &__spinner {
  246. width: $uv-loading-width;
  247. height: $uv-loading-height;
  248. position: relative;
  249. /* #ifndef APP-NVUE */
  250. box-sizing: border-box;
  251. max-width: $uv-loading-max-width;
  252. max-height: $uv-loading-max-height;
  253. animation: uv-rotate 1s linear infinite;
  254. /* #endif */
  255. }
  256. &__spinner--semicircle {
  257. border-width: $uv-loading-semicircle-border-width;
  258. border-color: $uv-loading-semicircle-border-color;
  259. border-top-right-radius: $uv-loading-semicircle-border-top-right-radius;
  260. border-top-left-radius: $uv-loading-semicircle-border-top-left-radius;
  261. border-bottom-left-radius: $uv-loading-semicircle-border-bottom-left-radius;
  262. border-bottom-right-radius: $uv-loading-semicircle-border-bottom-right-radiu;
  263. border-style: $uv-loading-semicircle-border-style;
  264. }
  265. &__spinner--circle {
  266. border-top-right-radius: $uv-loading-circle-border-top-right-radius;
  267. border-top-left-radius: $uv-loading-circle-border-top-left-radius;
  268. border-bottom-left-radius: $uv-loading-circle-border-bottom-left-radius;
  269. border-bottom-right-radius: $uv-loading-circle-border-bottom-right-radiu;
  270. border-width: $uv-loading-circle-border-width;
  271. border-top-color: $uv-loading-circle-border-top-color;
  272. border-right-color: $uv-loading-circle-border-right-color;
  273. border-bottom-color: $uv-loading-circle-border-bottom-color;
  274. border-left-color: $uv-loading-circle-border-left-color;
  275. border-style: $uv-loading-circle-border-style;
  276. }
  277. &--vertical {
  278. flex-direction: column
  279. }
  280. }
  281. /* #ifndef APP-NVUE */
  282. :host {
  283. font-size: $uv-loading-icon-host-font-size;
  284. line-height: $uv-loading-icon-host-line-height;
  285. }
  286. .uv-loading-icon {
  287. &__spinner--spinner {
  288. animation-timing-function: steps(12)
  289. }
  290. &__text:empty {
  291. display: none
  292. }
  293. &--vertical &__text {
  294. margin: $uv-loading-icon-vertical-margin;
  295. color: $uv-content-color;
  296. }
  297. &__dot {
  298. position: absolute;
  299. top: $uv-loading-icon-dot-top;
  300. left: $uv-loading-icon-dot-left;
  301. width: $uv-loading-icon-dot-width;
  302. height: $uv-loading-icon-dot-height;
  303. &:before {
  304. display: block;
  305. width: $uv-loading-icon-dot-before-width;
  306. height: $uv-loading-icon-dot-before-height;
  307. margin: $uv-loading-icon-dot-before-margin;
  308. background-color: $uv-loading-icon-dot-before-background-color;
  309. border-radius: $uv-loading-icon-dot-before-border-radius;
  310. content: " "
  311. }
  312. }
  313. }
  314. @for $i from 1 through 12 {
  315. .uv-loading-icon__dot:nth-of-type(#{$i}) {
  316. transform: rotate($i * 30deg);
  317. opacity: 1 - 0.0625 * ($i - 1);
  318. }
  319. }
  320. @keyframes uv-rotate {
  321. 0% {
  322. transform: rotate(0deg)
  323. }
  324. to {
  325. transform: rotate(1turn)
  326. }
  327. }
  328. /* #endif */
  329. </style>