Taro3实现自动生成骨架屏组件

4,766 阅读1分钟

起始

奖励自己个Taro骨架屏呗 自动生成的欧~

预览

组件

  1. Skeleton.ts
  import React, { FC, useState, useEffect } from 'react'
  import Taro from '@tarojs/taro'
  import { View } from '@tarojs/components'
  import { SkeletonProps } from './data.d'
  import './Skeleton.scss'

  const Skeleton: FC<SkeletonProps> = props => {
      const { selector = 'skeleton', loading = true } = props
      const [radiusList, setRadiusList] = useState([])
      const [rectList, setRectList] = useState([])
      const [loadingTest, setLoadingTest] = useState(true)
	  
      /**
       * 本地延迟模拟
       */
      useEffect(() => {
          setTimeout(() => {
              setLoadingTest(false)
          }, 2000)
      }, [])
      
	  /**
       * 等待父页面渲染后获取生成骨架屏
       */
      Taro.eventCenter.once(Taro?.Current.router.onReady, () => {
          initSkeleton()
      })

      /**
       * 初始化请求
       */
      const initSkeleton = () => {
          getGraphList(selector, `${selector}-radius`).then((res: any) => {
              setRadiusList(res)
          })
          getGraphList(selector, `${selector}-rect`).then((res: any) => {
              setRectList(res)
          })
      }
      /**
       * 选择器获取节点
       */
      const getGraphList = (ancestor, descendant) => {
          return new Promise((resolve, reject) => {
              if (process.env.TARO_ENV === 'weapp') {
                  Taro.createSelectorQuery().selectAll(`.${ancestor} >>> .${descendant}`)
                  .boundingClientRect().exec(rect => {
                          resolve(rect[0])
                      })
              }
              else {
                  Taro.createSelectorQuery().selectAll(`.${ancestor} .${descendant}`)
                  .boundingClientRect().exec(rect => {
                      resolve(rect[0])
                  })
              }     
          })
      }

      return (
          <View>
              {loadingTest &&
                  <View className='SkeletonCmpt'>
                      {radiusList.map((radiusItem: any) => 
                          <View
                              className='skeleton skeleton-radius skeleton-animate-gradient'
                              style={{width: `${radiusItem.width}px`, height: `${radiusItem.height}px`, top: `${radiusItem.top}px`, left: `${radiusItem.left}px`}}
                          />
                      )}
                      {rectList.map((rectItem: any) => 
                          <View
                              className='skeleton skeleton-animate-gradient'
                              style={{width: `${rectItem.width}px`, height: `${rectItem.height}px`, top: `${rectItem.top}px`, left: `${rectItem.left}px`}}
                          />
                      )}
                  </View>
              }
          </View>
      )
  }
  export default Skeleton
  1. Skeleton.scss
  .SkeletonCmpt {
      @include position-way-tl(absolute, 0, 0);
      width: 100vw;
      height: 100vh;
      background-color: #fff;
      overflow: hidden;
      z-index: 1000;
      .skeleton {
          position: absolute;
          background-color: #f2f3f5;
          &-radius {
              border-radius: 50%;
          }
          &-animate {
              &-blink {
                  animation: skeleton-blink 1.5s ease-in-out infinite;
              }
              &-elastic {
                  animation: skeleton-elastic 2s ease-in-out infinite;
              }
              &-gradient {
                  background-image: linear-gradient(90deg, #f2f3f5 25%, #e6e6e6 37%, #f2f3f5 63%);
                  list-style: none;
                  background-size: 400% 100%;
                  background-position: 100% 50%;
                  animation: skeleton-gradient 1.8s ease-in-out infinite;
              }
          }
      }


      @keyframes skeleton-blink {
          50% {
              opacity: .6;
          }
      }
      @keyframes skeleton-elastic {
          50% {
              transform: scaleX(.3);
          }
      }
      @keyframes skeleton-gradient {
          0% {
            background-position: 100% 50%;
          }
          100% {
            background-position: 0 50%;
          }
      }
  }
  1. data.d.ts
  /**
   * Skeleton.props 参数类型
   * @export
   * @interface SkeletonProps
   */
  export interface SkeletonProps {
      selector?: string
      loading: boolean
  }

使用

  <Skeleton selector='skeleton' loading={loading} />
  <View className='skeleton'>
      <View className='mainBox'>
          <Image className='avatar skeleton-radius' src={homeInfo.avatar} />
          <Text className='name skeleton-rect'>{homeInfo.name}</Text>	// 文字可以设置name.skeleton-rect{min-width: 属性占位}
          <View className='box skeleton-rect'></View>
          <Test />	// 组件中也可以添加className='skeleton-rect'
      </View>
  </View>