如何实现封装组件外部的SkeletonLoader/WirefreameLoader



假设我们有一个UserCard组件:

function UserCard(props) {
const { avatarUrl, name, loading } = props
return (
<div className='UserCard'>
<Avatar image={avatarUrl} />
<Name name={name} />
</div>
)
}

现在我们想为它实现一个线框加载。

  • 我们无法设计AvatarName组件的线框副本-它们的作者可能会改变它们的大小和形状,我们不希望我们的骨骼失去同步
  • 我们无法将加载程序插入AvatarName组件中——我们无法控制它们或它们的css
  • 我们可以控制AvatarName之间的空间,并从外部包裹它们
  • AvatarName可能有一些透明部分/边界半径,我们希望在它们的可见部分上绘制骨架动画

理想情况下,我们的Loader的用法应该有点像这样:

function UserCard(props) {
const { avatarUrl, name, loading } = props
return (
<div className='UserCard'>
<Loader loading={loading}>
<Avatar image={avatarUrl} />
</Loader>
<Loader loading={loading}>
<Name name={loading ? 'some text to control the size of the skeleton' : name} />
</Loader>
</div>
)
}

实现这一目标的方法是什么?如果我们有类似的限制,但希望将骨架加载添加到UserCard,该怎么办。从外部来看,我们知道这是一个包装其他div的单个div,我们希望显示一个骨架,而不是每个子div。

最终我想出的最好的办法是:

// Usage:
// @use 'styles/loading';
// .loading {
//   @extend .wireframe;
// }
.wireframe {
background-color: var(--colors-borderColor2);
border-radius: 4px;
position: relative;
color: transparent; // hide text
overflow: hidden;
white-space: nowrap;
> * {
visibility: hidden; // hide children
}
&::after {
display: block;
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transform: translateX(-100%);
background: linear-gradient(
90deg,
transparent,
rgba(255, 255, 255, 0.4),
transparent
);
animation: loading 1.5s infinite;
}
@keyframes loading {
100% {
transform: translateX(100%);
}
}
}

应该包装的组件由css选择器作为目标,并且可以在@extend以下调整骨架形状和大小。

所以我们的UserCard组件看起来是这样的:

import { stylesheet } from 'astroturf'
const styles = stylesheet`
@use 'styles/loading';
.UserCard {
//...
}

.UserCard:global(.loading) {
> * {
@extend .wireframe;

&:first-child {
border-radius: 50%; // make avatar round
}
}
}
`
function UserCard(props) {
const { avatarUrl, name, loading } = props
return (
<div className={cx('UserCard', { loading })>
<Avatar image={avatarUrl} />
<Name name={name} />
</div>
)
}

不理想,但对我有效。

相关内容

  • 没有找到相关文章

最新更新