容易忽视的flex属性align-content

本文通过一位开发者Tom的视角,详细介绍了在Flex布局中如何理解和使用align-content属性。Tom通过实践发现,align-content属性只有在设置明确的布局方向并形成多根轴线时才会生效。在横向主轴(flex-direction: row)时,align-content影响的是竖直方向的对齐,而在纵向交叉轴(flex-direction: column)时,则体现在水平方向。他还揭示了align-content: stretch属性在子项未设置高度或设置为auto时才会拉伸填充容器。通过这一系列实验,Tom对align-content有了更深入的理解,并鼓励读者探索更多Flex布局的属性。

鄢栋:coder & game player。爱健身,爱自由, 自律得(二声)自由。

背景

近期做项目,写页面的 css 写的太多了,基本上都要用到 flex 布局, 而我所用到的 flex 布局中的属性很有限, 基本只会用到flex-direction, justify-content, align-items这三个容器属性, 最多涉及图片被挤压了, 使用一下子项的 flex-shrink: 0;来防止图片被挤压。

就当我一边听着音乐一边没有感情的敲打着justify-content: space-between;的时候, 我的脑袋瓜子好像有两个小人在吵架:Jerry: 就不能用一下其他没用过的 flex 属性吗!Tom: 又不是不能用!一把梭多快乐!Jerry: 朽木不可雕也!Tom: 朽木仍可灼!Jerry: ...

回过神来,Tom 貌似觉得 Jerry 说的有些道理, 多个方式多条路, 实在不行写出来让别人琢磨去(看不懂,<-_<-)。

flex 容器属性之 align-content

关于 flex 的容器属性、项目属性这里就不在赘述了, 网上有很多讲的很清楚的文章,推荐阮一峰-flex 布局巧了, Tom 就是看了阮老师的文章,看到 align-content 的时候, 它迷糊了。咋个意思?虽然画图了, 但是没有实践, 就等于看着健身视频以为就能瘦是一样的道理!0b38ab1ac80929289b17fa3132ebe640.png

于是, Tom 开始开启了学习 align-content.

阮老师说:"align-content属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。"a9661bc09803308ebaac72ef4d20760e.png

Tom 思考着, flex 布局有主轴和交叉轴, 也就是 flex 布局默认的两根轴。那怎么才算是定义了多根轴线呢?经过 Tom 反复实践, 终于发现了规律, 原来需要显式的定义一下布局方向, 比如:

<div class="flex-container">
  <div class="flex-item flex-item-1">1</div>
  <div class="flex-item flex-item-1">1</div>
  <div class="flex-item flex-item-2">2</div>
  <div class="flex-item flex-item-2">2</div>
  <div class="flex-item flex-item-3">3</div>
  <div class="flex-item flex-item-3">3</div>
  <div class="flex-item flex-item-4">4</div>
  <div class="flex-item flex-item-4">4</div>
  <div class="flex-item flex-item-5">5</div>
  <div class="flex-item flex-item-5">5</div>
</div>

##横向主轴:定义容器的布局方向flex-direction: row;

.flex-container {
  height: 400px;
  display: flex;
  flex-flow: row wrap; /*必须定义某一个方向, 才会形成多根轴线*/
  align-content: flex-end;/*更改 align-content 的值,体验不同的效果*/
  background: gray;
}
.flex-item {
  white-space: nowrap;
  margin-right: 10px;
  margin-bottom: 10px;
  text-align: end;
}
.flex-item-1, .flex-item-3 {
  width: 60px;
  height: 80px;
  background: lightcoral;
}
.flex-item-2, .flex-item-4 {
  width: 90px;
  height: 40px;
  background: lightblue;
}
.flex-item-5 {
  width: 105px;
  height: 20px;
  background: lightgreen;
}

align-content: flex-start 效果

ad03544f87b1828a7097e3bebe50f327.png

align-content: flex-end 效果

