欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

如何实现同等间隙的卡片布局

程序员文章站 2022-03-12 19:56:32
在列表展示中,经常会使用卡片的内容展示形式,为了美观,常常要求各卡片间的间隙是一致的。 卡片内容不一样可能高度不等,但一般来说为了整体的一致性,会限制每个卡片的宽高都相等。 本文就基于宽高一致的多个卡片,在不同屏幕大小下,每行卡片数量可能有调整,考量如何实现等间隙的布局。 点我预览 放置一张张卡片项 ......

在列表展示中,经常会使用卡片的内容展示形式,为了美观,常常要求各卡片间的间隙是一致的。

卡片内容不一样可能高度不等,但一般来说为了整体的一致性,会限制每个卡片的宽高都相等。

本文就基于宽高一致的多个卡片,在不同屏幕大小下,每行卡片数量可能有调整,考量如何实现等间隙的布局。

点我预览

如何实现同等间隙的卡片布局

 

放置一张张卡片项,为了设置间距,最常见的就是直接使用一个特定的margin值了,这种方式虽然可以(通过精确计算后确实也可以)

直接设置一个间距,比如统一 margin-left 和 margin-bottom都为 20px ,并不能保证每行最后一个卡片之后的间距是20px

关于如何定这个 margin的值,需要通过一个规则来计算,这个后文再说明

 

设置同等间距,常用的还有 flex布局中的 justify-content: space-between,可以定义各子项目以相同间距布局,但不好处理左右子项目与边框的间距。 space-around这个就更用不得了,会使得左右子项目右margin == 左margin * 2

所以最终还是回到使用margin值来设置,通过一个可用的规则,来保证间距是一致的。

 

先把基本结构搭上

<div class="container">
    <h2>项目列表</h2>
    <ul class="proj-items"></ul>
</div>

<!-- 模板结构 -->
<script type="text/template" id="proj-item-tpl">
    <li class="proj-item">
        <a href="#/p/{{projectid}}">
            <h3 class="proj-item__title">{{projectname}}</h3>
            <p class="proj-item__author">{{author}}</p>
        </a>
    </li>
</script>

js生成n个项目

        function addevent(elem, type, handler) {
            elem.addeventlistener(type, handler, false);
        }

        function qs(selector) {
            return document.queryselector(selector);
        }

        function qsa(selectors) {
            return document.queryselectorall(selectors);
        }

        var mockdata = (function(num) {
            var data = [];
            
            for (var i = 1; i <= num; ++i) {
                data.push({
                    projectid: i,
                    projectname: '项目' + i,
                    author: '张大大'
                });
            }
            
            return data;
        })(8);
        
        var itemtpl = qs('#proj-item-tpl').innerhtml;
        var itemsdom = qs('.proj-items');
        
        /**
         * 渲染数据
         * @param  {[type]} data [description]
         * @return {[type]}      [description]
         */
        function renderlist(data) {
            var html = '';
            var fragment = document.createdocumentfragment();

            data.foreach(function(item) {
                var divtemp = document.createelement('div');

                // 模板替换
                divtemp.innerhtml = itemtpl.replace(/{{(\w+)}}/g, function(input, match) {
                    return match ? item[match] || '' : '';
                });

                fragment.appendchild(divtemp.firstelementchild);
            });

            // 渲染
            itemsdom.appendchild(fragment);
        }
       
        renderlist(mockdata);

把基础样式放上,这里我们先指定一个特定的itemmargin值为20px

$itemmargin: 20px;
$itemwidth: 130px;
$itemheight: 150px;

.container {
    margin: 20px auto;
    width: 450px;
    background-color: #f2f2f2;
    color: #666;
    
    h2 {
        margin: 20px;
        padding-top: 20px;
        font-size: 20px;
    }
}

.proj-items {
    display: flex;
    flex-wrap: wrap;
    /* justify-content: space-between; */
    padding: 0;
    list-style: none;
    
    &:after {
        content: "";
        display: block;
        flex-grow: 99999;
    }
}

.proj-item {
    margin-left: $itemmargin;
    margin-bottom: $itemmargin;
    width: $itemwidth;
    height: $itemheight;
    background-color: #fff;
    border-radius: 3px;
    text-align: center;
    
    &:hover {
        box-shadow: 0 0 20px #ddd;
    }
    
    a {
        display: block;
        padding: 15px;
        height: 100%;
        color: #666;
        text-decoration: none;
    }
    
    &__title {
        margin-top: 0;
        font-size: 16px;
    }
    
    &__author {
        font-size: 12px;
    }
}

如何实现同等间隙的卡片布局

 

可以看到,每行最后一个间距不一致了,所以不能简单的写个margin值

再来看看设置 space-between的时候

.proj-items {
    justify-content: space-between;
    ...
}

.proj-item {
    /* margin-left: $itemmargin; */
    margin-bottom: $itemmargin;
    ...
}

如何实现同等间隙的卡片布局

 

 看来并不够强大

如果看得仔细,应该能看到项目7和8是挨在一起的,为何没有间距呢

其一是因为没有margin-left值,其二是在项目列表后放了一个坑来占位,防止最后一行项目过少时 space-between的值太大了

