React Native绘制亮度图标:依据亮度百分比精准呈现
以下示例展示了如何在 React Native 中使用 react-native-svg
绘制一个“太阳”/“亮度”图标,并根据传入的亮度百分比(0–100%)精确地调整其视觉呈现。最终效果是在一个圆形太阳核心上,按照百分比动态改变填充半径或透明度,从而让图标“亮度”更直观。
一、思路概述
图标结构
我们以最常见的太阳图标为例,基本由以下几部分组成:- 中央圆(Core):代表“光源”本体,可以用纯色圆或渐变圆。
- 光线射线(Rays):环绕中央圆的若干条射线,用直线(Line)或矩形(Rect)表示。
亮度百分比映射
常见做法有两种:- 改变中央圆的半径:当亮度为 0% 时,核心圆半径为最小(甚至 0);当亮度为 100% 时,核心圆半径为最大值。
- 改变中央圆的颜色或透明度:例如,将
fillOpacity
设为percent / 100
,或者用 HSL/HSV 模型根据亮度值调整颜色明度。
本示例主要演示中央圆半径随亮度百分比线性变化,同时保持射线(Rays)不变。这样既直观表现“亮度从小到大”,也能保证太阳形状清晰。
- 使用
react-native-svg
react-native-svg
提供了类似 Web 上 SVG 的绘制能力:<Svg>、<Circle>、<Line>、<Defs>、<LinearGradient>
等组件。我们可以在 React Native 中直接引入并绘制矢量图形。
二、环境准备
安装
react-native-svg
如果你还没有安装react-native-svg
,请在项目根目录执行:npm install react-native-svg # 或者使用 yarn # yarn add react-native-svg
(仅限 Expo 用户)
如果你使用的是 Expo Managed workflow,通常无需额外链接,Expo 已内置react-native-svg
;若报错,可以通过:expo install react-native-svg
来确保安装与 Expo SDK 兼容的版本。
三、图标设计与绘制逻辑
下面先给出一个简化的 ASCII 图解,帮助你理解图标各部分的位置和坐标关系。假设我们将 SVG 视图框(viewBox
)设为 100×100,那么:
┌─────────────────────────────────┐
│ │
│ ↓ Y │
│ 50 ▲ (中心点) │
│ │ │
–––––––––––––––––––––––––––––––––––––––––
◄ 50 —──────────────────────── 50 ► X
–––––––––––––––––––––––––––––––––––––––––
│ │ │
│ ↓ │
│ (Rays) │
│ │
└─────────────────────────────────┘
- SVG 整体尺寸:
width=100, height=100
,viewBox="0 0 100 100"
。 - 中心点:
(cx, cy) = (50, 50)
。 - 中央圆最大半径:假设为
r_max = 20
,最小半径r_min = 4
(可根据需求自由调整)。 - 光线(Rays):围绕中心均匀分布 8 条直线(或更少/更多),长度从
r_max
延伸到边缘,比如长度L = 28
。每条光线用<Line>
从中心向某一角度画出。
示意图:
\ | / ← 8 条光线 \ | / \ | / ------- ● ------- ← 中心圆 / | \ / | \ / | \
其中 ● 表示中央圆,八条 “/、\、–、|” 即为光线。
四、完整代码示例
下面给出一个可复用的组件 BrightnessIcon
,接收如下 Props:
percent
(必须):亮度百分比,0–100 之间的数字。size
(可选):SVG 画布宽高,一般以正方形为例,默认为 100。color
(可选):中央圆和光线的颜色,默认为黄色#FFD700
。minRadius
、maxRadius
(可选):中央圆最小/最大半径。
该组件会根据 percent
动态计算中央圆半径 r = minRadius + (maxRadius - minRadius) * (percent / 100)
,并绘制中心半径为 r
的圆,以及外围 8 条等距光线。
// BrightnessIcon.js
import React from "react";
import { View } from "react-native";
import Svg, { Circle, Line } from "react-native-svg";
type BrightnessIconProps = {
percent: number; // 亮度百分比 (0 - 100)
size?: number; // SVG 画布大小 (正方形边长),默认 100
color?: string; // 图标颜色,默认金黄色
minRadius?: number; // 中央圆最小半径,默认 4
maxRadius?: number; // 中央圆最大半径,默认 20
};
const BrightnessIcon: React.FC<BrightnessIconProps> = ({
percent,
size = 100,
color = "#FFD700",
minRadius = 4,
maxRadius = 20,
}) => {
// 1. 限制 percent 范围在 [0, 100]
const p = Math.max(0, Math.min(100, percent));
// 2. 计算中央圆半径
const radius = minRadius + (maxRadius - minRadius) * (p / 100);
// 3. 中心坐标
const cx = size / 2;
const cy = size / 2;
// 4. 光线长度(从中央圆中心延伸到的终点距离),略大于 maxRadius
// 这里我们假设光线终点距离中心为 L = maxRadius + 8
const rayLength = maxRadius + 8;
// 5. 光线宽度 (strokeWidth)
const rayStrokeWidth = 2;
// 6. 生成 8 条光线的坐标:每隔 45° 画一条
const rays = Array.from({ length: 8 }).map((_, i) => {
const angle = (Math.PI / 4) * i; // 每隔 45° (π/4)
// 起点在中央圆边缘:radius
const x1 = cx + radius * Math.cos(angle);
const y1 = cy + radius * Math.sin(angle);
// 终点在半径为 rayLength 的圆环上
const x2 = cx + rayLength * Math.cos(angle);
const y2 = cy + rayLength * Math.sin(angle);
return { x1, y1, x2, y2 };
});
return (
<View>
<Svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
{/* 1. 画中央圆 */}
<Circle
cx={cx}
cy={cy}
r={radius}
fill={color}
fillOpacity={1} // 可以配合 percent 调整透明度
/>
{/* 2. 画 8 条光线 */}
{rays.map((ray, idx) => (
<Line
key={idx}
x1={ray.x1}
y1={ray.y1}
x2={ray.x2}
y2={ray.y2}
stroke={color}
strokeWidth={rayStrokeWidth}
strokeLinecap="round"
/>
))}
</Svg>
</View>
);
};
export default BrightnessIcon;
4.1 关键点说明
percent
范围约束const p = Math.max(0, Math.min(100, percent));
确保传入亮度百分比在
[0, 100]
,避免因误传造成负半径或超大半径。中央圆半径计算
const radius = minRadius + (maxRadius - minRadius) * (p / 100);
- 当
p = 0
时,radius = minRadius
; - 当
p = 100
时,radius = maxRadius
; - 之间线性插值,能够直观映射亮度。
- 当
光线坐标计算
每条光线从
(x1, y1)
到(x2, y2)
,其中:(x1, y1)
为中央圆边缘的一点,角度为angle
;(x2, y2)
为更远一点,半径为rayLength
,使光线长度 =rayLength - radius
。
利用极坐标公式:
x = cx + r * cos(angle) y = cy + r * sin(angle)
angle
从0
到7*(π/4)
,即 0°、45°、90°、…315°,共 8 条。
strokeLinecap="round"
让光线尾部更圆润,看起来更像“太阳光线”而非锋利直线。可选:调整透明度
如果需要让“亮度=0”时完全看不见圆心,可以将圆心的fillOpacity={p/100}
而非恒定1
。<Circle cx={cx} cy={cy} r={radius} fill={color} fillOpacity={p / 100} />
此时,当
percent = 0
时,圆心透明度为0
(完全透明),percent = 100
时,透明度为1
(完全不透明)。这种做法视觉上更突出“亮度从无到有”。
五、示例演示与用法
在任意页面或组件中引入 BrightnessIcon
,并根据状态(State)动态传递 percent
:
// ExampleUsage.js
import React, { useState } from "react";
import { View, Text, Slider, StyleSheet } from "react-native";
import BrightnessIcon from "./BrightnessIcon";
const ExampleUsage: React.FC = () => {
const [brightness, setBrightness] = useState(50); // 初始 50%
return (
<View style={styles.container}>
<Text style={styles.title}>调整亮度:{brightness}%</Text>
{/* 亮度图标 */}
<BrightnessIcon percent={brightness} size={150} color="#FFA500" />
{/* 下面是一个 Slider 控件,用于动态调节百分比 */}
<View style={styles.sliderContainer}>
<Slider
style={styles.slider}
minimumValue={0}
maximumValue={100}
step={1}
value={brightness}
onValueChange={(val) => setBrightness(val)}
minimumTrackTintColor="#FFA500"
maximumTrackTintColor="#ccc"
/>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#fff",
},
title: {
fontSize: 18,
marginBottom: 16,
},
sliderContainer: {
width: 200,
marginTop: 24,
},
slider: {
width: "100%",
height: 40,
},
});
export default ExampleUsage;
5.1 效果说明
初始渲染时:
brightness = 50
,中央圆半径约为minRadius + (maxRadius - minRadius) * 0.5
。- 8 条光线固定,长度从圆边缘向外延伸。
拖动 Slider:
brightness
值从 0 变到 100,中央圆半径从最小 4 线性增大到最大 20。- 若使用
fillOpacity={p/100}
,圆心也会从完全透明逐步变为不透明。
当
brightness = 0
:- 中央圆半径 =
minRadius = 4
,如果fillOpacity = p/100 = 0
,则中央圆肉眼不可见;只有 8 条光线留在画布上。 - 如果
fillOpacity
恒为 1,那么即使亮度为 0,中央圆也会以半径 4 显示,表示“极低亮度”。
- 中央圆半径 =
当
brightness = 100
:- 中央圆半径 =
maxRadius = 20
,中央圆最大,光线从圆心边缘开始,显得“最明亮”。
- 中央圆半径 =
六、扩展思路与进阶优化
渐变光晕
可以利用
<Defs>
+<RadialGradient>
为中央圆添加径向渐变,让“亮度越高中心越亮、边缘渐暗”更真实。例如:import Svg, { Defs, RadialGradient, Stop, Circle, Line } from "react-native-svg"; ... <Svg ...> <Defs> <RadialGradient id="grad" cx="50%" cy="50%" r="50%"> <Stop offset="0%" stopColor="#FFD700" stopOpacity={1} /> <Stop offset="100%" stopColor="#FFD700" stopOpacity={0} /> </RadialGradient> </Defs> <Circle cx={cx} cy={cy} r={radius} fill="url(#grad)" /> {rays.map(...)} </Svg>
- 上述示例让圆心为纯色、边缘透明,形成“光晕”效果。
- 你也可以根据
percent
调整渐变半径或透明度,如:r={radius * 1.5}
或stopOpacity={p/100}
等。
高级光线动画
- 利用
react-native-reanimated
或Animated
API,让光线围绕中心缓慢旋转、闪烁或放射,产生动态“呼吸灯”效果。 - 例如可以对每条
<Line>
的strokeOpacity
做循环动画,使光线呈现“闪烁”。
- 利用
更多光线样式
- 如果不想用直线,可以把光线画成三角形、矩形或路径(Path)来实现不同形状。
- 例如,让光线在中心处更细,末端更粗,模拟“光芒发散”。
响应式布局
- 如果需要在不同分辨率、设备像素密度下保持图标清晰,可将
size
以PixelRatio
动态计算,或者使用width: 100%
+aspectRatio: 1
的方式让图标自动撑满父容器。
- 如果需要在不同分辨率、设备像素密度下保持图标清晰,可将
向量检索融合
- 如果你的场景涉及“文案语义”与“地理信息”双重约束,不仅可以在图标层面做“亮度可视化”,也可以在搜索推荐逻辑中兼容“语义搜索 + 地理过滤”的思路,让用户既能看到“当前光标亮度”也能获得对应的地理语义推荐。
七、总结
本文详细介绍了如何在 React Native 中利用 react-native-svg
灵活绘制一个可根据亮度百分比动态变化的“太阳”图标,关键思路与要点如下:
SVG 视图框与坐标系
- 以
viewBox="0 0 size size"
建立 0–size 的坐标系; - 中心点固定为
(size/2, size/2)
。
- 以
中央圆半径随百分比线性变化
const radius = minRadius + (maxRadius - minRadius) * (percent / 100);
- 当
percent = 0
或100
时,分别对应minRadius
和maxRadius
。 - 可选地利用
fillOpacity
映射透明度。
- 当
光线(Rays)坐标计算
- 以中心点为原点,使用极坐标
angle = i * (π/4)
,通过Math.cos
/Math.sin
计算起点与终点位置。 - 可以通过调整
rayLength
、strokeWidth
、strokeLinecap
等,快速定制光线样式。
- 以中心点为原点,使用极坐标
动态渲染
- 结合 React State、
Slider
或其它交互控件,让用户实时拖动调节%
,看到图标“亮度”变化。 - 若想更“有趣”,可使用
Animated
实现呼吸灯、旋转等动画效果。
- 结合 React State、
扩展思路
- 可以使用径向渐变 (
RadialGradient
) 实现更柔和的光晕效果; - 若业务需要展示“屏幕亮度”、“能量值”、“进度”等,完全可以复用此思路并做相应修改;
- 用类似方式还能绘制“音量条”、“温度指示器”等其他动态图标。
- 可以使用径向渐变 (
通过本文示例,你即可在 React Native 中快速实现一个实时响应亮度百分比的“太阳”图标组件,既能直观提示亮度,也可以作为动态交互控件的可视化表现。希望对你在 RN 中绘制矢量图形、制作自定义图标有所帮助。祝你学习愉快!
评论已关闭