c888af1fc401abb2bf458414ad677889.png

align-content: center 效果

752326b71a22fd2882660df9f9dcf7b6.png

align-content: space-around 效果

e74286dedbbb0b230ad19f4454214780.png

align-content: space-between 效果

9a21bf7ee0b55debe4152123bb9d7143.png

align-content: stretch 效果

89a1f48c45a04408dbed1306ebc8b983.pngef43dc63b3b43b121953a8a8de6e85aa.pngTom:WTF?! stretch 不是要占满容器的吗, 为啥没有效果!!看阮老师画的图也是充满的呀, why is that so funny?! 文档也没有其他内容说明了!这时候, Tom 看了一眼 align-items 的值, 里面也有一个 stretch, 这里阮老师说:“如果项目未设置高度或设为 auto,将占满整个容器的高度。” Tom: align-content 的 stretch 属性跟这里应该是一个意思, 验证一下!Tom 回去看了一下子项目的 css, 发现每个项目都设置了高度, 把这些高度去掉或者改为 auto 试一下:

子项目高度改为 auto 时,align-content: stretch 效果

ec857809ec374743dfbf7e2daf8e616c.pngTom: 原来如此!只改了 item-1 和 item-3 的高度为 auto, 发现他们得到了延伸 stretch, 而其他没有修改高度的子项还是没有延伸。Tom 终于发现其中的奥秘了!!

Tom 又加以思索, 这个是设置了横向的布局方向, 纵向的是什么表现?也来看一看。

纵向交叉轴:定义容器的布局方向flex-direction: column;

align-content: flex-start 效果

fea432bcd8a2b696dfbeb77fb54e1120.png

align-content: flex-end 效果

4435836802413328cf87fe169ba760ad.png说明设置布局方向为 column 的时候, align-content 表现体现在水平方向上?如果是 center, 那就是水平居中了?试试!!

align-content: center 效果

cedfa11f291d5d136dd8813105ac0da3.png

align-content: space-around 效果

323c1764dc0dfd64da35ec07ca424b95.png

align-content: space-between 效果

117a9ccc3743a584ff911298b53b72c1.png

align-content: stretch 效果

fdc6eca8eb54dd9d1e7fc70233058edd.png

总结

经过一番实战后,Tom 可算找到规律了。对于容器属性align-content, 要让它生效的条件是:必须显式的设置布局方向

  • 如果布局方向为横向主轴:flex-direction: row;

    • 1、此时 align-content 的变化体现在竖直方向

    • 2、如果要让 align-content: stretch;生效,这个时候应该去掉子项的高度或者设置子项的高度为 auto

  • 如果布局方向为纵向交叉轴:flex-direction: column;

    • 1、此时 align-content 的变化体现在水平方向

    • 2、如果要让 align-content: stretch;生效,这个时候应该去掉子项的宽度或者设置子项的宽度为 auto

这次 Tom 见到 Jerry 了Tom: Hey!Jerry, I've learned a new property of flex, align-content, you can look it up in my blog.Jerry: oh, I see! that's awesome! keep it on, find more intereting properties of flex.Tom: Good idea! I'll do it next time! thank you!Jerry: My pleasure!

7fa44bb21b2f747c151eb56daaddf2f3.png

