【React Native鸿蒙跨平台开发】如何在React Native鸿蒙中,将内容加载时提供一个友好的视觉反馈,比如加载时的占位效果,骨架屏可以显著提高用户体验
本文介绍了在React Native中实现骨架屏组件的两种方法。第一种是使用现成库react-native-skeleton-placeholder快速实现,通过示例代码展示了如何创建包含头部和内容区域的骨架屏。第二种是自定义实现方案,利用Animated模块创建透明度动画效果,提供了更灵活的定制选项。文章指出,现成库适合快速开发,而自定义方案能满足特定需求。最后附带的真实案例代码展示了如何在应用
在React Native中开发一个骨架屏(Skeleton)组件,通常是为了在内容加载时提供一个友好的视觉反馈,比如加载时的占位效果。骨架屏可以显著提高用户体验,尤其是在网络请求较慢或内容动态加载时。下面是如何实现一个基本的骨架屏组件的步骤:
- 创建Skeleton组件
首先,你需要创建一个Skeleton组件。这个组件将包含多个视图(View),这些视图将模拟实际内容的布局和样式,但不会包含实际的数据。
步骤1: 安装必要的库
如果你想要一个更复杂的骨架屏效果,可以使用一些现有的库,如react-native-skeleton-placeholder。你可以通过npm或yarn来安装它:
npm install react-native-skeleton-placeholder
或者
yarn add react-native-skeleton-placeholder
步骤2: 使用Skeleton组件
使用react-native-skeleton-placeholder,你可以很容易地实现一个骨架屏。下面是一个基本的使用示例:
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import SkeletonPlaceholder from "react-native-skeleton-placeholder";
const Skeleton = () => {
return (
<SkeletonPlaceholder>
<View style={styles.container}>
<View style={styles.header}>
<SkeletonPlaceholder.Item flex={1} height={150} />
</View>
<View style={styles.content}>
<SkeletonPlaceholder.Item flex={1} height={20} width={200} />
<SkeletonPlaceholder.Item flex={1} height={20} width={150} />
<SkeletonPlaceholder.Item flex={1} height={20} width={100} />
</View>
</View>
</SkeletonPlaceholder>
);
};
const styles = StyleSheet.create({
container: {
padding: 16,
},
header: {
marginBottom: 16,
},
content: {
flexDirection: 'column',
justifyContent: 'space-between',
}
});
export default Skeleton;
- 自定义Skeleton组件
如果你想要更灵活的控制或者完全自定义骨架屏,你可以直接使用View和Animated模块来创建自己的骨架屏。例如:
import React, { useState, useEffect } from 'react';
import { View, StyleSheet, Animated } from 'react-native';
const CustomSkeleton = () => {
const [fadeAnim] = useState(new Animated.Value(0)); // 初始化透明度值
useEffect(() => {
Animated.timing(fadeAnim, { // 开始动画循环
toValue: 1, // 目标透明度为1(完全不透明)
duration: 500, // 动画持续时间(毫秒)
useNativeDriver: true, // 使用原生驱动提高性能(如果可能)
}).start(); // 开始动画循环
}, [fadeAnim]); // 当fadeAnim改变时重新开始动画(在这个例子中,只在组件挂载时触发)
return (
<Animated.View style={{...styles.skeleton, opacity: fadeAnim}}>
{/* 你的骨架屏内容 */}
</Animated.View>
);
};
const styles = StyleSheet.create({
skeleton: {
backgroundColor: 'ddd', // 骨架屏颜色,可以根据需要调整为灰色或其他颜色以模拟加载状态
// 其他样式,比如高度、宽度等,根据你的布局需求设置
}
});
在这个自定义的例子中,你可以根据需要添加多个View来模拟不同部分的加载状态。每个View都可以通过Animated.View来控制其透明度变化,从而达到骨架屏的动态效果。通过调整动画参数(如duration),你可以控制加载动画的速度。
结论:
选择哪种方法取决于你的具体需求。如果你需要一个快速实现的解决方案,使用现成的库如react-native-skeleton-placeholder是个不错的选择。如果你需要更多的定制化,那么自己编写一个骨架屏组件会更加灵活。无论哪种方式,骨架屏都能显著提升应用在内容加载时的用户体验。
真实案例代码演示:
// App.tsx
import React, { useState, useEffect } from 'react';
import {
View,
Text,
StyleSheet,
ScrollView,
SafeAreaView,
Image,
Dimensions,
TouchableOpacity,
Animated,
Easing
} from 'react-native';
// Base64 Icons for progress components
const PROGRESS_ICONS = {
download: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFHmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTAxLTAzVDE2OjU1OjQ5KzA4OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0wMS0wM1QxNjo1NjoxNCswODowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0wMS0wM1QxNjo1NjoxNCswODowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoyZjA5Y2Y0OS01YjVlLWY5NDctYjg3Yi0yZDM5YjQxMjQyZWIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MmYwOWNmNDktNWI1ZS1mOTQ3LWI4N2ItMmQzOWI0MTI0MmViIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6MmYwOWNmNDktNWI1ZS1mOTQ3LWI4N2ItMmQzOWI0MTI0MmViIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDoyZjA5Y2Y0OS01YjVlLWY5NDctYjg3Yi0yZDM5YjQxMjQyZWIiIHN0RXZ0OndoZW49IjIwMTktMDEtMDNUMTY6NTU6NDlaIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOCAoV2luZG93cykiLz4gPC9yZGY6U2VxPiA8L3htcE1NOkhpc3Rvcnk+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAA//+gB5ZbAAAJRElEQVRoBe1ZaWxUVRQ+97030+k6U6Z0hk5npksptKW0bFJAWRB3UEFFjQuJKIoLJCZqYhRjNC5RFCMuaKJREhMXjAuKggsuRI0bGkEpWymltNBS2ulMZ9qZmfbe9+699715MzPTmdLS6STnfPPee+fc755zz3nnPcNwHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMx......',
upload: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFHmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTAxLTAzVDE2OjU2OjIwKzA4OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0wMS0wM1QxNjo1Njo0NSswODowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0wMS0wM1QxNjo1Njo0NSswODowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoyZjE5Y2Y0OS01YjVlLWY5NDctYjg3Yi0yZDM5YjQxMjQyZWIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MmYxOWNmNDktNWI1ZS1mOTQ3LWI4N2ItMmQzOWI0MTI0MmViIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6MmYxOWNmNDktNWI1ZS1mOTQ3LWI4N2ItMmQzOWI0MTI0MmViIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDoyZjE5Y2Y0OS01YjVlLWY5NDctYjg3Yi0yZDM5YjQxMjQyZWIiIHN0RXZ0OndoZW49IjIwMTktMDEtMDNUMTY6NTY6MjBaIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOCAoV2luZG93cykiLz4gPC9yZGY6U2VxPiA8L3htcE1NOkhpc3Rvcnk+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAA//+gB5ZbAAAJRElEQVRoBe1ZaWxUVRQ+97030+k6U6Z0hk5npksptKW0bFJAWRB3UEFFjQuJKIoLJCZqYhRjNC5RFCMuaKJREhMXjAuKggsuRI0bGkEpWymltNBS2ulMZ9qZmfbe9+699715MzPTmdLS6STnfPPee+fc755zz3nnPcNwHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMx......',
success: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFHmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTAxLTAzVDE2OjU2OjUxKzA4OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0wMS0wM1QxNjo1NzoxNiswODowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0wMS0wM1QxNjo1NzoxNiswODowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoyZjI5Y2Y0OS01YjVlLWY5NDctYjg3Yi0yZDM5YjQxMjQyZWIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MmYyOWNmNDktNWI1ZS1mOTQ3LWI4N2ItMmQzOWI0MTI0MmViIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6MmYyOWNmNDktNWI1ZS1mOTQ3LWI4N2ItMmQzOWI0MTI0MmViIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDoyZjI5Y2Y0OS01YjVlLWY5NDctYjg3Yi0yZDM5YjQxMjQyZWIiIHN0RXZ0OndoZW49IjIwMTktMDEtMDNUMTY6NTY6NTFaIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOCAoV2luZG93cykiLz4gPC9yZGY6U2VxPiA8L3htcE1NOkhpc3Rvcnk+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAA//+gB5ZbAAAJRElEQVRoBe1ZaWxUVRQ+97030+k6U6Z0hk5npksptKW0bFJAWRB3UEFFjQuJKIoLJCZqYhRjNC5RFCMuaKJREhMXjAuKggsuRI0bGkEpWymltNBS2ulMZ9qZmfbe9+699715MzPTmdLS6STnfPPee+fc755zz3nnPcNwHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMx......',
error: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFHmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTAxLTAzVDE2OjU3OjIyKzA4OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0wMS0wM1QxNjo1Nzo0OCswODowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0wMS0wM1QxNjo1Nzo0OCswODowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoyZjM5Y2Y0OS01YjVlLWY5NDctYjg3Yi0yZDM5YjQxMjQyZWIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MmYzOWNmNDktNWI1ZS1mOTQ3LWI4N2ItMmQzOWI0MTI0MmViIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6MmYzOWNmNDktNWI1ZS1mOTQ3LWI4N2ItMmQzOWI0MTI0MmViIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDoyZjM5Y2Y0OS01YjVlLWY5NDctYjg3Yi0yZDM5YjQxMjQyZWIiIHN0RXZ0OndoZW49IjIwMTktMDEtMDNUMTY6NTc6MjJaIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOCAoV2luZG93cykiLz4gPC9yZGY6U2VxPiA8L3htcE1NOkhpc3Rvcnk+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAA//+gB5ZbAAAJRElEQVRoBe1ZaWxUVRQ+97030+k6U6Z0hk5npksptKW0bFJAWRB3UEFFjQuJKIoLJCZqYhRjNC5RFCMuaKJREhMXjAuKggsuRI0bGkEpWymltNBS2ulMZ9qZmfbe9+699715MzPTmdLS6STnfPPee+fc755zz3nnPcNwHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMx......',
refresh: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFHmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTAxLTAzVDE2OjU3OjUzKzA4OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0wMS0wM1QxNjo1ODoxOCswODowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0wMS0wM1QxNjo1ODoxOCswODowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoyZjQ5Y2Y0OS01YjVlLWY5NDctYjg3Yi0yZDM5YjQxMjQyZWIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MmY0OWNmNDktNWI1ZS1mOTQ3LWI4N2ItMmQzOWI0MTI0MmViIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6MmY0OWNmNDktNWI1ZS1mOTQ3LWI4N2ItMmQzOWI0MTI0MmViIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDoyZjQ5Y2Y0OS01YjVlLWY5NDctYjg3Yi0yZDM5YjQxMjQyZWIiIHN0RXZ0OndoZW49IjIwMTktMDEtMDNUMTY6NTc6NTNaIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOCAoV2luZG93cykiLz4gPC9yZGY6U2VxPiA8L3htcE1NOkhpc3Rvcnk+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAA//+gB5ZbAAAJRElEQVRoBe1ZaWxUVRQ+97030+k6U6Z0hk5npksptKW0bFJAWRB3UEFFjQuJKIoLJCZqYhRjNC5RFCMuaKJREhMXjAuKggsuRI0bGkEpWymltNBS2ulMZ9qZmfbe9+699715MzPTmdLS6STnfPPee+fc755zz3nnPcNwHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMdxHMx......'
};
// 进度条组件
interface ProgressProps {
percent: number;
status?: 'normal' | 'active' | 'success' | 'exception';
showInfo?: boolean;
strokeWidth?: number;
strokeColor?: string;
trailColor?: string;
format?: (percent: number) => string;
type?: 'line' | 'circle';
width?: number;
animationDuration?: number;
}
const Progress: React.FC<ProgressProps> = ({
percent = 0,
status = 'normal',
showInfo = true,
strokeWidth = 10,
strokeColor,
trailColor = '#334155',
format,
type = 'line',
width = 120,
animationDuration = 300
}) => {
const [animatedPercent] = useState(new Animated.Value(0));
useEffect(() => {
Animated.timing(animatedPercent, {
toValue: percent,
duration: animationDuration,
easing: Easing.out(Easing.ease),
useNativeDriver: false
}).start();
}, [percent, animationDuration]);
const getStatusColor = () => {
switch (status) {
case 'success':
return '#10b981';
case 'exception':
return '#ef4444';
case 'active':
return '#3b82f6';
default:
return strokeColor || '#60a5fa';
}
};
const getColor = getStatusColor();
const formattedText = format ? format(percent) : `${Math.round(percent)}%`;
if (type === 'circle') {
return (
<View style={styles.circleProgressContainer}>
<View style={[
styles.circleTrail,
{
width: width,
height: width,
borderRadius: width / 2,
backgroundColor: trailColor,
borderWidth: strokeWidth,
borderColor: trailColor
}
]}>
<Animated.View
style={[
styles.circleFill,
{
width: width,
height: width,
borderRadius: width / 2,
borderWidth: strokeWidth,
borderColor: getColor,
transform: [{ rotate: '-90deg' }],
borderLeftColor: 'transparent',
borderBottomColor: 'transparent',
borderRightColor: 'transparent'
}
]}
/>
</View>
{showInfo && (
<View style={[styles.circleInfo, { width: width, height: width }]}>
<Text style={[styles.circleText, { color: getColor }]}>{formattedText}</Text>
</View>
)}
</View>
);
}
return (
<View style={styles.progressContainer}>
<View style={styles.progressBar}>
<View
style={[
styles.progressTrail,
{
height: strokeWidth,
backgroundColor: trailColor,
borderRadius: strokeWidth / 2
}
]}
>
<Animated.View
style={[
styles.progressFill,
{
height: strokeWidth,
backgroundColor: getColor,
borderRadius: strokeWidth / 2,
width: animatedPercent.interpolate({
inputRange: [0, 100],
outputRange: ['0%', '100%']
})
}
]}
/>
</View>
</View>
{showInfo && (
<View style={styles.progressInfo}>
<Text style={[styles.progressText, { color: getColor }]}>{formattedText}</Text>
</View>
)}
</View>
);
};
// 主应用组件
const App = () => {
const [progressValues, setProgressValues] = useState([
{ id: 1, percent: 30, status: 'normal' as const, label: '文件下载' },
{ id: 2, percent: 65, status: 'active' as const, label: '数据上传' },
{ id: 3, percent: 100, status: 'success' as const, label: '任务完成' },
{ id: 4, percent: 45, status: 'exception' as const, label: '处理错误' }
]);
const [dynamicProgress, setDynamicProgress] = useState(0);
const [isRunning, setIsRunning] = useState(false);
useEffect(() => {
let interval: NodeJS.Timeout;
if (isRunning) {
interval = setInterval(() => {
setDynamicProgress(prev => {
if (prev >= 100) {
setIsRunning(false);
return 100;
}
return prev + 1;
});
}, 100);
}
return () => clearInterval(interval);
}, [isRunning]);
const startProgress = () => {
if (!isRunning) {
setIsRunning(true);
setDynamicProgress(0);
}
};
const resetProgress = () => {
setIsRunning(false);
setDynamicProgress(0);
};
return (
<SafeAreaView style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerTitle}>进度条组件演示</Text>
<Text style={styles.headerSubtitle}>暗黑风格进度条展示</Text>
</View>
<ScrollView contentContainerStyle={styles.contentContainer}>
<View style={styles.section}>
<Text style={styles.sectionTitle}>线性进度条</Text>
{progressValues.map(item => (
<View key={item.id} style={styles.progressItem}>
<View style={styles.progressLabel}>
<Image
source={{ uri: PROGRESS_ICONS[item.status === 'success' ? 'success' :
item.status === 'exception' ? 'error' :
item.status === 'active' ? 'upload' : 'download'] }}
style={[styles.progressIcon, {
tintColor: item.status === 'success' ? '#10b981' :
item.status === 'exception' ? '#ef4444' :
item.status === 'active' ? '#3b82f6' : '#60a5fa'
}]}
/>
<Text style={styles.progressLabelText}>{item.label}</Text>
</View>
<Progress
percent={item.percent}
status={item.status}
strokeWidth={12}
showInfo={true}
animationDuration={500}
/>
</View>
))}
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>环形进度条</Text>
<View style={styles.circleProgressSection}>
<View style={styles.circleProgressItem}>
<Progress
percent={75}
type="circle"
size={100}
strokeWidth={12}
strokeColor="#3b82f6"
showInfo={true}
width={100}
/>
<Text style={styles.circleProgressLabel}>下载进度</Text>
</View>
<View style={styles.circleProgressItem}>
<Progress
percent={45}
type="circle"
size={100}
strokeWidth={12}
strokeColor="#10b981"
showInfo={true}
width={100}
/>
<Text style={styles.circleProgressLabel}>上传进度</Text>
</View>
<View style={styles.circleProgressItem}>
<Progress
percent={100}
type="circle"
size={100}
strokeWidth={12}
strokeColor="#ef4444"
showInfo={true}
width={100}
/>
<Text style={styles.circleProgressLabel}>错误状态</Text>
</View>
</View>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>动态进度演示</Text>
<View style={styles.dynamicProgressSection}>
<Progress
percent={dynamicProgress}
status={dynamicProgress === 100 ? 'success' : 'active'}
strokeWidth={15}
showInfo={true}
animationDuration={100}
/>
<View style={styles.dynamicButtons}>
<TouchableOpacity
style={[styles.dynamicButton, styles.startButton]}
onPress={startProgress}
disabled={isRunning}
>
<Text style={styles.dynamicButtonText}>
{isRunning ? '运行中...' : '开始'}
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.dynamicButton, styles.resetButton]}
onPress={resetProgress}
>
<Text style={styles.dynamicButtonText}>重置</Text>
</TouchableOpacity>
</View>
</View>
</View>
<View style={styles.featuresSection}>
<Text style={styles.featuresTitle}>功能特性</Text>
<View style={styles.featureList}>
<View style={styles.featureItem}>
<Text style={styles.featureBullet}>•</Text>
<Text style={styles.featureText}>支持线性和环形两种进度展示</Text>
</View>
<View style={styles.featureItem}>
<Text style={styles.featureBullet}>•</Text>
<Text style={styles.featureText}>四种状态样式(正常、活动、成功、异常)</Text>
</View>
<View style={styles.featureItem}>
<Text style={styles.featureBullet}>•</Text>
<Text style={styles.featureText}>平滑动画过渡效果</Text>
</View>
<View style={styles.featureItem}>
<Text style={styles.featureBullet}>•</Text>
<Text style={styles.featureText}>可自定义颜色、粗细和尺寸</Text>
</View>
</View>
</View>
<View style={styles.usageSection}>
<Text style={styles.usageTitle}>使用说明</Text>
<Text style={styles.usageText}>
进度条组件可用于展示任务进度、文件传输状态等场景。
支持多种样式和状态,可根据业务需求灵活配置。
暗黑风格设计适合夜间模式或专业应用场景。
</Text>
</View>
</ScrollView>
<View style={styles.footer}>
<Text style={styles.footerText}>© 2023 进度条组件. All rights reserved.</Text>
</View>
</SafeAreaView>
);
};
const { width, height } = Dimensions.get('window');
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#0f172a',
},
header: {
backgroundColor: '#1e293b',
paddingTop: 20,
paddingBottom: 25,
paddingHorizontal: 20,
borderBottomWidth: 1,
borderBottomColor: '#334155',
},
headerTitle: {
fontSize: 26,
fontWeight: '700',
color: '#f1f5f9',
textAlign: 'center',
marginBottom: 5,
},
headerSubtitle: {
fontSize: 15,
color: '#94a3b8',
textAlign: 'center',
},
contentContainer: {
padding: 20,
},
section: {
marginBottom: 30,
},
sectionTitle: {
fontSize: 22,
fontWeight: '700',
color: '#e2e8f0',
marginBottom: 20,
paddingLeft: 10,
borderLeftWidth: 4,
borderLeftColor: '#3b82f6',
},
progressItem: {
marginBottom: 25,
},
progressLabel: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 10,
},
progressIcon: {
width: 20,
height: 20,
marginRight: 10,
},
progressLabelText: {
fontSize: 16,
fontWeight: '600',
color: '#cbd5e1',
},
progressContainer: {
flexDirection: 'row',
alignItems: 'center',
},
progressBar: {
flex: 1,
},
progressTrail: {
backgroundColor: '#334155',
borderRadius: 5,
overflow: 'hidden',
},
progressFill: {
height: '100%',
},
progressInfo: {
marginLeft: 15,
minWidth: 40,
},
progressText: {
fontSize: 16,
fontWeight: '600',
},
circleProgressContainer: {
position: 'relative',
justifyContent: 'center',
alignItems: 'center',
},
circleTrail: {
position: 'absolute',
},
circleFill: {
position: 'absolute',
},
circleInfo: {
position: 'absolute',
justifyContent: 'center',
alignItems: 'center',
},
circleText: {
fontSize: 18,
fontWeight: '700',
},
circleProgressSection: {
flexDirection: 'row',
justifyContent: 'space-around',
alignItems: 'center',
},
circleProgressItem: {
alignItems: 'center',
},
circleProgressLabel: {
marginTop: 10,
fontSize: 14,
color: '#94a3b8',
},
dynamicProgressSection: {
backgroundColor: '#1e293b',
borderRadius: 16,
padding: 20,
borderWidth: 1,
borderColor: '#334155',
},
dynamicButtons: {
flexDirection: 'row',
justifyContent: 'center',
marginTop: 20,
},
dynamicButton: {
paddingHorizontal: 25,
paddingVertical: 12,
borderRadius: 8,
marginHorizontal: 10,
},
startButton: {
backgroundColor: '#3b82f6',
},
resetButton: {
backgroundColor: '#334155',
},
dynamicButtonText: {
fontSize: 16,
fontWeight: '600',
color: '#f1f5f9',
},
featuresSection: {
backgroundColor: '#1e293b',
borderRadius: 16,
padding: 20,
marginBottom: 30,
borderWidth: 1,
borderColor: '#334155',
},
featuresTitle: {
fontSize: 20,
fontWeight: '700',
color: '#f1f5f9',
marginBottom: 15,
textAlign: 'center',
},
featureList: {
paddingLeft: 10,
},
featureItem: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 12,
},
featureBullet: {
fontSize: 18,
color: '#3b82f6',
marginRight: 10,
},
featureText: {
fontSize: 16,
color: '#cbd5e1',
flex: 1,
},
usageSection: {
backgroundColor: '#1e293b',
borderRadius: 16,
padding: 20,
borderWidth: 1,
borderColor: '#334155',
},
usageTitle: {
fontSize: 20,
fontWeight: '700',
color: '#f1f5f9',
marginBottom: 15,
textAlign: 'center',
},
usageText: {
fontSize: 16,
color: '#cbd5e1',
lineHeight: 24,
textAlign: 'center',
},
footer: {
paddingVertical: 15,
alignItems: 'center',
borderTopWidth: 1,
borderTopColor: '#334155',
backgroundColor: '#1e293b',
},
footerText: {
fontSize: 14,
color: '#94a3b8',
fontWeight: '500',
},
});
export default App;
这段React Native进度条组件代码实现了一个高度可配置的双模式进度指示系统,支持线性进度条和环形进度条两种可视化方式。组件通过状态管理和动画系统来实现平滑的进度过渡效果,使用Animated API在指定的持续时间内从当前进度值动画过渡到目标进度值。状态配置系统通过getStatusColor函数将四种状态类型映射到对应的颜色编码:成功状态使用翠绿色,异常状态使用警示红色,活动状态采用鲜艳蓝色,正常状态则使用默认蓝色或允许自定义颜色。
在鸿蒙系统适配方面,这段代码面临着深刻的架构差异。React Native的进度条实现依赖于基础组件的样式组合和Animated动画系统,通过useEffect监听进度值变化来触发动画过渡。环形进度条采用巧妙的CSS技巧实现,通过设置不同方向的边框颜色和旋转变换来模拟环形填充效果。这种实现方式虽然灵活,但在性能上存在一定局限,特别是当需要同时更新多个进度条时,JavaScript线程的负载会成为瓶颈。

