rn_for_openharmony_卡片为什么看起来“浮“在页面上:边框与阴影的视觉技巧
本文详细解析了React Native中卡片组件的设计细节,包括边框、圆角、间距等关键样式属性的作用。通过ArticleCard示例,展示了如何通过borderRadius、borderWidth、marginBottom和overflow属性实现美观的卡片效果。文章还探讨了动态边框颜色设置、替代阴影效果的方案、overflow:hidden的妙用、内容区域样式设计、卡片间距处理以及点击反馈优化等

案例项目开源地址:https://atomgit.com/nutpi/wanandroid_rn_openharmony
好的界面设计,往往藏在细节里。同样是一个卡片,有的看起来就是贴在背景上的,有的看起来像是浮在上面的。区别在哪?
答案是:边框、圆角、还有那一点点若有若无的层次感。
我们的卡片长什么样
先看 ArticleCard 的样式:
const styles = StyleSheet.create({
card: {
borderRadius: 12,
borderWidth: 1,
marginBottom: 12,
overflow: 'hidden'
},
});
四个属性,四个作用
borderRadius: 12 设置圆角。12 像素是一个比较柔和的圆角,不会太方正也不会太圆润。现代 UI 设计普遍使用圆角,因为圆角看起来更友好、更现代。
borderWidth: 1 设置边框宽度。1 像素的边框非常细,在高清屏上几乎是一条线。它的作用不是装饰,而是在深色背景上勾勒出卡片的边界。
marginBottom: 12 设置卡片之间的间距。12 像素让卡片之间有呼吸感,不会挤在一起。
overflow: 'hidden' 隐藏超出边界的内容。这个属性配合 borderRadius 使用,让卡片内的图片也有圆角效果。
边框颜色的动态设置
<TouchableOpacity
style={[styles.card, {backgroundColor: theme.card, borderColor: theme.border}]}
onPress={() => openLink(item.link)}
activeOpacity={0.7}
>
为什么边框颜色要动态设置
borderColor: theme.border 让边框颜色跟随主题变化。
在深色模式下,theme.border 是 #2a2a4a,一种深紫灰色。这个颜色比卡片背景(#1a1a2e)稍浅,能看到边界但不会太突兀。
在浅色模式下,theme.border 是 #e0e0e0,一种浅灰色。比卡片背景(白色)稍深,同样能勾勒边界。
如果用固定的边框颜色,比如纯黑或纯白,在某一种主题下会很突兀。动态颜色让边框在两种主题下都自然。
backgroundColor 的作用
backgroundColor: theme.card 设置卡片的背景色。深色模式是深蓝灰色,浅色模式是白色。
卡片背景色和页面背景色不同,这种色差本身就能产生层次感。卡片"浮"在页面上的感觉,很大程度来自这个色差。
为什么不用阴影
React Native 支持阴影效果,但我们没有用:
// 我们没有用这些属性
shadowColor: '#000',
shadowOffset: {width: 0, height: 2},
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3, // Android 专用
阴影的问题
第一,跨平台不一致。iOS 用 shadow* 属性,Android 用 elevation。两个平台的阴影效果不一样,很难调到一致。
第二,性能开销。阴影需要额外的渲染计算,尤其是在列表里有很多卡片时,可能影响滚动流畅度。
第三,深色模式下效果不好。阴影是通过在元素下方画一个模糊的深色区域实现的。在深色背景上,深色阴影几乎看不见。
我们的替代方案
用边框代替阴影。1 像素的边框在视觉上能起到类似的作用:勾勒出卡片的边界,让它和背景区分开。
边框的优点是:跨平台一致、性能好、深色浅色模式都能用。
overflow: hidden 的妙用
card: {
borderRadius: 12,
overflow: 'hidden'
},
image: {
width: '100%',
height: 150,
resizeMode: 'cover'
},
问题:图片的角是方的
卡片有 12 像素的圆角,但图片是矩形的。如果不做处理,图片的四个角会超出卡片的圆角边界,看起来很奇怪。
解决:overflow: hidden
overflow: 'hidden' 告诉 React Native:超出这个 View 边界的内容,不要显示。
卡片有圆角,图片超出圆角的部分会被裁掉,图片看起来也有了圆角。
这是一个常用技巧:父元素设置圆角和 overflow: 'hidden',子元素自动被裁成圆角。
只对顶部图片生效
我们的卡片结构是:图片在上,内容在下。图片的上两个角需要圆角(和卡片顶部对齐),下两个角不需要(和内容区域连接)。
overflow: 'hidden' 刚好实现了这个效果:图片上两个角被卡片的圆角裁掉,下两个角因为在卡片内部,不会被裁。
卡片内容区域的样式
content: {padding: 12},
为什么只有一个 padding
padding: 12 是简写,等于 paddingTop: 12, paddingRight: 12, paddingBottom: 12, paddingLeft: 12。
12 像素的内边距让文字不会贴着卡片边缘,有呼吸感。这个值和 borderRadius: 12 一致,视觉上比较协调。
为什么内容区域没有背景色
内容区域没有单独设置背景色,它会继承卡片的背景色。这样整个卡片是一个统一的颜色,图片区域和内容区域自然过渡。
卡片之间的间距
card: {
marginBottom: 12,
},
list: {
paddingBottom: 100,
},
marginBottom 的作用
marginBottom: 12 让每个卡片下方有 12 像素的空白。卡片之间不会紧贴,有间隔才能看出是独立的个体。
为什么是 12 而不是其他值
12 像素是一个"黄金值",不大不小。太小(比如 4 像素)卡片会显得拥挤,太大(比如 24 像素)会浪费空间,一屏能看到的卡片数量减少。
而且 12 和卡片的圆角、内边距一致,整个界面的间距保持统一,看起来更整齐。
paddingBottom: 100 的作用
列表底部留 100 像素的空白,是为了给底部 Tab 栏让位。Tab 栏是悬浮在列表上方的,如果不留空白,最后一个卡片会被 Tab 栏遮挡。
点击反馈
<TouchableOpacity
style={[styles.card, ...]}
onPress={() => openLink(item.link)}
activeOpacity={0.7}
>
activeOpacity 的作用
activeOpacity={0.7} 设置点击时的透明度。用户按下卡片时,卡片会变成 70% 透明度,松开后恢复。
这个视觉反馈告诉用户"我点到了",是交互设计的基本要求。没有反馈的按钮会让用户困惑:我点了吗?点成功了吗?
为什么是 0.7
0.7 是一个比较明显但不夸张的值。太高(比如 0.9)变化不明显,用户可能注意不到。太低(比如 0.3)变化太大,看起来像是闪烁。
默认值是 0.2,我们觉得太透明了,改成 0.7 更自然。
不同页面的卡片样式
我们的 App 有多种卡片:文章卡片、用户信息卡片、设置卡片、收藏卡片、关于卡片。它们的样式略有不同,但遵循统一的设计语言。
文章卡片
card: {borderRadius: 12, borderWidth: 1, marginBottom: 12, overflow: 'hidden'},
有图片,需要 overflow: 'hidden'。
用户信息卡片
userCard: {borderRadius: 16, borderWidth: 1, padding: 20, marginBottom: 16, flexDirection: 'row', alignItems: 'center'},
圆角更大(16),内边距更大(20),因为这是页面的主要卡片,需要更突出。
设置卡片
settingCard: {borderRadius: 16, borderWidth: 1, padding: 16, marginBottom: 16},
和用户信息卡片保持一致的圆角,但内边距稍小。
统一的设计语言
所有卡片都用:
- 圆角(12 或 16)
- 1 像素边框
- 动态的背景色和边框色
- 适当的内边距和外边距
这种统一让整个 App 看起来是一个整体,而不是拼凑的。
完整的卡片样式代码
// ArticleCard 的样式
const styles = StyleSheet.create({
card: {
borderRadius: 12,
borderWidth: 1,
marginBottom: 12,
overflow: 'hidden'
},
image: {
width: '100%',
height: 150,
resizeMode: 'cover'
},
content: {
padding: 12
},
title: {
fontSize: 16,
fontWeight: '600',
lineHeight: 22,
marginBottom: 6
},
desc: {
fontSize: 13,
lineHeight: 18,
marginBottom: 8
},
meta: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 8,
flexWrap: 'wrap',
gap: 8
},
footer: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center'
},
});
// 使用
<TouchableOpacity
style={[styles.card, {backgroundColor: theme.card, borderColor: theme.border}]}
onPress={() => openLink(item.link)}
activeOpacity={0.7}
>
{showImage && item.envelopePic ? (
<Image source={{uri: item.envelopePic}} style={styles.image} />
) : null}
<View style={styles.content}>
{/* 标题、描述、元信息、底部 */}
</View>
</TouchableOpacity>
设计原则总结
一致性
所有卡片用相似的圆角、边框、间距。用户看到一个卡片,就知道其他卡片大概长什么样。
层次感
通过背景色差异和边框,让卡片和页面背景区分开。不需要阴影也能有"浮起来"的感觉。
呼吸感
适当的内边距和外边距,让内容不拥挤。留白也是设计的一部分。
反馈感
点击有透明度变化,让用户知道操作被响应了。
这些细节单独看都很小,但加在一起,就是"好看"和"一般"的区别。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐
所有评论(0)