我在世博会上有一个工作循环计时器。它由 2 个计时器组成,当一个计时器完成时,下一个计时器开始,然后它们进入循环。每个人都有自己独立的时间。
我想添加一个初始时间,以便它们按照我偏好的“时钟时间”开始运行。
import * as React from 'react';
import { Text, View, StyleSheet, Animated, Button } from 'react-native';
import Constants from 'expo-constants';
import { CountdownCircleTimer } from 'react-native-countdown-circle-timer';
const duration = [10, 20];
export default function App() {
const [isPlaying, setIsPlaying] = React.useState(true);
const [timeIndex, setTimeIndex] = React.useState(0);
return (
<View style={styles.container}>
<CountdownCircleTimer
key={timeIndex}
isPlaying={isPlaying}
duration={duration[timeIndex]}
colors={[
['#FFFF00', 0.4],
['#0000ff', 0.4],
]}
onComplete={() => {
setTimeIndex((index) => {
let newIndex = index + 1;
if (newIndex >= duration.length) {
newIndex = 0;
}
return newIndex;
});
return [true];
}}>
{({ remainingTime, animatedColor }) => (
<Animated.Text style={{ color: animatedColor, fontSize: 40 }}>
{remainingTime}
</Animated.Text>
)}
</CountdownCircleTimer>
<Button
title="Toggle Playing"
onPress={() => setIsPlaying((prev) => !prev)}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
});
我创建了一个钩子
useClock
,它可以配置其间隔运行的频率、间隔何时开始,并提供间隔和开始时间之间可以经过的时间上限:
import { useState, useRef, useEffect } from 'react';
import { convertDate } from '../helpers/time';
export default function useClock({
intervalInSeconds = 1,
atMoment,
startTime,
maxTimeElaspedInSeconds = 15,
}) {
const hasStarted = useRef(false);
// hook doesnt need to return a time but I was using this state for debugging
const [time, setTime] = useState(new Date());
useEffect(() => {
const interval = setInterval(() => {
const newTime = new Date();
const timeDiff = newTime.getTime() - startTime.getTime();
// if waiting for next interval would call atMoment too late
// call atMoment in current interval
const shouldRun =
timeDiff + intervalInSeconds * 1000 > maxTimeElaspedInSeconds * 1000;
if (shouldRun && !hasStarted.current) {
console.log('Running at', convertDate(newTime));
hasStarted.current = true;
atMoment();
}
setTime(newTime);
}, intervalInSeconds * 1000);
return () => {
clearInterval(interval);
hasStarted.current = false;
};
}, [atMoment, startTime, intervalInSeconds, maxTimeElaspedInSeconds]);
return time;
}
使用此功能,您可以在指定时间运行函数:
import { Text, SafeAreaView, StyleSheet, Button, Animated } from 'react-native';
import useClock from './hooks/useClock';
import { useRef, useState, useEffect } from 'react';
import { createFutureDateInMinutes,convertDate } from './helpers/time';
import { CountdownCircleTimer } from 'react-native-countdown-circle-timer';
const duration = [10,20]
const intervalInSeconds = 15
export default function App() {
// todo: you will have to convert time string into a date object
// for testing purposes I just set the start time to a 30 seconds
// after the first render
const startTime = useRef(createFutureDateInMinutes(30/60)).current;
const [isPlaying, setIsPlaying] = useState(false);
const [timeIndex, setTimeIndex] = useState(0);
// useTime tries to estimate what the next
const time = useClock({
startTime,
// maximum time allowed to elaspe before calling atMoment
maxTimeElaspedInSeconds:1,
// Im not sure of performance effect of running an interval
// every second has, but depending on the complexity of your
// app you might need to make this variable as big as possible
// this hook guesses how much time will pass before its called again
// and if it exceeds maxTimeElaspedInSeconds, will call the hook earlier
intervalInSeconds,
atMoment: () => {
console.log('start time reached')
setIsPlaying(true);
},
});
return (
<SafeAreaView style={styles.container}>
<Text>Start time:{convertDate(startTime)}</Text>
<Text>Current time:{convertDate(time)}</Text>
<CountdownCircleTimer
key={timeIndex}
isPlaying={isPlaying}
duration={duration[timeIndex]}
colors={[
['#FFFF00', 0.4],
['#0000ff', 0.4],
]}
onComplete={() => {
setTimeIndex((index) => {
let newIndex = index + 1;
if (newIndex >= duration.length) {
newIndex = 0;
}
return newIndex;
});
return [true];
}}>
{({ remainingTime, animatedColor }) => (
<Animated.Text style={{ color: animatedColor, fontSize: 40 }}>
{remainingTime}
</Animated.Text>
)}
</CountdownCircleTimer>
<Button
title="Toggle Playing"
onPress={() => setIsPlaying((prev) => !prev)}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#ecf0f1',
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});