Skip to content

Instantly share code, notes, and snippets.

@dannyhw
Last active March 23, 2018 18:24
Show Gist options
  • Save dannyhw/56a4fcab356a697ae561ac67b698cb76 to your computer and use it in GitHub Desktop.
Save dannyhw/56a4fcab356a697ae561ac67b698cb76 to your computer and use it in GitHub Desktop.
react native hour clock with expo using the svg component library, saving for later
import { Svg } from "expo"
import React, { Component, Fragment } from "react"
import { View } from "react-native"
import { PropTypes } from "prop-types"
const getClockCoordinates = ({ radius, ratio }) => {
const coords = []
for (let i = 1; i <= 12; i++) {
const cx = radius
const cy = radius
const x =
radius * ratio * Math.cos(Math.PI / -2 + 2 * i * Math.PI / 12) + cx
const y =
radius * ratio * Math.sin(Math.PI / -2 + 2 * i * Math.PI / 12) + cy
coords.push({ x, y })
}
return coords
}
export default class SVGClock extends Component {
state = {
selected: undefined,
outerClockCoords: getClockCoordinates({
radius: this.props.radius,
ratio: 0.8
}),
innerClockCoords: getClockCoordinates({
radius: this.props.radius,
ratio: 0.48
})
}
render = () => {
const { radius, hasInner } = this.props
const diameter = radius * 2
const backgroundColor = "#F4F4F4"
const fontSize = 14
const fontSizeSM = 10
const { innerClockCoords, outerClockCoords, selected } = this.state
return (
<View
style={{
alignItems: "center",
justifyContent: "center",
backgroundColor: "white",
flex: 1
}}
>
<Svg height={diameter} width={diameter}>
<Svg.Circle
cx={radius}
cy={radius}
r={radius - 2}
fill={backgroundColor}
/>
{selected !== undefined && (
<Svg.Circle cx={radius} cy={radius} r={2} fill="lightblue" />
)}
{selected !== undefined &&
selected >= 12 && (
<Svg.Line
x1={radius}
y1={radius}
x2={innerClockCoords[selected - 12].x}
y2={innerClockCoords[selected - 12].y}
stroke="lightblue"
strokeWidth={1.5}
/>
)}
{hasInner &&
innerClockCoords !== undefined &&
innerClockCoords.map(({ x, y }, i) => (
<Fragment key={`${x}${y}`}>
<Svg.Circle
cx={x}
cy={y}
r={14}
fill={selected === i + 12 ? "lightblue" : backgroundColor}
onPress={() => this.setState({ selected: i + 12 })}
/>
<Svg.Text
fontSize={fontSizeSM}
textAnchor="middle"
x={x - 1}
y={y - fontSizeSM * 0.6}
onPress={() => this.setState({ selected: i + 12 })}
>
{this.props.innerLabels[i]}
</Svg.Text>
</Fragment>
))}
{selected !== undefined &&
selected < 12 && (
<Svg.Line
x1={radius}
y1={radius}
x2={outerClockCoords[selected].x}
y2={outerClockCoords[selected].y}
stroke="lightblue"
strokeWidth={1.5}
/>
)}
{outerClockCoords !== undefined &&
outerClockCoords.map(({ x, y }, i) => (
<Fragment key={`${x}${y}`}>
<Svg.Circle
cx={x}
cy={y}
r={16}
fill={selected === i ? "lightblue" : backgroundColor}
onPress={() => this.setState({ selected: i })}
/>
<Svg.Text
fontSize={fontSize}
textAnchor="middle"
x={x - 1}
y={y - fontSize * 0.6}
onPress={() => this.setState({ selected: i })}
onPressIn={() => this.setState({ selected: i })}
>
{this.props.outerLabels[i]}
</Svg.Text>
</Fragment>
))}
</Svg>
</View>
)
}
}
SVGClock.propTypes = {
innerLabels: PropTypes.array,
outerLabels: PropTypes.array,
hasInner: PropTypes.bool,
radius: PropTypes.number
}
SVGClock.defaultProps = {
innerLabels: [13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24],
outerLabels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
radius: 120
}
@dannyhw
Copy link
Author

dannyhw commented Mar 20, 2018

image

@dannyhw
Copy link
Author

dannyhw commented Mar 20, 2018

TODO:

  • Add clock arm with svg line
  • Extract calculation of coordinates from the render
  • make more generic so any list of items can be in the inner or outer circle

@dannyhw
Copy link
Author

dannyhw commented Mar 23, 2018

Props

prop default type
radius 120 Number
innerLabels [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] Array
outerLabels [13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24] Array
hasInner false Bool

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment