tui-skeleton.vue 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. <template>
  2. <view class="tui-skeleton-cmomon tui-skeleton-box"
  3. :style="{width: winWidth+'px', height:winHeight+'px', backgroundColor:backgroundColor}">
  4. <view class="tui-skeleton-cmomon" :class="{'tui-skeleton__active':active}" v-for="(item,index) in skeletonElements" :key="index"
  5. :style="{width: item.width+'px', height:item.height+'px', left: item.left+'px', top: item.top+'px',backgroundColor: skeletonBgColor,borderRadius:getRadius(item.skeletonType,borderRadius)}">
  6. </view>
  7. <view class="tui-loading" :class="[getLoadingType(loadingType)]" v-if="isLoading"></view>
  8. </view>
  9. </template>
  10. <script>
  11. export default {
  12. name: "tuiSkeleton",
  13. props: {
  14. //选择器(外层容器)
  15. selector: {
  16. type: String,
  17. default: "tui-skeleton"
  18. },
  19. //外层容器背景颜色
  20. backgroundColor: {
  21. type: String,
  22. default: "#fff"
  23. },
  24. //骨架元素背景颜色
  25. skeletonBgColor: {
  26. type: String,
  27. default: "#e9e9e9"
  28. },
  29. //骨架元素类型:矩形,圆形,带圆角矩形["rect","circular","fillet"]
  30. //默认所有,根据页面情况进行传值
  31. //页面对应元素class为:tui-skeleton-rect,tui-skeleton-circular,tui-skeleton-fillet
  32. //如果传入的值不在下列数组中,则为自定义class值,默认按矩形渲染
  33. skeletonType: {
  34. type: Array,
  35. default () {
  36. return ["rect", "circular", "fillet"]
  37. }
  38. },
  39. //圆角值,skeletonType=fillet时生效
  40. borderRadius: {
  41. type: String,
  42. default: "16rpx"
  43. },
  44. //骨架屏预生成数据:提前生成好的数据,当传入该属性值时,则不会再次查找子节点信息
  45. preloadData: {
  46. type: Array,
  47. default () {
  48. return []
  49. }
  50. },
  51. //是否需要loading
  52. isLoading: {
  53. type: Boolean,
  54. default: false
  55. },
  56. //loading类型[1-10]
  57. loadingType: {
  58. type: Number,
  59. default: 1
  60. },
  61. //是否展示动画效果
  62. active: {
  63. type: Boolean,
  64. default: true
  65. }
  66. },
  67. created() {
  68. const res = uni.getSystemInfoSync();
  69. this.winWidth = res.windowWidth;
  70. this.winHeight = res.windowHeight;
  71. //如果有预生成数据,则直接使用
  72. this.isPreload(true)
  73. },
  74. mounted() {
  75. this.$nextTick(() => {
  76. this.nodesRef(`.${this.selector}`).then((res) => {
  77. if (res && res[0]) {
  78. this.winHeight = res[0].height + Math.abs(res[0].top)
  79. }
  80. });
  81. !this.isPreload() && this.selectorQuery()
  82. })
  83. },
  84. data() {
  85. return {
  86. winWidth: 375,
  87. winHeight: 800,
  88. skeletonElements: []
  89. };
  90. },
  91. methods: {
  92. getLoadingType: function(type) {
  93. let value = 1
  94. if (type && type > 0 && type < 11) {
  95. value = type
  96. }
  97. return 'tui-loading-' + value
  98. },
  99. getRadius: function(type, val) {
  100. let radius = "0"
  101. if (type == "circular") {
  102. radius = "50%"
  103. } else if (type == "fillet") {
  104. radius = val
  105. }
  106. return radius;
  107. },
  108. isPreload(init) {
  109. let preloadData = this.preloadData || []
  110. if (preloadData.length) {
  111. init && (this.skeletonElements = preloadData)
  112. return true
  113. }
  114. return false
  115. },
  116. async selectorQuery() {
  117. let skeletonType = this.skeletonType || []
  118. let nodes = []
  119. for (let item of skeletonType) {
  120. let className = '';
  121. // #ifndef MP-WEIXIN
  122. className = `.${item}`;
  123. if (~'rect_circular_fillet'.indexOf(item)) {
  124. className = `.${this.selector}-${item}`;
  125. }
  126. // #endif
  127. // #ifdef MP-WEIXIN
  128. className = `.${this.selector} >>> .${item}`;
  129. if (~'rect_circular_fillet'.indexOf(item)) {
  130. className = `.${this.selector} >>> .${this.selector}-${item}`;
  131. }
  132. // #endif
  133. await this.nodesRef(className).then((res) => {
  134. res.map(d => {
  135. d.skeletonType = item
  136. })
  137. nodes = nodes.concat(res)
  138. })
  139. }
  140. this.skeletonElements = nodes
  141. },
  142. async nodesRef(className) {
  143. return await new Promise((resolve, reject) => {
  144. uni.createSelectorQuery().selectAll(className).boundingClientRect((res) => {
  145. if (res) {
  146. resolve(res);
  147. } else {
  148. reject(res)
  149. }
  150. }).exec();
  151. })
  152. }
  153. }
  154. }
  155. </script>
  156. <style scoped>
  157. .tui-skeleton-cmomon {
  158. position: absolute;
  159. z-index: 99999;
  160. }
  161. .tui-skeleton-box {
  162. left: 0;
  163. top: 0;
  164. }
  165. .tui-loading {
  166. display: inline-block;
  167. vertical-align: middle;
  168. width: 40rpx;
  169. height: 40rpx;
  170. background: 0 0;
  171. border-radius: 50%;
  172. border: 2px solid;
  173. animation: tui-rotate 0.7s linear infinite;
  174. position: fixed;
  175. z-index: 999999;
  176. left: 50%;
  177. top: 50%;
  178. margin-left: -20rpx;
  179. margin-top: -20rpx;
  180. }
  181. .tui-loading-1 {
  182. border-color: #e5e5e5 #e5e5e5 #e5e5e5 #5677fc;
  183. }
  184. .tui-loading-2 {
  185. border-color: #e5e5e5 #e5e5e5 #e5e5e5 #8f8d8e;
  186. }
  187. .tui-loading-3 {
  188. border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) #fff;
  189. }
  190. .tui-loading-4 {
  191. border-color: #e5e5e5 #e5e5e5 #e5e5e5 #35b06a;
  192. }
  193. .tui-loading-5 {
  194. border-color: #e5e5e5 #e5e5e5 #e5e5e5 #fc872d;
  195. }
  196. .tui-loading-6 {
  197. border-color: #e5e5e5 #e5e5e5 #e5e5e5 #eb0909;
  198. }
  199. .tui-loading-7 {
  200. border-color: #5677fc transparent #5677fc transparent;
  201. }
  202. .tui-loading-8 {
  203. border-color: #35b06a transparent #35b06a transparent;
  204. }
  205. .tui-loading-9 {
  206. border-color: #fc872d transparent #fc872d transparent;
  207. }
  208. .tui-loading-10 {
  209. border-color: #eb0909 transparent #eb0909 transparent;
  210. }
  211. @-webkit-keyframes tui-rotate {
  212. 0% {
  213. transform: rotate(0);
  214. }
  215. 100% {
  216. transform: rotate(360deg);
  217. }
  218. }
  219. @keyframes tui-rotate {
  220. 0% {
  221. transform: rotate(0);
  222. }
  223. 100% {
  224. transform: rotate(360deg);
  225. }
  226. }
  227. .tui-skeleton__active {
  228. background: linear-gradient(90deg, #f2f2f2 25%, #e6e6e6 37%, #f2f2f2 63%);
  229. animation: tui-active 1.4s ease infinite;
  230. background-size: 400% 100%
  231. }
  232. @keyframes tui-active {
  233. 0% {
  234. background-position: 100% 50%
  235. }
  236. 100% {
  237. background-position: 0 50%
  238. }
  239. }
  240. </style>