RN for OpenHarmony 实战 TodoList 项目:渐变背景色

案例开源地址:https://atomgit.com/lqjmac/rn_openharmony_todolist

纯色背景太单调

打开很多应用,背景就是一个纯色。深色模式是 #0f0f23,浅色模式是 #f5f5f5。功能上没问题,但总觉得少了点什么。

设计师喜欢在背景上加点"料"。渐变色、装饰图形、纹理图案,这些元素让界面更有层次感。不是花里胡哨,而是恰到好处的点缀。

我们的 TodoList 应用用了一种简单的方式:在背景上放两个半透明的大圆。一个紫色的在右上角,一个红色的在左下角。它们不会抢内容的风头,但让整个界面看起来更有设计感。


背景装饰元素的代码

先看看我们是怎么实现的:

tsx 复制代码
return (
  <SafeAreaView style={[styles.container, {backgroundColor: theme.bg}]}>
    <StatusBar barStyle={darkMode ? 'light-content' : 'dark-content'} backgroundColor={theme.bg} />
    <View style={styles.bgGradient1} />
    <View style={styles.bgGradient2} />

    <Animated.View style={[styles.content, {opacity: fadeAnim, transform: [{translateY: slideAnim}]}]}>
      {/* 页面内容 */}
    </Animated.View>
  </SafeAreaView>
);

结构很简单。SafeAreaView 是最外层容器,设置了背景色。然后放了两个 View,就是那两个装饰圆。最后是 Animated.View 包裹的实际内容。

装饰元素放在内容前面,这样它们会被内容"盖住"。但因为内容区域是透明的,装饰元素还是能透出来。


装饰圆的样式

tsx 复制代码
bgGradient1: {
  position: 'absolute',
  top: -100,
  right: -100,
  width: 300,
  height: 300,
  borderRadius: 150,
  backgroundColor: 'rgba(108, 92, 231, 0.1)',
},
bgGradient2: {
  position: 'absolute',
  bottom: -150,
  left: -150,
  width: 400,
  height: 400,
  borderRadius: 200,
  backgroundColor: 'rgba(255, 107, 107, 0.05)',
},

两个圆的样式差不多,但有一些区别。

position: 'absolute'

绝对定位,让装饰元素脱离正常的布局流。它们不会占用空间,不会影响其他元素的位置。

负数的定位值

top: -100right: -100 让第一个圆的一部分超出屏幕。同样,bottom: -150left: -150 让第二个圆也超出屏幕。

为什么要超出屏幕?因为这样看起来更自然。如果圆完全在屏幕内,会显得很刻意,像是"放了一个圆在那里"。超出屏幕后,用户只能看到圆的一部分,感觉像是"背景延伸到了屏幕外"。

borderRadius 等于宽高的一半

width: 300borderRadius: 150。宽高相等,圆角等于宽高的一半,就是一个正圆。

半透明的颜色

rgba(108, 92, 231, 0.1) 是紫色,透明度 0.1,非常淡。rgba(255, 107, 107, 0.05) 是红色,透明度 0.05,更淡。

透明度很重要。太高会喧宾夺主,影响内容的可读性。太低又看不出效果。0.05 到 0.1 是一个比较好的范围。


为什么用这两个颜色

紫色 #6c5ce7 是我们应用的主题色,用在按钮、进度条、高亮等地方。红色 #ff6b6b 是高优先级任务的颜色。

背景装饰用这两个颜色,和应用的整体配色保持一致。不是随便选的颜色,而是有意义的颜色。

紫色在右上角,红色在左下角。对角线的布局让画面更有动感。如果两个圆都在同一侧,会显得不平衡。


深色模式和浅色模式

我们的装饰圆用的是固定的颜色和透明度,没有根据主题调整。这是因为:

紫色和红色在深色背景上效果不错,淡淡的光晕感。在浅色背景上也能接受,虽然不那么明显。

如果想针对不同主题调整,可以这样:

tsx 复制代码
bgGradient1: {
  ...
  backgroundColor: darkMode ? 'rgba(108, 92, 231, 0.15)' : 'rgba(108, 92, 231, 0.08)',
},

深色模式下透明度高一点,浅色模式下低一点。但这会增加代码复杂度,效果提升有限,所以我们选择了简单的固定值。


不用第三方库的渐变

React Native 原生不支持 CSS 那样的 linear-gradient。如果想要真正的渐变效果,通常需要用 react-native-linear-gradient 这样的第三方库。

但我们的方案不需要任何第三方库。用几个半透明的圆形 View,就能模拟出类似渐变的效果。这种方式:

  • 不需要额外安装依赖
  • 不需要原生模块链接
  • 在所有平台上效果一致
  • 性能开销很小

