<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          實(shí)現(xiàn)瀑布流布局,就這幾行代碼?

          共 1294字,需瀏覽 3分鐘

           ·

          2022-02-12 14:18

          瀑布流布局是一種比較流行的頁(yè)面布局方式,表現(xiàn)為參差不齊的多欄卡片。跟網(wǎng)格布局相比,顯得更靈動(dòng),更具藝術(shù)氣息。

          實(shí)現(xiàn)瀑布流布局的方式有多種,比如multi-column布局,grid布局,flex 布局等。但是這些實(shí)現(xiàn)方式都有各自的局限性,代碼也略復(fù)雜。

          其實(shí),有個(gè)最原始、最簡(jiǎn)單,也是兼容性最好的實(shí)現(xiàn)方式,那就是使用絕對(duì)定位。瀑布流布局的元素是一些等寬不等高的卡片,只要根據(jù)元素的實(shí)際寬高計(jì)算出自己的坐標(biāo)位置就行了。

          要計(jì)算坐標(biāo)自然要用到 JavaScript,這就不是純 CSS 方案,對(duì)某些前端極客來(lái)講顯得不那么純粹。不過(guò)只要理清思路了,也用不了幾行代碼。本文就給出最近實(shí)現(xiàn)的一個(gè)版本。

          //?計(jì)算每個(gè)卡片的坐標(biāo)
          export?function?calcPositions({?columns?=?2,?gap?=?7,?elements?})?{
          ??if?(!elements?||?!elements.length)?{
          ????return?[];
          ??}
          ??const?y?=?[];?//上一行卡片的底部縱坐標(biāo)數(shù)組,用于找到新卡片填充位置
          ??const?positions?=?[];?//?每個(gè)卡片的坐標(biāo)數(shù)組
          ??elements.forEach((item,?index)?=>?{
          ????if?(y.length?//?還未填滿(mǎn)一行
          ??????y.push(item.offsetHeight);
          ??????positions.push({
          ????????left:?(index?%?columns)?*?(item.offsetWidth?+?gap),
          ????????top:?0
          ??????});
          ????}?else?{
          ??????const?min?=?Math.min(...y);?//?最小縱坐標(biāo)
          ??????const?idx?=?y.indexOf(min);?//?縱坐標(biāo)最小的卡片索引
          ??????y.splice(idx,?1,?min?+?gap?+?item.offsetHeight);?//?替換成新卡片的縱坐標(biāo)
          ??????positions.push({
          ????????left:?idx?*?(item.offsetWidth?+?gap),
          ????????top:?min?+?gap
          ??????});
          ????}
          ??});
          //?由于采用絕對(duì)定位,容器是無(wú)法自動(dòng)撐開(kāi)的。因此需要計(jì)算實(shí)際高度,即最后一個(gè)卡片的top加上自身高度
          ??return?{?positions,?containerHeight:?positions[positions.length?-?1].top?+?elements[elements.length?-?1].offsetHeight?};
          }

          上面這段代碼的作用就是計(jì)算每個(gè)卡片的lefttop,以及容器的總高度。關(guān)鍵位置都有注釋?zhuān)瑧?yīng)該不難理解。

          有了這幾行核心代碼,要想封裝成瀑布流組件就很容易了。以 Vue 為例,可以這樣封裝:
          MasonryLite.vue

          <template>
          ??<div?class="masonry-lite">
          ????<slot>slot>
          ??div>
          template>
          <script>
          import?{?calcPositions?}?from?'./index.js';
          export?default?{
          ??name:?'MasonryLite',
          ??props:?{
          ????gap:?{
          ??????type:?Number,
          ??????default:?12,
          ????},
          ????columns:?{
          ??????type:?Number,
          ??????default:?2,
          ????},
          ??},
          ??data()?{
          ????return?{};
          ??},
          ??mounted()?{
          ????this.doLayout();
          ??},
          ??methods:?{
          ????doLayout()?{
          ??????const?children?=?[...this.$el.querySelectorAll('.masonry-item')];
          ??????if?(children.length?===?0)?{
          ????????return;
          ??????}
          ??????const?{?positions,?containerHeight?}?=?calcPositions({
          ????????elements:?children,
          ????????columns:?this.columns,
          ????????gap:?this.gap,
          ??????});
          ??????children.forEach((item,?index)?=>?{
          ????????item.style.cssText?=?`left:${positions[index].left}px;top:${positions[index].top}px;`;
          ??????});
          ??????this.$el.style.height?=?`${containerHeight}px`;
          ????},
          ??},
          };
          script>
          <style?lang="scss"?scoped>
          .masonry-lite{
          ??position:?relative;
          }
          .masonry-item?{
          ??position:?absolute;
          }
          style>

          使用組件:

          <MasonryLite>
          ??<div?class="product-card masonry-item"?v-v-for="(item,?index)?in?items"?:key="index">
          ????<img?:src="item.imageUrl"?/>
          ????<header>{{?item.title?}}header>
          ??div>
          MasonryLite>

          不過(guò)這樣其實(shí)還會(huì)有點(diǎn)問(wèn)題,就是doLayout的執(zhí)行時(shí)機(jī)。因?yàn)樵摲桨富诮^對(duì)定位,需要元素在渲染完成后才能獲取到實(shí)際寬高。如果卡片內(nèi)有延遲加載的圖片或者其他動(dòng)態(tài)內(nèi)容,高度會(huì)發(fā)生變化。這種情況下就需要在DOM更新后主動(dòng)調(diào)用一次doLayout重新計(jì)算布局。

          如果大家有更好的實(shí)現(xiàn)方案,歡迎交流!

          代碼倉(cāng)庫(kù):https://github.com/kaysonli/masonry-lite
          npm 包:masonry-lite

          如果覺(jué)得對(duì)你有幫助,幫忙點(diǎn)個(gè)不要錢(qián)的star。

          往期干貨

          ?26個(gè)經(jīng)典微信小程序+35套微信小程序源碼+微信小程序合集源碼下載(免費(fèi))

          ?干貨~~~2021最新前端學(xué)習(xí)視頻~~速度領(lǐng)取

          ?前端書(shū)籍-前端290本高清pdf電子書(shū)打包下載


          點(diǎn)贊和在看就是最大的支持??


          瀏覽 73
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  超碰国产成人 | 国产操逼网av | 大香蕉久久依人网站 | 精品六区 | 久久免费视频热16 |