tui-skeleton.vue 5.0 KB

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