当然,这不是真正的渐变。如果需要精确的线性渐变或径向渐变,还是得用第三方库。但对于背景装饰来说,这种"伪渐变"已经够用了。


多层叠加的效果

两个圆叠加在一起,会产生一些有趣的效果。在它们重叠的区域,颜色会混合。紫色加红色,透明度叠加,形成一种微妙的过渡。

这种叠加是自然发生的,不需要额外的代码。多个半透明元素重叠,浏览器(或者说渲染引擎)会自动计算混合后的颜色。

如果想要更丰富的效果,可以加更多的装饰元素:

tsx 复制代码
<View style={styles.bgGradient1} />
<View style={styles.bgGradient2} />
<View style={styles.bgGradient3} />
<View style={styles.bgGradient4} />

四个圆,四个角,或者其他的布局。但要注意不要加太多,否则会显得杂乱。两到三个通常就够了。


装饰元素的层级

在我们的代码里,装饰元素放在内容之前:

tsx 复制代码
<View style={styles.bgGradient1} />
<View style={styles.bgGradient2} />
<Animated.View style={styles.content}>
  {/* 内容 */}
</Animated.View>

这意味着内容会"盖在"装饰元素上面。如果内容有背景色,装饰元素就会被遮住。

我们的内容区域 styles.content 没有设置背景色,所以是透明的。装饰元素可以透过内容区域显示出来。

如果想让装饰元素在内容上面,可以调整顺序,或者用 zIndex

tsx 复制代码
bgGradient1: {
  ...
  zIndex: 1,
},

但通常装饰元素应该在内容下面,不然会干扰用户阅读。


动态的装饰元素

静态的装饰圆已经不错了,但如果能动起来会更有趣。比如缓慢地旋转、移动、或者改变大小。

旋转动画

tsx 复制代码
const rotateAnim = useRef(new Animated.Value(0)).current;

useEffect(() => {
  Animated.loop(
    Animated.timing(rotateAnim, {
      toValue: 1,
      duration: 30000,
      useNativeDriver: true,
    })
  ).start();
}, []);

const spin = rotateAnim.interpolate({
  inputRange: [0, 1],
  outputRange: ['0deg', '360deg'],
});

30 秒转一圈,非常慢。用户几乎感觉不到在转,但会有一种"活的"感觉。

然后把动画应用到装饰元素:

tsx 复制代码
<Animated.View style={[styles.bgGradient1, {transform: [{rotate: spin}]}]} />

因为圆是对称的,旋转其实看不出来。但如果是椭圆或者其他形状,旋转就会有明显的效果。

缩放动画

tsx 复制代码
const scaleAnim = useRef(new Animated.Value(1)).current;

useEffect(() => {
  Animated.loop(
    Animated.sequence([
      Animated.timing(scaleAnim, {toValue: 1.1, duration: 5000, useNativeDriver: true}),
      Animated.timing(scaleAnim, {toValue: 1, duration: 5000, useNativeDriver: true}),
    ])
  ).start();
}, []);

圆慢慢变大,再慢慢变小,循环往复。像是在"呼吸"。

tsx 复制代码
<Animated.View style={[styles.bgGradient1, {transform: [{scale: scaleAnim}]}]} />

这种动画很微妙,不会分散用户注意力,但让界面更有生气。


性能考虑

装饰元素会影响性能吗?

几个静态的 View,性能影响可以忽略。它们只是普通的视图,没有复杂的计算。

如果加了动画,情况会稍微复杂一点。但只要用 useNativeDriver: true,动画在原生线程执行,不会阻塞 JS 线程。

需要注意的是,不要在装饰元素上加太多效果。比如同时有旋转、缩放、透明度变化,可能会有性能问题。保持简单,一两个动画就够了。


和主题系统的配合

我们的应用有深色和浅色两种主题:

tsx 复制代码
const theme = {
  bg: darkMode ? '#0f0f23' : '#f5f5f5',
  card: darkMode ? '#1a1a2e' : '#ffffff',
  text: darkMode ? '#ffffff' : '#333333',
  subText: darkMode ? '#888888' : '#666666',
  border: darkMode ? '#2a2a4a' : '#e0e0e0',
  accent: '#6c5ce7',
};

背景色 theme.bg 会根据主题变化。装饰元素叠加在背景色上,效果也会不同。

深色背景 #0f0f23 上,紫色和红色的装饰圆会显得比较明显,有一种"发光"的感觉。

浅色背景 #f5f5f5 上,装饰圆会显得比较淡,更像是"阴影"或"水印"。