鸿蒙的ArkUI框架提供了Progress组件作为系统级的进度指示器实现,采用声明式配置方式,开发者只需设置进度值、类型和样式参数,系统会自动处理进度显示和动画过渡。鸿蒙的Progress组件支持线性、环型和刻度条三种类型,每种类型都有丰富的自定义选项。线性进度条可以配置圆角半径、渐变色和条纹效果;环形进度条支持多种端点样式和旋转动画;刻度条则能够显示离散的进度级别。在性能优化方面,鸿蒙的Progress组件在Native层实现,避免了JavaScript桥接带来的性能损耗,能够提供更流畅的动画体验。
鸿蒙的动画系统与React Native有本质区别。React Native的动画在JavaScript线程计算,然后通过桥接传递给原生组件。而鸿蒙的动画系统在Native层执行,能够实现更精确的时间控制和更高效的资源利用。特别是在处理连续的进度更新时,鸿蒙的架构优势更加明显。
打包
接下来通过打包命令npn run harmony将reactNative的代码打包成为bundle,这样可以进行在开源鸿蒙OpenHarmony中进行使用。

打包之后再将打包后的鸿蒙OpenHarmony文件拷贝到鸿蒙的DevEco-Studio工程目录去:

最后运行效果图如下显示:

欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
更多推荐



所有评论(0)