实现一个响应式的瀑布流组件

在图片网站中,通常是使用瀑布流的方式来显示图片,等宽度不等高,这样显得错落有致

今天 使用Vue3实现一个瀑布流组件,并发布为一个npm

  1. grid布局
  2. 支持响应式

参考自react-plock

1、window.matchMedia

window.matchMedia(mediaQueryString)方法返回一个MediaQueryList对象,表示指定css媒体查询字符串的结果

其中参数mediaQueryString表示CSS @media规则的任何媒体查询选项,比如:

  • min-widthmin-heightorientation
ini 复制代码
const media = window.matchMedia("(max-width: 850px)")

MediaQueryList对象中有两个属性:

  1. matches:返回查询结果,是一个布尔值
  2. 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进行显示

需要注意的是:columnsgapmedias的个数要一致

发布npm

做好工程化后,就可以通过发布为npm

地址:vue3-plock

现在可以通过依赖命令来安装了

csharp 复制代码
pnpm add vue3-plock

点击codesandbox查看线上的运行

源码已经上传github,欢迎start 😀😀


参考:

  1. github.com/askides/rea...
  2. developer.mozilla.org/zh-CN/docs/...
相关推荐
逐·風2 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#
Devil枫3 小时前
Vue 3 单元测试与E2E测试
前端·vue.js·单元测试
尚梦3 小时前
uni-app 封装刘海状态栏(适用小程序, h5, 头条小程序)
前端·小程序·uni-app
GIS程序媛—椰子4 小时前
【Vue 全家桶】6、vue-router 路由(更新中)
前端·vue.js
前端青山4 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
毕业设计制作和分享5 小时前
ssm《数据库系统原理》课程平台的设计与实现+vue
前端·数据库·vue.js·oracle·mybatis
程序媛小果5 小时前
基于java+SpringBoot+Vue的旅游管理系统设计与实现
java·vue.js·spring boot
从兄5 小时前
vue 使用docx-preview 预览替换文档内的特定变量
javascript·vue.js·ecmascript
凉辰6 小时前
设计模式 策略模式 场景Vue (技术提升)
vue.js·设计模式·策略模式
清灵xmf7 小时前
在 Vue 中实现与优化轮询技术
前端·javascript·vue·轮询