这是我的启动页面代码 倒计时或者手动跳转到首页的 , 但是小米上架审核的时候 在首页再点击我的 还是跳回到首页 这是uniapp的bug 还是我这个页面倒计时影响的呢? <template> <view> <view v-if="hasNet && showAdvert" class="flex flex-direction flex-wrap align-center justify-center benben-flex-layout firstHome_flex_1"> <view style="position: relative;width:100%;height:100%"> <swiper ref="benbenSwiperfd1_0" :current="defaultAdsIndex" @change="defaultAdsIndex = $event.detail.current" class=&#39;flex position-relative firstHome_fd1_0&#39; previous-margin="0rpx" next-margin="0rpx" :display-multiple-items="1" :interval="2000" :duration="500" :autoplay=&#39;true&#39; :circular=&#39;true&#39;> <swiper-item class=&#39;flex firstHome_fd1_0&#39; v-for="(item, index) in advert"> <image class=&#39;firstHome_fd1_0_c1_c0&#39; mode="aspectFill" :src=&#39;item.thumb&#39;></image> </swiper-item> </swiper> <view style="position: absolute" class="flex dot flex align-center justify-center firstHome_swiperDotfd1_0"> <template v-for="(item, index) in advert"> <view :key="index" v-if="defaultAdsIndex == index" class="flex dot selected flex align-center justify-center firstHome_swiperDotSelectedfd1_0"> </view> <view :key="index" v-else class="flex dot unselected flex align-center justify-center firstHome_swiperDotUnselectedfd1_0"> </view> </template> </view> </view> <!-- 优化的跳过按钮 --> <view class="skip-container skip-bottom-right"> <text class="skip-btn" v-if="advertTime > 0" @click="pass()"> <text class="skip-text">跳过</text> <text class="countdown">{{advertTime}}s</text> </text> <text class="enter-btn" v-if="advertTime == 0" @click="pass()">正在进入</text> </view> </view> <fu-notwork></fu-notwork> </view> </template> <script> export default { data() { return { hasNet:false, showAdvert: false, advert: [], advertTime: 3,//倒计时 defaultAdsIndex: 0, timer:null }; }, computed: { themeColor() { return this.$store.getters.themeColor }, }, onHide() { this.showAdvert = false clearInterval(this.timer) }, async onShow() { var that = this //苹果要先检查网络,有网时执行,无网不显示 let s = 0; let betTimer = setInterval(() => { uni.getNetworkType({ success: (res) => { console.log(res.networkType, s,&#39;网络&#39;); if (res.networkType !== &#39;none&#39;) { clearInterval(betTimer); console.log(&#39;已有网络!&#39;);//有网络就加载 that.hasNet = true that.getadvert() //写逻辑代码,(包含接口文件和网络接口的) } else{ console.log(&#39;没有网络&#39;) } } }); s++; }, 500); }, onBackPress() { this.showAdvert = false clearInterval(this.timer) }, methods: { pass(){ clearInterval(this.timer) uni.switchTab({ url: &#39;/pages/tabBar/home/home&#39; }) }, getadvert() { let that = this that.$api.post(global.apiUrls.getNotice, { typeid: 2 }).then(res => { // console.log(res,&#39;引导页&#39;) if (res.data.code == 1) { that.advert = res.data.data uni.hideLoading(); if(that.advert.length == 0){ uni.switchTab({ url: &#39;/pages/tabBar/home/home&#39; }) return false }else{ that.showAdvert = true that.timer = setInterval(function() { if (that.advertTime <= 0) { clearInterval(that.timer) that.pass() } else { that.advertTime-- } }, 1000) } } }); } } }; </script> <style lang="scss" scoped> .page { width: 100vw; overflow-x: hidden; min-height: calc(100vh - var(--window-bottom)); background-size: 100% auto; } .firstHome_flex_0 { width: 100%; overflow: hidden; z-index: 10; top: 0rpx; height: 96rpx; background-size: 100% auto; } .firstHome_flex_1 { width:100%;height:100vh; } .firstHome_fd1_0_c1_c0 { width: 100%; height: 100vh; // border-radius: 24rpx 24rpx 24rpx 24rpx; } .firstHome_fd1_0 { width: 100%; height: 100vh; } .firstHome_swiperDotUnselectedfd1_0 { border: 3px solid rgba(255, 255, 255, 0.6); background: #FFFFFF; width: 8rpx; height: 8rpx; border-radius: 10rpx 10rpx 10rpx 10rpx; margin: 0rpx 6rpx 0rpx 0rpx; font-size: 24rpx; color: #fff; opacity: 0.5; } .firstHome_swiperDotSelectedfd1_0 { border: 3px solid var(--benbenbdColor1); background: #FFFFFF; width: 28rpx; height: 8rpx; border-radius: 10rpx 10rpx 10rpx 10rpx; margin: 0rpx 6rpx 0rpx 0rpx; font-size: 24rpx; color: #fff; } ::v-deep .firstHome_swiperDotfd1_0 { position: absolute; bottom: 6%; left: 0rpx; right: 0rpx; } .firstHome_flex_2 { background: var(--benbenbgColor1); padding: 32rpx 32rpx 24rpx 32rpx; background-size: 100% auto; } .firstHome_fd2_1_c0_c2_c1 { font-size: 18rpx; color: rgba(160, 165, 172, 1); } .firstHome_fd2_1_c0_c2_c0_c0 { color: #666666; font-size: 28rpx; font-weight: 400; width: 480rpx; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .firstHome_fd2_1_c0_c2_c0 { touch-action: none; height: 40rpx; } .firstHome_fd2_1_c0_c1 { background: rgba(221, 221, 221, 1); width: 1rpx; height: 30rpx; margin: 0rpx 24rpx 0rpx 24rpx; transform: scaleX(0.5); } .firstHome_fd2_1_c0_c0 { width: 106rpx; height: 33rpx; border-radius: 0rpx 0rpx 0rpx 0rpx; } .firstHome_fd2_1_c0 { background: #F8F8F8; border-radius: 44rpx 44rpx 44rpx 44rpx; width: 686rpx; height: 88rpx; padding: 0rpx 32rpx 0rpx 32rpx; } /* 新增右下角跳过按钮样式 */ .skip-container { position: fixed; z-index: 999; animation: fadeIn 0.5s ease-out; &.skip-bottom-right { bottom: 106rpx; /* 距离底部距离 */ right: 40rpx; /* 距离右侧距离 */ /* 如果在小屏设备上需要更大间距 */ @media (max-width: 375px) { bottom: 106rpx; right: 30rpx; } } } .skip-btn, .enter-btn { display: flex; align-items: center; justify-content: center; font-size: 28rpx; border-radius: 50rpx; color: #fff; padding: 14rpx 24rpx; box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.25); transition: all 0.3s ease; background: rgba(0,0,0,0.5); overflow: hidden; position: relative; /* 添加圆角外边框增强视觉效果 */ &::before { content: &#39;&#39;; position: absolute; top: 0; left: 0; right: 0; bottom: 0; border: 1px solid rgba(255,255,255,0.3); border-radius: 50rpx; pointer-events: none; } /* 悬停效果 */ &:active { transform: scale(0.95); background: rgba(0,0,0,0.65); } } /* 跳过文字和倒计时分开样式 */ .skip-text { font-weight: 500; padding-right: 8rpx; } .countdown { font-weight: bold; color: #FFD700; /* 金色倒计时数字 */ } .enter-btn { // background: linear-gradient(135deg, #4A90E2, #5CBD9D); background: rgba(40, 120, 255, 0.8); } /* 淡入动画 */ @keyframes fadeIn { from { opacity: 0; transform: translateX(40rpx); } to { opacity: 1; transform: translateX(0); } } /* 添加倒计时跳动动画 */ @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.1); } 100% { transform: scale(1); } } /* 倒计时最后3秒添加跳动效果 */ .countdown { display: inline-block; } /* #ifndef MP-WEIXIN */ /* 当倒计时小于4秒时添加动画 */ .skip-btn:has(.countdown:contains(&#39;3&#39;)), .skip-btn:has(.countdown:contains(&#39;2&#39;)), .skip-btn:has(.countdown:contains(&#39;1&#39;)) { .countdown { animation: pulse 0.5s ease-in-out infinite; color: #FF6347; /* 红色提示 */ } } /* #endif */ </style>
最新发布
10-24
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值