两种效果都可以接受。如果想要更一致的效果,可以根据主题调整装饰圆的颜色和透明度。


其他形状的装饰元素

圆形是最常用的,但不是唯一的选择。

椭圆

tsx 复制代码
bgEllipse: {
  position: 'absolute',
  top: -50,
  right: -100,
  width: 400,
  height: 200,
  borderRadius: 100,
  backgroundColor: 'rgba(108, 92, 231, 0.1)',
  transform: [{rotate: '30deg'}],
},

宽高不等,就是椭圆。加上旋转,可以有更多变化。

圆角矩形

tsx 复制代码
bgRect: {
  position: 'absolute',
  top: 100,
  left: -50,
  width: 200,
  height: 300,
  borderRadius: 20,
  backgroundColor: 'rgba(255, 107, 107, 0.05)',
},

圆角矩形比圆更"硬"一些,适合更现代的设计风格。

三角形

React Native 没有直接画三角形的方式,但可以用边框技巧:

tsx 复制代码
bgTriangle: {
  position: 'absolute',
  top: 0,
  right: 0,
  width: 0,
  height: 0,
  borderLeftWidth: 150,
  borderLeftColor: 'transparent',
  borderBottomWidth: 150,
  borderBottomColor: 'rgba(108, 92, 231, 0.1)',
},

这会画一个直角三角形。但这种方式比较 hack,不太推荐。


模糊效果

如果想让装饰元素更柔和,可以加模糊效果。但 React Native 原生不支持 blur,需要用 @react-native-community/blur 这样的库。

不用第三方库的话,可以用多层叠加模拟模糊:

tsx 复制代码
// 多个同心圆,透明度递减
<View style={[styles.bgGradient1, {width: 300, height: 300, backgroundColor: 'rgba(108, 92, 231, 0.1)'}]} />
<View style={[styles.bgGradient1, {width: 320, height: 320, backgroundColor: 'rgba(108, 92, 231, 0.05)'}]} />
<View style={[styles.bgGradient1, {width: 340, height: 340, backgroundColor: 'rgba(108, 92, 231, 0.02)'}]} />

三个圆,从内到外,透明度递减。看起来像是一个模糊的圆。但这会增加渲染的元素数量,性能上不如真正的模糊。


响应式布局

装饰元素的位置和大小是固定的。在不同尺寸的屏幕上,效果可能不一样。

小屏幕上,300x300 的圆可能占据很大比例。大屏幕上,同样的圆可能显得很小。

如果想要响应式的装饰元素,可以用 Dimensions

tsx 复制代码
import {Dimensions} from 'react-native';

const {width, height} = Dimensions.get('window');

bgGradient1: {
  ...
  width: width * 0.8,
  height: width * 0.8,
  borderRadius: width * 0.4,
},

圆的大小是屏幕宽度的 80%,在不同设备上会自动调整。

但对于背景装饰来说,固定大小通常也够用。因为它们只是点缀,不需要精确适配。

小结

用两个半透明的圆形 View 作为背景装饰,不需要第三方库就能实现类似渐变的效果。position: 'absolute' 让它们脱离布局流,负数定位让它们部分超出屏幕,低透明度保证不影响内容可读性。


欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

相关推荐
花归去8 小时前
echarts 柱状图曲线图
开发语言·前端·javascript
老前端的功夫8 小时前
TypeScript 类型魔术:模板字面量类型的深层解密与工程实践
前端·javascript·ubuntu·架构·typescript·前端框架
Nan_Shu_6148 小时前
学习: Threejs (2)
前端·javascript·学习
G_G#8 小时前
纯前端js插件实现同一浏览器控制只允许打开一个标签,处理session变更问题
前端·javascript·浏览器标签页通信·只允许一个标签页
程序猿追8 小时前
【鸿蒙PC桌面端实战】从零构建 ArkTS 高性能图像展示器:DevEco Studio 调试与 HDC 命令行验证全流程
华为·harmonyos
@大迁世界9 小时前
TypeScript 的本质并非类型,而是信任
开发语言·前端·javascript·typescript·ecmascript
Amumu121389 小时前
React面向组件编程
开发语言·前端·javascript
冰暮流星9 小时前
javascript逻辑运算符
开发语言·javascript·ecmascript
前端世界10 小时前
设备找不到、Ability 启不动?一次讲清 DevEco Studio 调试鸿蒙分布式应用
华为·harmonyos
AI大佬的小弟10 小时前
【小白第一课】大模型基础知识(1)---大模型到底是啥?
人工智能·自然语言处理·开源·大模型基础·大模型分类·什么是大模型·国内外主流大模型