乐趣区

关于javascript:实现一个响应式的瀑布流组件

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

明天 应用 Vue3 实现一个瀑布流组件,并公布为一个 npm

  1. grid布局
  2. 反对响应式

参考自react-plock

1、window.matchMedia

window.matchMedia(mediaQueryString)办法返回一个 MediaQueryList 对象,示意指定 css 媒体查问字符串的后果

其中参数 mediaQueryString 示意 CSS @media 规定的任何媒体查问选项,比方:

  • min-widthmin-heightorientation
const media = window.matchMedia("(max-width: 850px)")

MediaQueryList 对象中有两个属性:

  1. matches:返回查问后果,是一个布尔值
  2. media:媒体查问的字符串

所以,依据 matches 咱们就能够晓得是否命中以后的媒体查问选项

并且,咱们还能够通过 addEventListener 监听 mediaQuery 查问后果的变动

media.removeEventListener('change', () => {console.log(media.matches)
});

2、瀑布流组件

响应式管制

依据屏幕大小确定图片的列数

export interface PropConfigType {gap: number[];  // 行列的间距
  columns: number[];  // 不同媒体查问选项对应的列数
  medias?: number[];  // 媒体查问选项}
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,渲染等到新的瀑布流界面

// fall-row 组件

<div
  :style="{
    display: 'grid',
    rowGap: prop.gap,
    gridTemplateColumns: `minmax(0, 1fr)`
  }"
>
  // 用户的内容通过插槽的模式增加
  <slot></slot>
</div>
// 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、应用

// 导入组件
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

当初能够通过依赖命令来装置了

pnpm add vue3-plock

点击 codesandbox 查看线上的运行

源码曾经上传 github,欢送 start 😀😀


参考:

  1. https://github.com/askides/react-plock
  2. https://developer.mozilla.org/zh-CN/docs/Web/API/Window/match…
退出移动版