在图片网站中,通常是使用瀑布流的方式来显示图片,等宽度不等高,这样显得错落有致
今天 使用Vue3
实现一个瀑布流组件,并发布为一个npm
包
grid
布局- 支持响应式
参考自react-plock
1、window.matchMedia
window.matchMedia(mediaQueryString)
方法返回一个MediaQueryList
对象,表示指定css
媒体查询字符串的结果
其中参数mediaQueryString
表示CSS @media
规则的任何媒体查询选项,比如:
min-width
、min-height
、orientation
等
ini
const media = window.matchMedia("(max-width: 850px)")
在MediaQueryList
对象中有两个属性:
matches
:返回查询结果,是一个布尔值media
:媒体查询的字符串
所以,根据matches
我们就可以知道是否命中当前的媒体查询选项
并且,我们还可以通过addEventListener
监听mediaQuery
查询结果的变化
javascript
media.removeEventListener('change', () => {
console.log(media.matches)
});
2、瀑布流组件
响应式控制
根据屏幕大小确定图片的列数
TypeScript
export interface PropConfigType {
gap: number[]; // 行列的间距
columns: number[]; // 不同媒体查询选项对应的列数
medias?: number[]; // 媒体查询选项
}
JavaScript
import { reactive, onUnmounted } from 'vue';
export function useMediaValues(config: PropConfigType) {
const info = reactive({
columns: 1,
gap: 1
});
const { columns, gap, media } = config;
if (!media) {
return setInfo(0);
}
// 循环查询medias
const mediaQueries = media.map(media =>
window.matchMedia(`(min-width: ${media}px)`)
);
const onSizeChange = () => {
// 记录匹配到的哪一个media
let matches = 0;
mediaQueries.forEach(mediaQuery => {
mediaQuery.matches && matches++;
});
// 优先匹配后面的
const idx = Math.min(mediaQueries.length - 1, matches);
setInfo(idx);
};
onSizeChange();
// 监听查询结果
for (let mediaQuery of mediaQueries) {
mediaQuery.addEventListener('change', onSizeChange);
}
onUnmounted(() => {
for (let mediaQuery of mediaQueries) {
mediaQuery.removeEventListener('change', onSizeChange);
}
});
function setInfo(idx: number) {
info.columns = columns[idx];
info.gap = gap[idx];
return info;
}
return info;
}
数据结构
info.columns
计算出瀑布流的数据结构,每列为一个数组,并存储到dataColumns
通过for
循环将fall-row
给渲染出来
当屏幕宽度变化时,会重新计算info.columns
,得到一个新的dataColumms
,渲染等到新的瀑布流界面
vue
// fall-row组件
<div
:style="{
display: 'grid',
rowGap: prop.gap,
gridTemplateColumns: `minmax(0, 1fr)`
}"
>
// 用户的内容通过插槽的形式添加
<slot></slot>
</div>
vue
// water-fall 组件
<div
:style="{
display: 'grid',
alignItems: 'start',
gridColumnGap: info.gap + 'px',
gridTemplateColumns: `repeat(${info.columns}, minmax(0, 1fr))`
}"
>
<fallRow
v-for="(columns, index) in dataColumns"
:key="index"
:gap="info.gap + 'px'"
>
// 此处为作用域插槽
<slot
v-for="(src, index) in columns"
:key="index"
:src="src"
></slot>
</fallRow>
</div>
- 通过作用域插槽将图片的
src
传递给父组件
3、使用
vue
// 导入组件
import { waterFall } from ...
<waterFall
:data="datasource"
:config="{
columns: [1, 2, 3],
gap: [24, 12, 6],
medias: [640, 1024, 1280]
}"
v-slot="slotProps"
>
<img
:src="slotProps.src"
:style="{ width: '100%', height: 'auto' }"
/>
</waterFall>
v-slot="slotProps"
访问父组件的作用域,获取到src
进行显示
需要注意的是:columns
、gap
、medias
的个数要一致
发布npm
包
做好工程化后,就可以通过发布为npm
包
地址:vue3-plock
现在可以通过依赖命令来安装了
csharp
pnpm add vue3-plock
点击codesandbox查看线上的运行
源码已经上传github,欢迎start 😀😀
参考: