Et si on faisait le Flutter Clock Contest en React Native

Horloge de Dominik Roszkowski
import React from ‘react’;import { StyleSheet, View, Text } from ‘react-native’;export default class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {
}
}
componentDidMount() {
}
render() {
return (
<View style={styles.container}>
<Text>Et si on construisait une horloge ?</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: ‘center’,
justifyContent: ‘center’
}
});
import React from ‘react’;
import { StyleSheet, View, Dimensions } from ‘react-native’;
export default class Clock extends React.Component {
[…]
render() {
let frameSize = Dimensions.get('screen').width > Dimensions.get('screen').height ? Dimensions.get('screen').height : Dimensions.get('screen').width;
frameSize = frameSize - 40;
const frameWidth = 20;
const clockSize = frameSize-2*frameWidth-36;
return (
<View style={styles.container}>
<View style={[styles.shadow, styles.frame, { width: frameSize, height: frameSize, borderRadius: frameSize / 2 }]}>
<View style={[styles.innerShadow, styles.innerFrame, { width: frameSize, height: frameSize, borderRadius: frameSize / 2, borderWidth: frameWidth }]}>
<View style={[{ width: clockSize, height: clockSize, borderRadius: clockSize / 2, justifyContent: 'center', alignItems: 'center' }]}>
</View>
</View>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
[...]
shadow: {
shadowColor: “#000”,
shadowOffset: {
width: 0,
height: 5,
},
shadowOpacity: 0.27,
shadowRadius: 4.65,
elevation: 6
},
frame: {
backgroundColor: '#fff',
justifyContent: 'center',
alignItems: 'center'
},
innerShadow: {
shadowColor: “#000”,
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.23,
shadowRadius: 2.62,
elevation: 4
},
innerFrame: {
overflow: 'hidden',
borderColor: '#fff',
justifyContent: 'center',
alignItems: 'center'
}
});
Le cadre de notre horloge
import React from ‘react’;
import { StyleSheet, View, Dimensions, TouchableOpacity, Text} from ‘react-native’;
const lightModeTheme = {
background:’#E0E0E0',
shadow:'#9E9E9E',
frame:’#E0E0E0',
text:'#123456'
}
const darkModeTheme = {
background:’#3C4043',
shadow:'#212121',
frame:’#3C4043',
text:'#fff'
}
export default class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {
isLightMode:true
}
}
[...]
render() {
const colorTheme = this.state.isLightMode ? lightModeTheme : darkModeTheme;
const colorName = this.state.isLightMode ? 'Light Mode' : 'Dark Mode';
[...]
return (
<View style={[styles.container, {backgroundColor:colorTheme.background}]}>
<View style={[styles.shadow, styles.frame, { shadowColor: colorTheme.shadow, backgroundColor: colorTheme.frame, width: frameSize, height: frameSize, borderRadius: frameSize / 2 }]}>
<View style={[styles.innerShadow, styles.innerFrame, { shadowColor: colorTheme.shadow, width: frameSize, height: frameSize, borderRadius: frameSize / 2, borderWidth: frameWidth, borderColor: theme.frame }]}>
<View style={[{ width: clockSize, height: clockSize, borderRadius: clockSize / 2, justifyContent: ‘center’, alignItems: ‘center’ }]}>
</View>
</View>
</View>
<TouchableOpacity style={styles.modeButton} onPress={() => this.setState({isLightMode:!this.state.isLightMode})}>
<Text style={{color:colorTheme.text}}>{colorName}</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: ‘center’,
justifyContent: ‘center’
},
shadow: {
shadowOffset: {
width: 0,
height: 5,
},
shadowOpacity: 1,
shadowRadius: 4.65,
elevation: 6
},
frame: {
justifyContent: ‘center’,
alignItems: ‘center’
},
innerShadow: {
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 1,
shadowRadius: 2.62,
elevation: 4
},
innerFrame: {
overflow: ‘hidden’,
justifyContent: ‘center’,
alignItems: ‘center’
},
modeButton: {
position:'absolute',
top:30,
right:30
}
});
Mise en place d’un switch entre Light Mode et Dark Mode
import React from ‘react’;import { StyleSheet, View, Dimensions, Text, TouchableOpacity } from ‘react-native’;const lightModeTheme = {
[...]
tick:’#212121'
}
const darkModeTheme = {
[...]
tick:’#212121'
}
export default class Clock extends React.Component {
[...]
render() {
[...]
//Rayon de l'horloge
const clockRadius = (clockSize) / 2;
//Centre de l'horloge
const clockCenter = { x: clockRadius, y: clockRadius };
//Initialisation de la vue contenant les points représentant chaque heure
let hoursView = [];
for (let i = 0; i < 12; i++) {
const clockAngle = Math.PI + i * Math.PI / 6;
hoursView.push(<View
style={{
position: 'absolute',
top: this.getPointAbscissa(clockAngle, clockRadius, clockCenter),
left: this.getPointOrdinate(clockAngle, clockRadius, clockCenter)-2}}>
<View style={{ width: 4, height: 4, borderRadius:2, backgroundColor: colorTheme.tick }} />
</View>)
}

return (
[...]
<View style={[{ width: clockSize, height: clockSize, borderRadius: clockSize / 2, justifyContent: 'center', alignItems: 'center' }]}>
{hoursView}
</View>
[...]
);
}
getPointAbscissa = (angle, radius, center) => {
return center.x + radius * Math.cos(angle);
}
getPointOrdinate = (angle, radius, center) => {
return center.y + radius * Math.sin(angle);
}
}
Le marquage des heures de notre horloge
<View 
style={{
position: ‘absolute’,
top: this.getPointAbscissa(clockAngle, clockRadius, clockCenter),
left: this.getPointOrdinate(clockAngle, clockRadius, clockCenter)-2
}}>
<View style={{ width: 4, height: 30, backgroundColor: colorTheme.tick }} />
</View>
<View
style={{
transform: [
{ rotate: (-i * (Math.PI / 6)) + ‘rad’ }
],
position: ‘absolute’,
top: this.getPointAbscissa(clockAngle, clockRadius, clockCenter),
left: this.getPointOrdinate(clockAngle, clockRadius, clockCenter) — 2
}}>
<View style={{ width: 4, height: 30, backgroundColor: colorTheme.tick }} />
</View>
render() {
[…]
const clockSize = frameSize — 2 * frameWidth — 56;
[…]
//Initialisation de la vue contenant les points représentant chaque heure
let hoursView = [];
for (let i = 0; i < 12; i++) {
const clockAngle = Math.PI + i * Math.PI / 6;
//Largeur du trait. Si l’heure est un multiple de 3 on grossit le trait
const widthOfHours = (i % 3 === 0) ? 6 : 4;
hoursView.push(<View
style={{
transform: [
{ rotate: (-i * (Math.PI / 6)) + ‘rad’ }
],
position: ‘absolute’,
top: this.getPointAbscissa(clockAngle, clockRadius, clockCenter) — 15,
left: this.getPointOrdinate(clockAngle, clockRadius, clockCenter) — (widthOfHours / 2)
}}>
<View style={{ width: widthOfHours, height: 30, backgroundColor: colorTheme.tick }} />
</View>)
}
return (
[…]
);
}
<View style={[{ width: clockSize, height: clockSize, borderRadius: clockSize / 2, justifyContent: ‘center’, alignItems: ‘center’ }]}
{hoursView}
<View style={{
transform: [
{ rotate: 10 * Math.PI / 6 + ‘rad’ },
{ scaleY: 0.5 },
{ translateY: -clockSize / 4 }
],
position: ‘absolute’,
left: (clockSize / 2) — hourPadding,
top: clockSize / 4,
width: hourWidth,
height: clockSize / 2,
backgroundColor: colorTheme.hour
}} />
<View style={{
transform: [
{ rotate: 10 * Math.PI / 30 + ‘rad’ },
{ scaleY: 0.7 },
{ translateY: -clockSize / 4 }
],
position: ‘absolute’,
left: (clockSize / 2) — minutePadding,
top: clockSize / 4,
width: minuteWidth,
height: clockSize / 2,
backgroundColor: colorTheme.minute
}} />
<View style={{
transform: [
{ rotate: 30 * Math.PI / 30 + ‘rad’ },
{ scaleY: 1 },
{ translateY: -20 }
],
position: ‘absolute’,
left: (clockSize / 2) — secondePadding,
top: clockSize / 6,
width: secondeWidth,
height: clockSize * 2 / 3,
backgroundColor: colorTheme.second
}} />
</View>
</View>
export default class Clock extends React.Component{
constructor(props) {
super(props);
this.state = {
isLightMode: true,
hours: 10,
minutes: 10,
seconds: 30
}
}
componentDidMount() {
this.getTime();
}
render() {
[...]
//Valeurs des variables des heures, minutes et secondes
const { hours, minutes, seconds } = this.state;
return (
<View style={[styles.container, { backgroundColor: colorTheme.background }]}>
<View style={[styles.shadow, styles.frame, { shadowColor: colorTheme.shadow, backgroundColor: colorTheme.frame, width: frameSize, height: frameSize, borderRadius: frameSize / 2 }]}>
<View style={[styles.innerShadow, styles.innerFrame, { shadowColor: colorTheme.shadow, width: frameSize, height: frameSize, borderRadius: frameSize / 2, borderWidth: frameWidth, borderColor: colorTheme.frame }]}>
<View style={[{ width: clockSize, height: clockSize, borderRadius: clockSize / 2, justifyContent: ‘center’, alignItems: ‘center’ }]}>
{hoursView}
<View style={{
transform: [
{ rotate: hours * Math.PI / 6 + ‘rad’ },
{ scaleY: 0.5 },
{ translateY: -clockSize / 4 }
],
position: ‘absolute’,
left: (clockSize / 2) — hourPadding,
top: clockSize / 4,
width: hourWidth,
height: clockSize / 2,
backgroundColor: colorTheme.hour
}} />
<View style={{
transform: [
{ rotate: minutes * Math.PI / 30 + ‘rad’ },
{ scaleY: 0.7 },
{ translateY: -clockSize / 4 }
],
position: ‘absolute’,
left: (clockSize / 2) — minutePadding,
top: clockSize / 4,
width: minuteWidth,
height: clockSize / 2,
backgroundColor: colorTheme.minute
}} />
<View style={{
transform: [
{ rotate: seconds * Math.PI / 30 + ‘rad’ },
{ scaleY: 1 },
{ translateY: -20 }
],
position: ‘absolute’,
left: (clockSize / 2) — secondePadding,
top: clockSize / 6,
width: secondeWidth,
height: clockSize * 2 / 3,
backgroundColor: colorTheme.second,
alignItems: ‘center’,
justifyContent: ‘flex-end’
}}>
<View style={[styles.counterweight, {backgroundColor: colorTheme.second }]} />
</View>
<View style={[styles.pivot, { backgroundColor: colorTheme.second }]} />
</View>
</View>
</View>
<TouchableOpacity style={styles.modeButton} onPress={() => this.setState({ isLightMode: !this.state.isLightMode })}>
<Text style={{ color: colorTheme.text }}>{colorName}</Text>
</TouchableOpacity>
</View>
);
}

getPointAbscissa = (angle, radius, center) => {
return center.x + radius * Math.cos(angle);
}
getPointOrdinate = (angle, radius, center) => {
return center.y + radius * Math.sin(angle);
}
getTime = () => {
const now = new Date();
this.setState({ hours: now.getHours(), minutes: now.getMinutes(), seconds: now.getSeconds() });
setTimeout(() => {
this.getTime();
}, 1000);
}
}
const styles = StyleSheet.create({
[...]
pivot: {
width: 12,
height: 12,
borderRadius: 6
},
counterweight: {
marginBottom: 30,
width: 20,
height: 20,
borderRadius: 10
}
});
Mon horloge du Flutter Clock Contest en React Native

--

--

Show Runner de projets mobiles. Développeur React Native et passionné par les challenges du monde mobile.

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Axel de Sainte Marie

Axel de Sainte Marie

Show Runner de projets mobiles. Développeur React Native et passionné par les challenges du monde mobile.