前言:用户在等待数据渲染的时候,有可能因为网络速度慢,手机硬件等问题,造成等待时间延长,使得用户体验不好。
之前的做法是放个加载中的图标,而现在是直接根据页面原有元素绘制图形的方式,让用户有种页面就快渲染好的错觉。
参考资料:
https://ext.dcloud.net.cn/plugin?id=256
备注:我是准备应用到项目中,从uniapp的插件市场下载了demo,结果出现一些小问题,在下载下来的demo做了些小修改
加载过程效果图:如图,从图一到图二,最底部多出了一个动态加载的骨架,模拟同一页面多个数据请求(每个请求所需时间不同),
我这边的处理是在每个请求的回调中,先赋值渲染的动态数据,再重新抓取需要绘制的动态元素(因为绘制的元素需要先有数据给它撑开),
最后页面中的请求基本完成的时候,隐藏骨架屏,显示原先的页面
问题:对demo有更好建议的可以提出来哈,相互学习一下
代码如下:
组件
1 <template> 2 <view v-if="show" :style="{width: systemInfo.width + 'px', height: systemInfo.height + 'px', backgroundColor: bgcolor, position: 'absolute', left: 0, top: 0, zIndex: 9998, overflow: 'hidden'}"> 3 <view v-for="(item,rect_idx) in skeletonRectLists" :key="rect_idx + 'rect'" :class="[loading == 'chiaroscuro' ? 'chiaroscuro' : '']" 4 :style="{width: item.width + 'px', height: item.height + 'px', backgroundColor: 'rgb(194, 207, 214)', position: 'absolute', left: item.left + 'px', top: item.top + 'px'}"></view> 5 <view v-for="(item,circle_idx) in skeletonCircleLists" :key="circle_idx + 'circle'" :class="loading == 'chiaroscuro' ? 'chiaroscuro' : ''" 6 :style="{width: item.width + 'px', height: item.height + 'px', backgroundColor: 'rgb(194, 207, 214)', borderRadius: item.width + 'px', position: 'absolute', left: item.left + 'px', top: item.top + 'px'}"></view> 7 <view class="spinbox" v-if="loading == 'spin'"> 8 <view class="spin"></view> 9 </view> 10 </view> 11 </template> 12 13 <script> 14 export default { 15 name: "skeleton", 16 props: { 17 bgcolor: { 18 type: String, 19 value: '#FFF' 20 }, 21 selector: { 22 type: String, 23 value: 'skeleton' 24 }, 25 loading: { 26 type: String, 27 value: 'spin' 28 }, 29 show: { 30 type: Boolean, 31 value: false 32 }, 33 isNodes: { 34 type: Number, 35 value: false 36 } //控制什么时候开始抓取元素节点,只要数值改变就重新抓取 37 }, 38 data() { 39 return { 40 loadingAni: ['spin', 'chiaroscuro'], 41 systemInfo: {}, 42 skeletonRectLists: [], 43 skeletonCircleLists: [] 44 } 45 }, 46 watch: { 47 isNodes (val) { 48 this.readyAction(); 49 } 50 }, 51 mounted() { 52 this.attachedAction(); 53 }, 54 methods: { 55 attachedAction: function(){ 56 //默认的首屏宽高,防止内容闪现 57 const systemInfo = uni.getSystemInfoSync(); 58 this.systemInfo = { 59 width: systemInfo.windowWidth, 60 height: systemInfo.windowHeight 61 }; 62 this.loading = this.loadingAni.includes(this.loading) ? this.loading : 'spin'; 63 }, 64 readyAction: function(){ 65 console.log('子组件readyAction') 66 const that = this; 67 //绘制背景 68 uni.createSelectorQuery().selectAll(`.${this.selector}`).boundingClientRect().exec(function(res){ 69 that.systemInfo.height = res[0][0].height + res[0][0].top; 70 }); 71 72 //绘制矩形 73 this.rectHandle(); 74 75 //绘制圆形 76 this.radiusHandle(); 77 }, 78 rectHandle: function(){ 79 const that = this; 80 81 //绘制不带样式的节点 82 uni.createSelectorQuery().selectAll(`.${this.selector}-rect`).boundingClientRect().exec(function(res){ 83 that.skeletonRectLists = res[0]; 84 }); 85 86 }, 87 radiusHandle(){ 88 const that = this; 89 90 uni.createSelectorQuery().selectAll(`.${this.selector}-radius`).boundingClientRect().exec(function(res){ 91 that.skeletonCircleLists = res[0]; 92 }); 93 } 94 } 95 } 96 </script> 97 98 <style> 99 .spinbox{ 100 position: fixed; 101 display: flex; 102 justify-content: center; 103 align-items: center; 104 height: 100%; 105 width: 100%; 106 z-index: 9999 107 } 108 .spin { 109 display: inline-block; 110 width: 64rpx; 111 height: 64rpx; 112 } 113 .spin:after { 114 content: " "; 115 display: block; 116 width: 46rpx; 117 height: 46rpx; 118 margin: 1rpx; 119 border-radius: 50%; 120 border: 5rpx solid #409eff; 121 border-color: #409eff transparent #409eff transparent; 122 animation: spin 1.2s linear infinite; 123 } 124 @keyframes spin { 125 0% { 126 transform: rotate(0deg); 127 } 128 100% { 129 transform: rotate(360deg); 130 } 131 } 132 133 .chiaroscuro{ 134 width: 100%; 135 height: 100%; 136 background: rgb(194, 207, 214); 137 animation-duration: 2s; 138 animation-name: blink; 139 animation-iteration-count: infinite; 140 } 141 142 @keyframes blink { 143 0% { 144 opacity: .4; 145 } 146 50% { 147 opacity: 1; 148 } 149 100% { 150 opacity: .4; 151 } 152 } 153 154 @keyframes flush { 155 0% { 156 left: -100%; 157 } 158 50% { 159 left: 0; 160 } 161 100% { 162 left: 100%; 163 } 164 } 165 .shine { 166 animation: flush 2s linear infinite; 167 position: absolute; 168 top: 0; 169 bottom: 0; 170 width: 100%; 171 background: linear-gradient(to left, 172 rgba(255, 255, 255, 0) 0%, 173 rgba(255, 255, 255, .85) 50%, 174 rgba(255, 255, 255, 0) 100% 175 ) 176 } 177 </style>