shrub/pkg/interface/clock/tile/tile.js

414 lines
8.4 KiB
JavaScript
Raw Normal View History

2019-05-29 02:19:07 +03:00
import React, { Component } from 'react';
import classnames from 'classnames';
2020-02-04 00:45:05 +03:00
import moment from 'moment'
import SunCalc from 'suncalc'
2019-05-29 02:19:07 +03:00
const outerSize = 234; //tile size
const innerSize = 218; //clock size
//polar to cartesian
2020-02-04 00:59:31 +03:00
// var ptc = function(r, theta) {
// return {
// x: r * Math.cos(theta),
// y: r * Math.sin(theta)
// }
// }
2019-05-29 02:19:07 +03:00
2020-02-04 00:45:05 +03:00
const toRelativeTime = (date, referenceTime, unit) => moment(date)
.diff(referenceTime, unit)
const minsToDegs = (mins) => {
// 1440 = total minutes in an earth day
return (mins / 1440) * 360
}
const clockwise = (deg, delta) => deg + delta
const anticlockwise = (deg, delta) => deg - delta
const splitArc = (start, end) => end + ((start - end) * 0.5)
const isOdd = n => Math.abs(n % 2) == 1;
const radToDeg = (rad) => rad * (180 / Math.PI);
const degToRad = (deg) => deg * (Math.PI / 180);
const convert = (date, referenceTime) => {
return minsToDegs(toRelativeTime(date, referenceTime, 'minutes'))
}
const circle = (ctx, x, y, r, from, to, fill) => {
ctx.beginPath();
ctx.arc( x, y, r, from, to, );
ctx.strokeStyle = 'rgba(0,0,0,0)';
ctx.fillStyle = fill || 'rgba(0,0,0,0)';
ctx.fill();
}
const circleOutline = (ctx, x, y, r, from, to, stroke, lineWidth) => {
ctx.beginPath();
ctx.arc( x, y, r, from, to, );
ctx.fillStyle = 'rgba(0,0,0,0)';
ctx.lineWidth = lineWidth;
ctx.strokeStyle = stroke || 'rgba(0,0,0,0)';
ctx.stroke();
}
const arc = (ctx, x, y, r, from, to, fill) => {
ctx.beginPath();
ctx.arc( x, y, r, from, to, );
ctx.fillStyle = 'rgba(0,0,0,0)';
ctx.lineWidth = r * 2;
ctx.strokeStyle = fill || 'rgba(0,0,0,0)';
ctx.stroke();
}
const degArc = (ctx, x, y, r, from, to, fill) => {
ctx.beginPath();
ctx.arc( x, y, r, degToRad(from), degToRad(to));
ctx.fillStyle = 'rgba(0,0,0,0)';
ctx.lineWidth = r * 2;
ctx.strokeStyle = fill || 'rgba(0,0,0,0)';
ctx.stroke();
}
2019-05-29 02:19:07 +03:00
class Clock extends Component {
constructor(props) {
super(props);
this.animate = this.animate.bind(this);
this.canvasRef = React.createRef();
this.canvas = null;
2020-02-04 00:45:05 +03:00
this.angle = 0;
this.referenceTime = moment().startOf('day').subtract(6, 'hours')
this.state = {
lat: 0,
lon: 0,
geolocationSuccess: false,
sunrise: 0,
sunsetStart: 0,
sunset:0,
sunriseEnd: 0,
dusk: 0,
dawn: 0,
night: 0,
nightEnd: 0,
nauticalDawn: 0,
nauticalDusk: 0,
// sunriseStartTime = 1509967519,
// sunriseEndTime = 2500 + 1509967519,
// sunsetStartTime = 1510003982,
// sunsetEndTime
// moonPhase = 0.59,
}
2019-05-29 02:19:07 +03:00
}
componentDidMount() {
this.canvas = initCanvas(
this.canvasRef,
{ x: innerSize, y: innerSize },
4
);
2020-02-04 00:45:05 +03:00
navigator.geolocation.getCurrentPosition((res) => {
2019-05-29 02:19:07 +03:00
2020-02-04 00:45:05 +03:00
const lat = res.coords.latitude
const lon = res.coords.longitude
2019-05-29 02:19:07 +03:00
2020-02-04 00:45:05 +03:00
const suncalc = SunCalc.getTimes(new Date(), lat, lon)
2019-05-29 02:19:07 +03:00
2020-02-04 00:45:05 +03:00
const convertedSunCalc = {
sunset: convert(suncalc.sunset, this.referenceTime),
sunrise: convert(suncalc.sunrise, this.referenceTime),
sunsetStart: convert(suncalc.sunsetStart, this.referenceTime),
sunriseEnd: convert(suncalc.sunriseEnd, this.referenceTime),
dusk: convert(suncalc.dusk, this.referenceTime),
dawn: convert(suncalc.dawn, this.referenceTime),
night: convert(suncalc.night, this.referenceTime),
nightEnd: convert(suncalc.nightEnd, this.referenceTime),
nauticalDawn: convert(suncalc.nauticalDawn, this.referenceTime),
nauticalDusk: convert(suncalc.nauticalDusk, this.referenceTime),
}
2019-05-29 02:19:07 +03:00
2020-02-04 00:45:05 +03:00
this.setState({
lat,
lon,
...convertedSunCalc,
geolocationSuccess: true,
}, (err) => {
2020-02-04 00:59:31 +03:00
if (err) console.log(err);
2020-02-04 00:45:05 +03:00
}, { maximumAge: Infinity, timeout: 10000 });
});
this.animate()
2019-05-29 02:19:07 +03:00
}
2020-02-04 00:45:05 +03:00
2019-05-29 02:19:07 +03:00
animate() {
2020-02-04 00:45:05 +03:00
window.setTimeout(() => window.requestAnimationFrame(this.animate), 1000)
const { state } = this
2020-02-04 00:59:31 +03:00
const time = new Date();
const ctx = this.canvas.getContext("2d");
2019-05-29 02:19:07 +03:00
ctx.clearRect(0, 0, c.width, c.height);
ctx.save();
2020-02-04 00:45:05 +03:00
const ctr = innerSize / 2
2020-02-04 00:59:31 +03:00
// Sun+moon calculations
2020-02-04 00:45:05 +03:00
const dd = 4
var cx = ctr
var cy = ctr
this.angle = degToRad(convert(time, this.referenceTime))
var newX = cx + (ctr - 24) * Math.cos(this.angle);
var newY = cy + (ctr - 24) * Math.sin(this.angle);
2020-02-04 00:59:31 +03:00
// Initial background
2020-02-04 00:45:05 +03:00
circle(
ctx,
ctr,
ctr,
ctr,
-1,
2 * Math.PI,
'#FFFFFF'
)
2020-02-04 00:59:31 +03:00
// Day
2020-02-04 00:45:05 +03:00
degArc(
ctx,
ctr,
ctr,
ctr / 2,
state.sunriseEnd,
state.sunset,
'#6792FF'
);
// Sunrise
degArc(
ctx,
ctr,
ctr,
ctr / 2,
state.nightEnd,
state.sunriseEnd,
'#FCC440'
);
// Sunset
degArc(
ctx,
ctr,
ctr,
ctr / 2,
state.nightEnd,
splitArc(state.sunriseEnd, state.nightEnd),
'#FF611E'
);
// Sunset
degArc(
ctx,
ctr,
ctr,
ctr / 2,
state.sunset,
state.night,
'#FCC440'
);
// Sunset
degArc(
ctx,
ctr,
ctr,
ctr / 2,
splitArc(state.sunset, state.night),
state.night,
'#FF611E'
);
if (
radToDeg(this.angle) > splitArc(state.sunriseEnd, state.nightEnd)
&& radToDeg(this.angle) < splitArc(state.sunset, state.night)
) {
// Sun circle
circle(
ctx,
newX-1/2,
newY-1/2,
16,
0,
2 * Math.PI,
'#FCC440'
)
2019-05-29 02:19:07 +03:00
2020-02-04 00:45:05 +03:00
// Sun circle border
circleOutline(
ctx,
newX-1/2,
newY-1/2,
16,
0,
2 * Math.PI,
'#6792FF',
1
);
} else {
// Moon circle
circle(
ctx,
newX-1/2,
newY-1/2,
16,
0,
2 * Math.PI,
'#FFFFFF'
)
2020-02-04 00:59:31 +03:00
// Moon circle outline
2020-02-04 00:45:05 +03:00
circleOutline(
ctx,
newX-1/2,
newY-1/2,
16,
0,
2 * Math.PI,
'#000000',
1
);
}
// Night
degArc(
ctx,
ctr,
ctr,
ctr / 2,
state.night,
state.nightEnd,
'rgb(26, 26, 26)'
);
// Outer borders
circleOutline(
ctx,
ctr,
ctr,
ctr,
-1,
2 * Math.PI,
'#000000',
1
);
// Center white circle with time and date
circle(
ctx,
ctr,
ctr,
ctr/1.85,
-1,
2 * Math.PI,
'#000000'
)
// Center white circle border
circleOutline(
ctx,
ctr,
ctr,
ctr/1.85,
-1,
2 * Math.PI,
'rgb(26, 26, 26)',
1
);
// Text for time and date
const timeText = isOdd(time.getSeconds())
? moment().format('h mm A')
: moment().format('h:mm A')
const dateText = moment().format('MMM Do')
ctx.textAlign = 'center'
ctx.fillStyle = '#FFFFFF'
ctx.font = '16px Inter'
ctx.fillText(timeText, ctr, ctr + 6 - 12)
ctx.fillStyle = '#B1B1B1'
ctx.font = '16px Inter'
ctx.fillText(dateText, ctr, ctr + 6 + 12)
2019-05-29 02:19:07 +03:00
ctx.restore();
}
render() {
return <div style={{position:'relative'}}>
2020-02-04 00:45:05 +03:00
<canvas
style={{position:'absolute'}}
ref={ canvasRef => this.canvasRef = canvasRef }
id="clock-canvas"/>
2019-05-29 02:19:07 +03:00
</div>
}
}
export default class ClockTile extends Component {
constructor(props) {
super(props);
}
renderWrapper(child) {
return (
2019-06-22 02:09:04 +03:00
<div className="pa2" style={{
width: outerSize,
height: outerSize,
background: 'rgba(0,0,0,1)'
2019-06-22 02:09:04 +03:00
}}>
2019-05-29 02:19:07 +03:00
{child}
</div>
);
}
render() {
let data = !!this.props.data ? this.props.data : {};
return this.renderWrapper((
<Clock/>
));
}
}
const loadImg = (base64, cb) => new Promise(resolve => {
const img = new Image();
img.onload = () => resolve(cb(img));
img.onerror = () => reject('Error loading image');
img.src = base64;
});
const initCanvas = (canvas, size, ratio) => {
const { x, y } = size;
let ctx = canvas.getContext('2d');
// let ratio = ctx.webkitBackingStorePixelRatio < 2
// ? window.devicePixelRatio
// : 1;
// default for high print resolution.
// ratio = ratio * resMult;
canvas.width = x * ratio;
canvas.height = y * ratio;
canvas.style.width = x + 'px';
canvas.style.height = y + 'px';
canvas.getContext('2d').scale(ratio, ratio);
return canvas;
}
2019-05-29 02:19:07 +03:00
window.clockTile = ClockTile;