今天我们来用原生js实现一个骨架屏的效果,效果如下:
首先思考如何实现
思考实现方式
骨架屏的原理是在数据没加载出来的时候,使用滚动的背景颜色去替代,等到加载完毕后则显示对应内容
那么我们的核心就是实现一个.skeleton
的样式,当这个样式出现的时候,就通过animation
去开启一个背景色无限滚动的动画,数据加载完毕后则将这个类名去除即可
思路还是比较简单的,我们先搭建一个整体结构,将数据都写死看看效果单的,我们先搭建一个整体结构,将数据都写死看看效果e单的,我们先搭建一个整体结构,将数据都写死看看效果单的,我们先搭建一个整体结构,将数据都写死看看效果e先
静态结构
<div class="card"><<img src="https://unsplash.com/photos/EaB4Ml7C7fE/download?ixid=MnwxMjA3fDB8MXxzZWFyY2h8MTN8fGNvZGV8ZW58MHx8fHwxNjYwMzQwOTQ4&force=true"alt=""/></header><main><h3>I love coding!</h3><p>Talk is cheap. Show me the code.</p><section class="author"><div class="author-avatar"><img src="https://pixabay.com/get/g9ac59ef391ebfbe7b3303ed0278986a31783bedd580d5e097980ed990d9bf646b5dc44932b18fd633df1813686e4f809.png?attachment=" alt="" /></div><div class="author-info"><!-- 名字 --><strong>Plasticine</strong><!-- 日期 --><small>Aug 13, 2022</small></div></section></main" style="margin: auto" />
</div>
再写一些基础样式
img {height: 100%;width: 100%;object-fit: cover;
}.card {width: 350px;border-radius: 10px;background-color: white;overflow: hidden;box-shadow: 5px 5px 10px 10px rgba(255, 255, 255, 0.2);
}.card header {height: 200px;
}.card main {padding: 30px;
}.card main h3 {margin: 0;
}.card main p {color: gray;
}.author {display: flex;align-items: center;gap: 10px;
}.author .author-avatar {height: 40px;width: 40px;border-radius: 50%;overflow: hidden;
}.author .author-info {display: flex;flex-direction: column;gap: 5px;width: 100px;
}.author .author-info small {color: gray;
}
现在的效果如下:
骨架屏特效
现在就可以尝试添加骨架屏特效了,骨架屏特效本身就只是一个背景色向右流动的效果,所以我们需要一个渐变色背景,然后设置一个动画让背景色的background-position
不断向右移动,就可以实现骨架屏的效果
对应的css
代码如下:
.skeleton {background: linear-gradient(to right,#f6f7f8 0%,#edeef1 10%,#f6f7f8 20%,#f6f7f8 100%);background-size: 200% 100%;animation: flow 1s linear infinite;
}@keyframes flow {0% {background-position: 50% 0;}100% {background-position: -150% 0;}
}
那么有了这个骨架屏特效的代码,我们还需要看看其效果是否真的和我们预期中一样呢?
可以先把html
中的内容注释掉,只保留框架部分,模拟一下数据还没加载时候的效果,然后再在需要应用骨架屏特效的地方加上.skeleton
类名
我们要应用骨架屏特效的地方有背景图片、卡片标题、卡片内容、作者头像、作者姓名、留言日期,所以在这些地方加上.skeleton
类名
<div class="card"><<img src="https://unsplash.com/photos/EaB4Ml7C7fE/download?ixid=MnwxMjA3fDB8MXxzZWFyY2h8MTN8fGNvZGV8ZW58MHx8fHwxNjYwMzQwOTQ4&force=true"alt=""/> --></header><main><h3 class="skeleton"><!-- I love coding! --></h3><p class="skeleton"><!-- Talk is cheap. Show me the code. --></p><section class="author"><div class="author-avatar skeleton"><!-- <imgsrc="https://pixabay.com/get/g9ac59ef391ebfbe7b3303ed0278986a31783bedd580d5e097980ed990d9bf646b5dc44932b18fd633df1813686e4f809.png?attachment="alt=""/> --></div><div class="author-info"><!-- 名字 --><strong class="skeleton"><!-- Plasticine --></strong><!-- 日期 --><small class="skeleton"><!-- Aug 13, 2022 --></small></div></section></main" style="margin: auto" />
</div>
再来看看效果:
设置文本元素占位符作为骨架屏填充
咦?生效是生效了,但是只有头部背景图和作者头像有效果,而文字部分全都没效果了,这是为啥呢?
这是因为文本元素中没有文本的时候,它没有自己的宽高,那设置background
属性自然也是不会生效的,所以我们需要给它添加一个用于占位的元素,只要有一个字符,就能够充满当前行了,这里我们就填充一个
空格占位符吧
<div class="card"><<img src="https://unsplash.com/photos/EaB4Ml7C7fE/download?ixid=MnwxMjA3fDB8MXxzZWFyY2h8MTN8fGNvZGV8ZW58MHx8fHwxNjYwMzQwOTQ4&force=true"alt=""/> --></header><main><h3 class="skeleton"> <!-- I love coding! --></h3><p class="skeleton"> <!-- Talk is cheap. Show me the code. --></p><section class="author"><div class="author-avatar skeleton"><!-- <imgsrc="https://pixabay.com/get/g9ac59ef391ebfbe7b3303ed0278986a31783bedd580d5e097980ed990d9bf646b5dc44932b18fd633df1813686e4f809.png?attachment="alt=""/> --></div><div class="author-info"><!-- 名字 --><strong class="skeleton"> <!-- Plasticine --></strong><!-- 日期 --><small class="skeleton"> <!-- Aug 13, 2022 --></small></div></section></main" style="margin: auto" />
</div>
现在的效果如下:
可以看到这样就行了,那么接下来我们就通过js
去模拟数据加载,加载完成后,将数据插入到对应元素中,并将.skeleton
样式去除
js模拟数据加载效果
为了方便js
获取对应元素,我们给应用了骨架屏特效的元素起一个语义化的id
<div class="card"><<img src="https://unsplash.com/photos/EaB4Ml7C7fE/download?ixid=MnwxMjA3fDB8MXxzZWFyY2h8MTN8fGNvZGV8ZW58MHx8fHwxNjYwMzQwOTQ4&force=true"alt=""/> --></header><main><h3 class="skeleton" id="card-title"> <!-- I love coding! --></h3><p class="skeleton" id="card-content"> <!-- Talk is cheap. Show me the code. --></p><section class="author"><div class="author-avatar skeleton" id="card-author-avatar-container"><!-- <imgsrc="https://pixabay.com/get/g9ac59ef391ebfbe7b3303ed0278986a31783bedd580d5e097980ed990d9bf646b5dc44932b18fd633df1813686e4f809.png?attachment="alt=""/> --></div><div class="author-info"><!-- 名字 --><strong class="skeleton" id="card-author-name"> <!-- Plasticine --></strong><!-- 日期 --><small class="skeleton" id="card-author-date"> <!-- Aug 13, 2022 --></small></div></section></main" style="margin: auto" />
</div>
现在就可以用js
去模拟数据加载效果啦
(() => {const skeletonEls = {oHeaderImgContainer: document.getElementById("header-img-container"),oCardTitle: document.getElementById("card-title"),oCardContent: document.getElementById("card-content"),oCardAuthorAvatarContainer: document.getElementById("card-author-avatar-container"),oCardAuthorName: document.getElementById("card-author-name"),oCardAuthorDate: document.getElementById("card-author-date"),};const init = () => {const fetchData = () => {setTimeout(() => {const data = {headerImg: `<imgsrc="https://unsplash.com/photos/EaB4Ml7C7fE/download?ixid=MnwxMjA3fDB8MXxzZWFyY2h8MTN8fGNvZGV8ZW58MHx8fHwxNjYwMzQwOTQ4&force=true"alt=""/>`,cardTitle: "I love coding<img src="https://pixabay.com/get/g9ac59ef391ebfbe7b3303ed0278986a31783bedd580d5e097980ed990d9bf646b5dc44932b18fd633df1813686e4f809.png?attachment="alt=""/>`,cardAuthorName: "Plasticine",cardAuthorDate: "Aug 13, 2022",};// 插入加载到的数据skeletonEls.oHeaderImgContainer.innerHTML = data.headerImg.trim();skeletonEls.oCardTitle.innerHTML = data.cardTitle;skeletonEls.oCardContent.innerHTML = data.oCardContent;skeletonEls.oCardAuthorAvatarContainer.innerHTML =data.cardAuthorAvatar.trim();skeletonEls.oCardAuthorName.innerHTML = data.cardAuthorName;skeletonEls.oCardAuthorDate.innerHTML = data.cardAuthorDate;// 移除 `.skeleton` 类名从而 移除骨架屏特效for (const el of Object.values(skeletonEls)) {el.classList.remove("skeleton");}}, 3000);};fetchData();};init()" style="margin: auto" />
})();
最终效果就像开头中的那样,大功告成!
最后
整理了75个JS高频面试题,并给出了答案和解析,基本上可以保证你能应付面试官关于JS的提问。
有需要的小伙伴,可以点击下方卡片领取,无偿分享