我想在 30 秒后重新启动一个圆形进度条
Posted
技术标签:
【中文标题】我想在 30 秒后重新启动一个圆形进度条【英文标题】:I want to restart a circular progress bar after 30 seconds 【发布时间】:2022-01-02 23:07:20 【问题描述】:const useProgress = (maxTimeInSeconds = 30) =>
const [elapsedTime, setElapsedTime] = useState(0);
const [progress, setProgress] = useState(0);
useEffect(() =>
const intervalId = setInterval((callback) =>
if (progress < 1)
setElapsedTime((t) => t + 1);
, 1000);
return () => clearInterval(intervalId);
, []);
useEffect(() =>
setProgress(elapsedTime / maxTimeInSeconds);
console.log(elapsedTime);
, [elapsedTime]);
return progress;
;
const progress = useProgress();
在这里,我实现了将圆形进度条移动 30 秒的逻辑。我想要做的是在 30 秒后重新开始进度并保持循环 30 秒。
这里我已经粘贴了整个代码
import Observer from "mobx-react";
import React, Component, useEffect, useState from "react";
import
StyleSheet,
Text,
View,
ImageBackground,
TouchableOpacity,
Image,
Platform,
Dimensions,
Clipboard,
from "react-native";
import * as Progress from "react-native-progress";
import AppStore from "../../stores/AppStore";
import Iconpack from "../../utils/Iconpack";
import Theme from "../../utils/Theme";
import DeviceInfo from "react-native-device-info";
import CircularProgressBar from "./CircularProgressBar";
import Shadow from "react-native-shadow-2";
import Toast from "react-native-simple-toast";
import BoxShadow from "react-native-shadow";
import FastImage from "react-native-fast-image";
// import Clipboard from "@react-native-community/clipboard";
const hRem = AppStore.screenHeight / 812;
const wRem = AppStore.screenWidth / 375;
const OtpDetails = ( route, params, navigation ) =>
const id, title = route.params;
const useProgress = (maxTimeInSeconds = 30) =>
const [elapsedTime, setElapsedTime] = useState(0);
const [progress, setProgress] = useState(0);
useEffect(() =>
const intervalId = setInterval((callback) =>
if (progress < 1)
setElapsedTime((t) => t + 1);
, 1000);
return () => clearInterval(intervalId);
, []);
useEffect(() =>
console.log(elapsedTime);
setProgress(elapsedTime / maxTimeInSeconds);
if (elapsedTime == 30)
setProgress(elapsedTime / maxTimeInSeconds);
, [elapsedTime / maxTimeInSeconds]);
return progress;
;
const progress = useProgress();
const setTextIntoClipboard = async () =>
await Clipboard.setString(title);
Toast.show("OTP copied successfully!", 200);
;
const deviceName = DeviceInfo.getModel();
return (
<Observer>
() => (
<View style= flex: 1 >
<FastImage
style=styles.container
source=Iconpack.GLOBAL_BG
resizeMode=FastImage.resizeMode.cover
/>
<View style=styles.headerStyle>
<TouchableOpacity
onPress=() =>
navigation.navigate("OtpScreen");
>
<Image
style=styles.backButton
source=Iconpack.GLOBAL_BACK_BUTTON
/>
</TouchableOpacity>
<Text style=styles.headerTitle>title</Text>
<View style= flexDirection: "row" >
<TouchableOpacity>
<Image
style=[styles.editTrashButton, marginRight: 19 ]
source=Iconpack.EDIT_ICON
/>
</TouchableOpacity>
<TouchableOpacity>
<Image
style=styles.editTrashButton
source=Iconpack.DELETE_ICON
/>
</TouchableOpacity>
</View>
</View>
<View style=styles.circleOuter>
Platform.OS === "ios" ? (
<View style=styles.circleContainer>
<Progress.Circle
style= alignItems: "center"
progress=progress
// indeterminate=this.state.indeterminate
size=Dimensions.get("window").width * 0.561
thickness=10
borderWidth=0
endAngle=10
strokeCap="round"
color="#354659"
// indeterminateAnimationDuration=1
unfilledColor="#031830"
/>
</View>
) : (
<Shadow
distance=20
startColor="rgba(27, 100, 206, 0.5)"
radius=210
safeRender=true
>
<View style=styles.circleContainer>
<Progress.Circle
style= alignItems: "center", overflow: "hidden"
progress=progress
// indeterminate=this.state.indeterminate
size=Dimensions.get("window").width * 0.561
thickness=10
borderWidth=0
endAngle=10
strokeCap="round"
color="#354659"
indeterminateAnimationDuration=2000
unfilledColor="#031830"
/>
</View>
</Shadow>
)
<Text style=[styles.circleItem]>title</Text>
<TouchableOpacity
onPress=() =>
setTextIntoClipboard();
style=styles.copyItem
>
<Text style=styles.copyText>TAP TO COPY</Text>
</TouchableOpacity>
</View>
/* <View style=styles.circleOuter>
<View style=styles.circleContainer>
<Text style=styles.circleItem>title</Text>
</View>
<TouchableOpacity
onPress=() =>
copyToClipboard();
style=styles.copyItem
>
<Text style=styles.copyText>TAP TO COPY</Text>
</TouchableOpacity> */
/* </View> */
</View>
)
</Observer>
);
;
export default OtpDetails;
const styles = StyleSheet.create(
container:
flex: 1,
position: "absolute",
top: 0,
right: 0,
left: 0,
bottom: 0,
backgroundColor: "#021831",
,
headerStyle:
flexDirection: "row",
marginTop: Platform.OS === "ios" ? 56 : 40,
marginHorizontal: wRem * 26.66,
justifyContent: "space-between",
alignItems: "center",
,
backButton:
width: wRem * 12,
height: hRem * 21,
,
editTrashButton:
width: wRem * 23,
height: hRem * 23,
,
headerTitle:
...Theme.encodeSansMed2,
color: "#FFFFFF",
fontWeight: "600",
marginLeft: wRem * 50,
,
circleOuter:
flex: 1,
justifyContent: "center",
alignItems: "center",
,
circleContainer:
borderRadius:
Math.round(
Dimensions.get("window").width + Dimensions.get("window").height
) / 2,
width: Dimensions.get("window").width * 0.56,
height: Dimensions.get("window").width * 0.56,
justifyContent: "center",
backgroundColor: "black",
shadowColor: "rgba(27, 100, 206, 0.5)",
shadowOffset:
width: 0,
height: 0,
,
shadowOpacity: 30,
shadowRadius: 30,
,
circleItem:
color: "white",
...Theme.encodeSansMed7,
textAlign: "center",
position: "absolute",
width: "100%",
paddingBottom: 100,
,
copyItem:
justifyContent: "center",
alignItems: "center",
marginTop: hRem * 64,
,
copyText:
color: "#3D4756",
...Theme.encodeSansMed4,
,
);
所有样式,最重要的是我正在使用第三方库 react-native-progress 谢谢
【问题讨论】:
如果你正在编写一个 TOTP 应用程序,视觉组件不应该由代码到期的实际时间驱动,而不是......相反吗? 【参考方案1】:progress
不需要是状态原子,因为它总是可以从经过的时间和最大时间明确地计算出来。
使用模(余数)运算符,您可以让观察到的进度在最长时间后始终循环回 0。
const useProgress = (maxTimeInSeconds = 30) =>
const [elapsedTime, setElapsedTime] = useState(0);
useEffect(() =>
const intervalId = setInterval(() =>
setElapsedTime((t) => t + 1);
, 1000);
return () => clearInterval(intervalId);
, []);
return (elapsedTime % maxTimeInSeconds) / maxTimeInSeconds;
;
const progress = useProgress();
或者,如果您想要更准确的基于挂钟的计时:
const useProgress = (maxTimeInSeconds = 30) =>
const [initialTime] = useState(() => +new Date());
const [counter, setCounter] = useState(0);
useEffect(() =>
const intervalId = setInterval(() =>
setCounter((t) => t + 1);
, 1000);
return () => clearInterval(intervalId);
, []);
const elapsedTime = Math.floor(((+new Date()) - initialTime) / 1000);
if (elapsedTime == 30)
return maxTimeInSeconds;
return (elapsedTime % maxTimeInSeconds) / maxTimeInSeconds;
;
这会将进度开始锁定到组件的挂载时间,即使setInterval
不准确,也应该可以更好地工作。
【讨论】:
非常感谢,但是当它达到 30 秒时它会回来并重新开始我想要的是,它会继续顺时针移动@AkX 什么会顺时针移动?这里没有视觉元素。 请找到整个代码 非常感谢您的回复,但有一个问题,进度条从 0 开始,到 28 秒结束,然后又回来,根本没有达到 30 秒@AKX 如果这是一个 (T)OTP 应用程序,无论如何你都不应该依赖setInterval
- 不能保证它会以 1000 毫秒的间隔运行。以上是关于我想在 30 秒后重新启动一个圆形进度条的主要内容,如果未能解决你的问题,请参考以下文章