把这个撤掉看看这个影响

    &:after {
        content: "";
        display: block;
        flex-grow: 99999;
    }

如何实现同等间隙的卡片布局

 

 

还是把目光投向margin值的设定规则吧

在设计一个页面布局时,至少已经确定了xx页面大小的情况下,容器宽度应该设置为多少(比如为1200px),每行放n个项目,项目的宽高是多少

有了这些指标(也必须有这些指标),我们就可以用来计算margin值了

containerwidth == n * itemwidth + (n + 1) * itemmargin

得出

itemmargin = (containerwidth - n * itemwidth) / (n + 1)

代入这里的情况,containerwidth 450px,itemwidth 130px,每行 3个,即可得出 itemmargin 正好为 15px 

如何实现同等间隙的卡片布局

 

有了某种特定情况下的布局规则之后,接下来还要考虑不同屏幕大小的情况下,怎么调整这个margin值

这个需要结合媒体查询来设定,同时相应的计算规则也可以通过scss来处理

第一种情况是每行3个,n只可能为整数,即可推算出需要处理的临界值为1 2 3 4 5 6 ... 这些整数值

加入n为4,如果要保证 itemmargin值15px在各种情况下都相等,计算可得 容器宽度containerwidth值 为 595px

同理求得 n是5时为 740px ,n是2时为 305px

当然,如果觉得这个containerwidth值不太好看,也可以自己定义,比如 n是4的时候设置为 600px,代入公式那么 itemmargin值为16px。

为了保证各种请下间距都相等,我个人就不推荐这么干了

如何实现同等间隙的卡片布局

如何实现同等间隙的卡片布局

 

通过上述的规则计算,我们可以得出每行项目数量递增时的容器宽度临界值。把这些临界值放在媒体查询里面配置,即可方便地实现这种布局的自适应。

/* 这两个为初始就确定的基准值 */
$containerwidth: 305px;
$itemmargin: 15px;

$itemwidth: 130px;
$itemheight: 150px;

/* 每行项目数量为itemnum时的容器宽度 */
@function getcontainerwidth($itemnum) {
    @return $itemnum * $itemwidth + ($itemnum + 1) * $itemmargin; 
}

/* 配置各个页面宽度下的容器宽度(应用) */
@mixin adjustcontainerwidth(
    $from: 2,
    $to: 5
) {
    @for $i from $from through $to {
        $minwidth: getcontainerwidth($i);
        $maxwidth: getcontainerwidth($i + 1);

        @media only screen and (min-width: $minwidth) and (max-width: $maxwidth) {
            .container {
                width: $minwidth;
            }
        }
    }
}

.container {
    margin: 20px auto;
    width: $containerwidth;
    background-color: #f2f2f2;
    color: #666;
    
    h2 {
        margin: 20px;
        padding-top: 20px;
        font-size: 20px;
    }
}

@include adjustcontainerwidth(
    $from: 1,
    $to: 7
);

即可实现各个页面大小下的自适应效果

完整的css部分

如何实现同等间隙的卡片布局
 1 /* 这两个为初始就确定的基准值 */
 2 $containerwidth: 305px;
 3 $itemmargin: 15px;
 4 
 5 $itemwidth: 130px;
 6 $itemheight: 150px;
 7 
 8 /* 每行项目数量为itemnum时的容器宽度 */
 9 @function getcontainerwidth($itemnum) {
10     @return $itemnum * $itemwidth + ($itemnum + 1) * $itemmargin; 
11 }
12 
13 /* 配置各个页面宽度下的容器宽度(应用) */
14 @mixin adjustcontainerwidth(
15     $from: 2,
16     $to: 5
17 ) {
18     @for $i from $from through $to {
19         $minwidth: getcontainerwidth($i);
20         $maxwidth: getcontainerwidth($i + 1);
21 
22         @media only screen and (min-width: $minwidth) and (max-width: $maxwidth) {
23             .container {
24                 width: $minwidth;
25             }
26         }
27     }
28 }
29 
30 .container {
31     margin: 20px auto;
32     width: $containerwidth;
33     background-color: #f2f2f2;
34     color: #666;
35     
36     h2 {
37         margin: 20px;
38         padding-top: 20px;
39         font-size: 20px;
40     }
41 }
42 
43 @include adjustcontainerwidth(
44     $from: 1,
45     $to: 7
46 );
47 
48 .proj-items {
49     display: flex;
50     flex-wrap: wrap;
51     padding: 0;
52     list-style: none;
53 }
54 
55 .proj-item {
56     margin-left: $itemmargin;
57     margin-bottom: $itemmargin;
58     width: $itemwidth;
59     height: $itemheight;
60     background-color: #fff;
61     border-radius: 3px;
62     text-align: center;
63     
64     &:hover {
65         box-shadow: 0 0 20px #ddd;
66     }
67     
68     a {
69         display: block;
70         padding: 15px;
71         height: 100%;
72         color: #666;
73         text-decoration: none;
74     }
75     
76     &__title {
77         margin-top: 0;
78         font-size: 16px;
79     }
80     
81     &__author {
82         font-size: 12px;
83     }
84 }
view code