一、项目场景:
在小程序上需要实现一个如下图的ui效果图
需要满足以下条件
- 一行放不下 自动换行
- 最后一行或者只有一行时,文字底部不能有线
二、初版实现
按照上面的要求,最开是的实现代码如下
我是给每一个元素都添加了一个下边框,但是这样的话,在最后一行的时候,会出现边框重合的情况
效果图如下,最后一行放大可发现有重叠部分,颜色加重
<template><div class="box"><div class="Rp"><div class="h4">Rp</div><div class="herb-box"><div v-for="(item, index) in list" :key="index" class="herb"><div class="name">{{ item.name }}</div><div class="weight">{{ item.weight }}g</div></div></div></div></div>
</template>
<script setup>
import { ref } from 'vue'
const list = ref([{ name: '阿莫西林', weight: 5 },{ name: '阿莫阿莫西林阿莫西林', weight: 8 },{ name: '西林', weight: 10 },{ name: '阿西林', weight: 30 },{ name: '阿到付贵莫西林', weight: 3 },{ name: '时代峰', weight: 10 },{ name: '阿莫西林', weight: 10 },{ name: '发', weight: 5 },{ name: '阿莫西林', weight: 5 }
])
</script>
<style lang="scss" scoped>
.box {padding: 20rpx 32rpx 40rpx 32rpx;.Rp {width: 686rpx;background-color: #f7f8fa;border-radius: 16rpx;border: 2rpx solid #ced6e2;margin-bottom: 32rpx;padding-top: 12rpx;.h4 {font-weight: 600;font-size: 40rpx;color: #7089ab;margin-bottom: 12rpx;margin-left: 24rpx;}.herb-box {display: flex;flex-wrap: wrap;padding: 0 24rpx;.herb {display: flex;align-items: center;padding: 18rpx;padding-left: 0;border-bottom: 2rpx solid #e6e6e6; // 真实的内部元素边框position: relative;.name {margin-right: 8rpx;font-weight: 400;font-size: 28rpx;color: #435675;}.weight {font-weight: 600;font-size: 28rpx;color: #435675;}}}}
}
</style>
三、解决思路:
1.使用css获取最后一行的元素——pase
我只要知道最后一行中有哪几个元素,然后给最后一行的元素设置border-bottom:none
就行了
但是 :last-child
只能获取到最后一个元素
:nth-last-child
需要知道一排要有几个元素才行
2.使用伪元素 或者是 一个新的标签定位做遮盖——pase
想法就是使用一个新的元素,模仿背景色,做一个覆盖,感觉上是可行的
但是会造成dom结构的破坏,使用伪元素在真正定位的时候,会出现颜色透传
3.使用ref计算每个元素的宽度,然后动态设置class
这种是可以实现ui图的功能,但是感觉有点大材小用,高射炮打蚊子。
本人是很不喜欢使用js解决css的问题
理由如下:
- 性能不好
- 太low 不够优雅
computed: {// 计算哪些元素在最后一行isInLastRow() {const containerWidth = this.$refs.herbBox.offsetWidth; // herb-box 的宽度let currentRowWidth = 0;let lastRowIndex = 0;return (index) => {const herbElement = this.$refs[`herb-${index}`][0]; // 取出当前元素的 DOM 对象const herbWidth = herbElement.offsetWidth;if (currentRowWidth + herbWidth > containerWidth) {currentRowWidth = herbWidth; // 开始新的一行lastRowIndex = index;} else {currentRowWidth += herbWidth;}// 返回 true 表示在最后一行return index >= lastRowIndex;};}
}
4.使用margin+伪元素解决
在偶然的机会查到了双边框的问题,之前倒是知道这个,一直没有遇到过。
这突然遇到了,第一时间没有想起来,真的是太菜了
双边框的问题,可以使用margin:-1px
解决
在使用了后,出现了颜色不一致的问题,因为我内部元素的颜色和外部边框的颜色不一样。
应该是显示外部的,但是实际效果是显示的内部元素的边框颜色
解决方法就是加上:after
在模拟一个外部边框的颜色
伪元素的优势:
独立控制:伪元素如 :before
和 :after
是独立的虚拟元素,可以完全控制其位置、颜色、大小等属性,而不会影响或与实际的元素边框重叠。
避免重叠:通过定位伪元素,你可以让其覆盖某一部分的实际边框,或者完全避开元素的边框,从而避免颜色不同导致的问题。
假设你有两个相邻的元素,它们的边框颜色不同。你可以使用伪元素来创建一个虚拟的边框,使之看起来像是边框在一起但没有实际重叠。
四、解决方案:
使用第四种方案,css代码如下
<style lang="scss" scoped>
.box {padding: 20rpx 32rpx 40rpx 32rpx;.Rp {width: 686rpx;background-color: #f7f8fa;border-radius: 16rpx;border: 2rpx solid #ced6e2;margin-bottom: 32rpx;padding-top: 12rpx;.h4 {font-weight: 600;font-size: 40rpx;color: #7089ab;margin-bottom: 12rpx;margin-left: 24rpx;}.herb-box {display: flex;flex-wrap: wrap;padding: 0 24rpx;.herb {display: flex;align-items: center;padding: 18rpx;padding-left: 0;border-bottom: 2rpx solid #e6e6e6; // 真实的内部元素边框position: relative;margin-bottom: -1px; // 隐藏重叠的边框&::after {// 使用伪元素是为了解决,两个边框颜色不一致,在使用 margin-bottom: -1px 时,显示的颜色为设置为-1的值,应该显示为外部的边框颜色content: '';position: absolute;left: 0;right: 0;bottom: -1rpx;height: 2rpx;background-color: #ced6e2; // 与外层的 .Rp 边框颜色一致z-index: 1; // 确保伪元素位于 .herb 的边框之上}.name {margin-right: 8rpx;font-weight: 400;font-size: 28rpx;color: #435675;}.weight {font-weight: 600;font-size: 28rpx;color: #435675;}}}}
}
</style>
最后的效果