一、项目场景:
在小程序上需要实现一个如下图的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>
最后的效果