Hello, 各位勇敢的小伙伴, 大家好, 我是你们的嘴强王者小五, 身体健康, 脑子没病.
本人有丰富的脱发技巧, 能让你一跃成为资深大咖.
一看就会一写就废是本人的主旨, 菜到抠脚是本人的特点, 卑微中透着一丝丝刚强, 傻人有傻福是对我最大的安慰.
欢迎来到
小五
的随笔系列
之重温新手村项目 -- 图片瀑布流
.
前言
本文目标意在实现横向和纵向图片瀑布流;横向采用 Flex布局
,纵向则提供Absolute绝对定位
和 Grid布局
两种解决方案;数据上采用真实的接口请求,图片为爬虫爬取后存库;优化上做了懒加载和防抖,减少资源消耗的同时增强用户体验。
演示网址:
效果图:
tips:图片为爬虫爬取【 来源为 wallhaven.cc 】,过滤大小为200K,实际工作中可存取不同规格的图片,瀑布流时展示小图,预览在加载高清大图。
基础拾遗
此部分内容为本文所需知识点,如需扩充,请各位看官自行查阅相关资料
懒加载
瀑布流是一个大量加载图片的需求;一次性加载的话,浏览器无法一口气处理掉全部任务,会造成卡顿、白屏等现象,且浪费资源。
懒加载是对图片加载时机的优化,即图片进入视野后,在进行加载;
🤔 思路如下:
/* --- 可视区域高度 --- **/
winHeight = window.innerHeight
/* --- 页面滑动距离 --- **/
scrollTop = document.body.scrollTop || document.documentElement.scrollTop
/* --- 元素顶部到浏览器顶部的距离 --- **/
offsetTop = imageNode.offsetTop
/* --- 加载时机 --- **/
offsetTop < scrollTop + winHeight
/* --- 加载图片 --- **/
imageNode.src = imageNode.getAttribute('data-src')
节流与防抖
笔者在该项目中仅用了基础的防抖,此处,节流与防抖的优化做知识扩充;
👉 节流
核心思想:第一个说的算
所谓节流,是指一段时间内,无视后来产生的回调请求;
function throttle(fn, interval) {
let last = 0; // 上次回调时间戳
return function () {
let now = +new Date(); // 本次回调时间戳
if (now - last >= interval) { // 本次 - 上次 >= 时间间隔,则触发回调
last = now; // 重新赋值
fn.apply(this, arguments); // 执行函数
}
}
}
👉 防抖
核心思想:最后一个说的算
所谓防抖,是指一段时间内,若产生新的回调,则重新计时;
function debounce(fn, delay) {
let timer = null; // 初始化
return function () {
if (timer) clearTimeout(timer); // 在 delay 时间内重复调用,清除定时器
timer = setTimeout(function () { // 设置定时器
fn.apply(this, arguments); // 触发函数
}, delay);
}
}
👉 节流优化防抖
避免频繁操作却迟迟没有响应,打造一个有底线的防抖
function debounce(fn, delay) {
let last = 0;
let timer = null;
return function () {
let now = +Date.now();
if (now - last >= delay) {
last = now;
fn.apply(this, arguments);
} else {
if (timer) clearTimeout(timer);
timer = setTimeout(function () {
last = now;
fn.apply(this, arguments);
}, delay);
}
}
}
Grid 网格布局
tips:这里仅针对项目中用到的知识点加以说明
以上述选中元素为例,上代码:
.grid-wrapper {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
grid-auto-rows: 10px;
grid-auto-flow: row dense;
gap: 0 10px;
}
.grid-item {
width: 100%;
height: auto;
grid-row-start: auto;
grid-row-end: span 22;
}
.grid-item img {
object-fit: cover;
width: 100%;
height: auto;
background-color: #eee;
box-sizing: border-box;
border: 1px solid #ccc;
}
- grid-template-columns 和 grid-template-rows
用于设置行高和列宽,如 grid-template-columns: 200px 100px 200px;
表示分为3列,分别为 200px 100px 和 200px;
-> repeat(重复次数, 子元素宽度)
-> auto-fit:宽度固定,数量自适应
-> minmax(150px, 1fr) 最小宽度150px,最大宽度1fr(类比 flex: 1),在该区间内自适应
- grid-auto-rows、grid-row-start、grid-row-end
/* 父元素 **/
grid-auto-rows: 10px;
/* 子元素 **/
grid-row-start: auto;
grid-row-end: span 22; // 这里单元格数量需设置为整数
代表每个单元格为10px,子元素起始位置自适应,高度占据22个单元格高度,即220px
tips:设置10px是为了让截图的网格更清晰,实际操作中,笔者设置的最小单元格为1px
- grid-auto-flow
该属性为排列方式,设置为row即横向排列;第二个参数dense表示尽可能填充,减少空白(相当于absolute布局的插入方式,详见下文);
- gap
行间距和列间距,gap: 0 10px;
表示列间距为 10px
功能实现
接口:
tips:图片来源为 wallhaven.cc,获取方式为爬虫爬取
数据格式:
data = [
{
id: 1,
image_url: "xxx",
size: "xxx",
title: "xxx",
width: "xxx",
height: "xxx"
},
...
]
横向瀑布流
特点:定高不定宽
思路:Flex布局,利用 flex-wrap
换行,利用 flex-grow
分配剩余空间,达到自适应的目地。
纵向瀑布流
特点:定宽不定高
提供两种思路,分别为 Absolute绝对定位 和 Grid网格布局
Absolute 方案
目标:
-
为每个元素追加 top 和 left;
-
由于 absolute 脱离文档流,故需为父元素追加高度;
✍️ 子元素宽度计算
第一步,先来确定每个子元素的宽度和列数
这里为了效果更好,我们来模拟上文提到的minmax
两个已知的设定值:【gap 间距】【baseWidth 基础宽度】
由此可列出以下信息:
-
n * (baseWidth + x) + (n + 1) * gap = width
,其中n为列数,x为分配长度,width为屏幕宽度 -
n 为正整数
-
x > 0 & x < baseWidth
-
在符合上述条件的基础上,让n最大化
我们将n代入求x,找最优解,代码如下:
✍️ 布局
已知子元素宽高及列数后,我们为其追加top、left属性
先来为第一排元素追加属性,如下:
itemNode.style.top = `${gap}px`;
itemNode.style.left = `${gap + i * (itemWidth + gap)}px`;
其中gap为间距;itemWidth为刚刚计算出的子元素宽度;i为循环的index,即第几个元素;
❓ 第二排元素的top该如何确定呢
👉 根据每列的高度计算,我们定义一个长度为列数的heights数组,用于存放每列的高度
❓ 顺序排列会如何
👉 图片长短不一,会导致一些列很长,一些列很短,如下图
故每次都应将元素推到最小高度的一列,已达到视觉效果最佳
实现如下:
Math.max(...heights)
即为对父元素追加的高度
Grid 方案
思路在基础拾遗部分已逐一讲解,其中 grid-row-end 需通过计算获得;dense 可做到优先最短高度填充;
其余代码片段
懒加载
计算子元素高度
图片错误处理
防抖
结语
-
图片过大时,应存储不同尺寸图片,瀑布流时展示小图,预览时展示大图
-
absolute 方案未作 resize 处理,大家可自行添加(笔者将其用在移动端,将 Grid 方案用在 PC 端)
-
图片的宽高比是必要的,否则无法在图片加载前呈现良好的效果