main
js 105 lines 2.77 KB
Raw
1 import React, {createRef, PureComponent} from 'react';
2
3 const SPEED = 0.003 / Math.PI;
4 const FRAMES = 10;
5
6 export default class Clock extends PureComponent {
7 faceRef = createRef();
8 arcGroupRef = createRef();
9 clockHandRef = createRef();
10 frame = null;
11 hitCounter = 0;
12 rotation = 0;
13 t0 = Date.now();
14 arcs = [];
15
16 animate = () => {
17 const now = Date.now();
18 const td = now - this.t0;
19 this.rotation = (this.rotation + SPEED * td) % (2 * Math.PI);
20 this.t0 = now;
21
22 this.arcs.push({rotation: this.rotation, td});
23
24 let lx, ly, tx, ty;
25 if (this.arcs.length > FRAMES) {
26 this.arcs.forEach(({rotation, td}, i) => {
27 lx = tx;
28 ly = ty;
29 const r = 145;
30 tx = 155 + r * Math.cos(rotation);
31 ty = 155 + r * Math.sin(rotation);
32 const bigArc = SPEED * td < Math.PI ? '0' : '1';
33 const path = `M${tx} ${ty}A${r} ${r} 0 ${bigArc} 0 ${lx} ${ly}L155 155`;
34 const hue = 120 - Math.min(120, td / 4);
35 const colour = `hsl(${hue}, 100%, ${60 - i * (30 / FRAMES)}%)`;
36 if (i !== 0) {
37 const arcEl = this.arcGroupRef.current.children[i - 1];
38 arcEl.setAttribute('d', path);
39 arcEl.setAttribute('fill', colour);
40 }
41 });
42 this.clockHandRef.current.setAttribute('d', `M155 155L${tx} ${ty}`);
43 this.arcs.shift();
44 }
45
46 if (this.hitCounter > 0) {
47 this.faceRef.current.setAttribute(
48 'fill',
49 `hsla(0, 0%, ${this.hitCounter}%, 0.95)`
50 );
51 this.hitCounter -= 1;
52 } else {
53 this.hitCounter = 0;
54 this.faceRef.current.setAttribute('fill', 'hsla(0, 0%, 5%, 0.95)');
55 }
56
57 this.frame = requestAnimationFrame(this.animate);
58 };
59
60 componentDidMount() {
61 this.frame = requestAnimationFrame(this.animate);
62 if (this.faceRef.current) {
63 this.faceRef.current.addEventListener('click', this.handleClick);
64 }
65 }
66
67 componentDidUpdate() {
68 console.log('componentDidUpdate()', this.faceRef.current);
69 }
70
71 componentWillUnmount() {
72 this.faceRef.current.removeEventListener('click', this.handleClick);
73 if (this.frame) {
74 cancelAnimationFrame(this.frame);
75 }
76 }
77
78 handleClick = e => {
79 e.stopPropagation();
80 this.hitCounter = 50;
81 };
82
83 render() {
84 const paths = new Array(FRAMES);
85 for (let i = 0; i < FRAMES; i++) {
86 paths.push(<path className="arcHand" key={i} />);
87 }
88 return (
89 <div className="stutterer">
90 <svg height="310" width="310">
91 <circle
92 className="clockFace"
93 onClick={this.handleClick}
94 cx={155}
95 cy={155}
96 r={150}
97 ref={this.faceRef}
98 />
99 <g ref={this.arcGroupRef}>{paths}</g>
100 <path className="clockHand" ref={this.clockHandRef} />
101 </svg>
102 </div>
103 );
104 }
105 }