Skip to content

Instantly share code, notes, and snippets.

@revskill10
Created October 5, 2018 16:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save revskill10/018b101faa5e4b9e2d0a02d79a049f0b to your computer and use it in GitHub Desktop.
Save revskill10/018b101faa5e4b9e2d0a02d79a049f0b to your computer and use it in GitHub Desktop.
React Clock
<div id="root" class="container"></div>
class App extends React.Component {
render() {
return (
<div>
<div className="row">
<div className="col-lg-6 col-lg-offset-3">
<div className="text-center">
<h1>React Clock</h1>
</div>
</div>
</div>
<div className="row">
<div className="col-lg-12">
<Clock size={400} timeFormat="24hour" hourFormat="standard" />
</div>
</div>
</div>
);
}
}
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = { time: new Date() };
this.radius = this.props.size / 2;
this.drawingContext = null;
this.draw24hour = this.props.timeFormat.toLowerCase().trim() === "24hour";
this.drawRoman = !this.draw24hour && this.props.hourFormat.toLowerCase().trim() === "roman";
}
componentDidMount() {
this.getDrawingContext();
this.timerId = setInterval(() => this.tick(), 1000);
}
componentWillUnmount() {
clearInterval(this.timerId);
}
getDrawingContext() {
this.drawingContext = this.refs.clockCanvas.getContext('2d');
this.drawingContext.translate(this.radius, this.radius);
this.radius *= 0.9;
}
tick() {
this.setState({ time: new Date() });
const radius = this.radius;
let ctx = this.drawingContext;
this.drawFace(ctx, radius);
this.drawNumbers(ctx, radius);
this.drawTicks(ctx, radius);
this.drawTime(ctx, radius);
}
drawFace(ctx, radius) {
ctx.beginPath();
ctx.arc(0,0, radius, 0, 2 * Math.PI);
ctx.fillStyle = "white";
ctx.fill();
const grad = ctx.createRadialGradient(0, 0, radius * 0.95, 0, 0, radius * 1.05);
grad.addColorStop(0, "#333");
grad.addColorStop(0.5, "white");
grad.addColorStop(1, "#333");
ctx.strokeStyle = grad;
ctx.lineWidth = radius * 0.1;
ctx.stroke();
ctx.beginPath();
ctx.arc(0, 0, radius * 0.05, 0, 2 * Math.PI);
ctx.fillStyle = "#333";
ctx.fill();
}
drawNumbers(ctx, radius) {
const romans = ["I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII"];
const fontBig = radius * 0.15 + "px Arial";
const fontSmall = radius * 0.075 + "px Arial";
let ang, num;
ctx.textBaseline = "middle";
ctx.textAlign = "center";
for (num = 1; num < 13; num++) {
ang = num * Math.PI / 6;
ctx.rotate(ang);
ctx.translate(0, -radius * 0.78);
ctx.rotate(-ang);
ctx.font = fontBig;
ctx.fillStyle = "black";
ctx.fillText(this.drawRoman ? romans[num-1] : num.toString(), 0, 0);
ctx.rotate(ang);
ctx.translate(0, radius * 0.78);
ctx.rotate(-ang);
// Draw inner numerals for 24 hour time format
if (this.draw24hour) {
ctx.rotate(ang);
ctx.translate(0, -radius * 0.60);
ctx.rotate(-ang);
ctx.font = fontSmall;
ctx.fillStyle = "red";
ctx.fillText((num + 12).toString(), 0, 0);
ctx.rotate(ang);
ctx.translate(0, radius * 0.60);
ctx.rotate(-ang);
}
}
// Write author text
ctx.font = fontSmall;
ctx.fillStyle = "#3D3B3D";
ctx.translate(0, radius * 0.30);
ctx.fillText("@AlanMunsonTech", 0, 0);
ctx.translate(0, -radius * 0.30);
}
drawTicks(ctx, radius) {
let numTicks, tickAng, tickX, tickY;
for (numTicks = 0; numTicks < 60; numTicks++) {
tickAng = (numTicks * Math.PI / 30);
tickX = radius * Math.sin(tickAng);
tickY = -radius * Math.cos(tickAng);
ctx.beginPath();
ctx.lineWidth = radius * 0.010;
ctx.moveTo(tickX, tickY);
if (numTicks % 5 === 0) {
ctx.lineTo(tickX * 0.88, tickY * 0.88);
} else {
ctx.lineTo(tickX * 0.92, tickY * 0.92);
}
ctx.stroke();
}
}
drawTime(ctx, radius) {
const now = this.state.time;
let hour = now.getHours();
let minute = now.getMinutes();
let second = now.getSeconds();
// hour
hour %= 12;
hour = (hour * Math.PI / 6) + (minute * Math.PI / (6 * 60)) + (second * Math.PI / (360 * 60));
this.drawHand(ctx, hour, radius * 0.5, radius * 0.05);
// minute
minute = (minute * Math.PI / 30) + (second * Math.PI / (30 * 60));
this.drawHand(ctx, minute, radius * 0.8, radius * 0.05);
// second
second = (second * Math.PI / 30);
this.drawHand(ctx, second, radius * 0.9, radius * 0.02, "red");
}
drawHand(ctx, position, length, width, color) {
color = color || "black";
ctx.beginPath();
ctx.lineWidth = width;
ctx.lineCap = "round";
ctx.fillStyle = color;
ctx.strokeStyle = color;
ctx.moveTo(0, 0);
ctx.rotate(position);
ctx.lineTo(0, -length);
ctx.stroke();
ctx.rotate(-position);
}
render() {
return (
<div className="Clock" style={{ width: String(this.props.size) + 'px' }}>
<canvas width={this.props.size} height={this.props.size} ref="clockCanvas"/>
</div>
);
}
}
Clock.defaultProps = {
size: 400, // size in pixels => size is length & width
timeFormat: "24hour", // {standard | 24hour} => if '24hour', hourFormat must be 'standard'
hourFormat: "standard" // {standard | roman}
};
Clock.propTypes = {
size: React.PropTypes.number,
timeFormat: React.PropTypes.string,
hourFormat: React.PropTypes.string
};
ReactDOM.render(
<App />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react-dom.min.js"></script>
.Clock {
padding: 5px;
margin-top: 15px;
margin-left: auto;
margin-right: auto;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment