diff --git a/pkg/interface/src/views/apps/launch/components/tiles/clock.js b/pkg/interface/src/views/apps/launch/components/tiles/clock.js index dd0a4c79f7..64fcaf553f 100644 --- a/pkg/interface/src/views/apps/launch/components/tiles/clock.js +++ b/pkg/interface/src/views/apps/launch/components/tiles/clock.js @@ -1,38 +1,28 @@ import React from 'react'; import moment from 'moment'; import SunCalc from 'suncalc'; +import styled from 'styled-components'; import Tile from './tile'; -const innerSize = 124; // clock size +const VIEWBOX_SIZE = 100; +const CX = VIEWBOX_SIZE / 2; +const CY = VIEWBOX_SIZE / 2; +const RADIUS = VIEWBOX_SIZE / 2; +const CELESTIAL_BODY_SIZE = 16; -// polar to cartesian -// var ptc = function(r, theta) { -// return { -// x: r * Math.cos(theta), -// y: r * Math.sin(theta) -// } -// } - -let timeTextColor = '#000000', dateTextColor = '#333333', background = '#ffffff'; - -const dark = window.matchMedia('(prefers-color-scheme: dark)'); - -if (dark.matches) { - timeTextColor = '#ffffff'; - dateTextColor = '#7f7f7f'; - background = '#333'; -} - -function darkColors(dark) { - if (dark.matches) { - timeTextColor = '#ffffff'; - dateTextColor = '#7f7f7f'; - background = '#ffffff'; +const ApplyClockBg = styled.div` + .background { + fill: ${p => p.theme.colors.white}; } - } - -dark.addListener(darkColors); + .time { + fill: ${p => p.theme.colors.black}; + color: ${p => p.theme.colors.black}; + } + .date { + fill: ${p => p.theme.colors.gray}; + } +`; const toRelativeTime = (date, referenceTime, unit) => moment(date) .diff(referenceTime, unit); @@ -42,10 +32,6 @@ const minsToDegs = (mins) => { return (mins / 1440) * 360; }; -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); @@ -54,58 +40,137 @@ 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 circleClip = (ctx, x, y, r, from, to, fill) => { - ctx.globalCompositeOperation = 'xor'; - circle(ctx, x, y, r, from, to, fill); - ctx.globalCompositeOperation = 'source-over'; -}; - -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)'; - if (lineWidth) { - ctx.stroke(); +// https://github.com/tingletech/moon-phase +export const dFromPhase = (moonPhase) => { + let mag, sweep, d = "m50,0"; + if (moonPhase <= 0.25) { + sweep = [ 1, 0 ]; + mag = 20 - 20 * moonPhase * 4; + } else if (moonPhase <= 0.50) { + sweep = [ 0, 0 ]; + mag = 20 * (moonPhase - 0.25) * 4; + } else if (moonPhase <= 0.75) { + sweep = [ 1, 1 ]; + mag = 20 - 20 * (moonPhase - 0.50) * 4; + } else if (moonPhase <= 1) { + sweep = [ 0, 1 ]; + mag = 20 * (moonPhase - 0.75) * 4; } -}; -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(); -}; + d = d + "a" + mag + ",20 0 1," + sweep[0] + " 0,100 "; + d = d + "a20,20 0 1," + sweep[1] + " 0,-100"; + return d; +} -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(); -}; +const Moon = ({ angle, ...props }) => { + const phase = SunCalc.getMoonIllumination(moment().toDate()).phase.toFixed(2); + const cx = CX + (RADIUS - 12) * Math.cos(degToRad(angle)) - (CELESTIAL_BODY_SIZE / 2); + const cy = CY + (RADIUS - 12) * Math.sin(degToRad(angle)) - (CELESTIAL_BODY_SIZE / 2); + return ( + + + + + + + + ); +} -class Clock extends React.Component { +const Sun = ({ angle, ...props}) => ( + +); + +const SvgArc = ({ start, end, ...rest }) => { + const x1 = CX + RADIUS * Math.cos(degToRad(start)); + const y1 = CY + RADIUS * Math.sin(degToRad(start)); + const x2 = CX + RADIUS * Math.cos(degToRad(end)); + const y2 = CY + RADIUS * Math.sin(degToRad(end)); + + const isLarge = Math.abs((start > 360 ? start - 360 : start) - end) > 180; + + const d = [ + 'M', CX, CY, + 'L', x1, y1, + 'A', RADIUS, RADIUS, '0', (isLarge ? '1' : '0'), '1', x2, y2, 'z' + ].join(' '); + + return ; +} + +class ClockText extends React.Component { + constructor(props) { + super(props); + this.state = { + time: Date.now() + } + } + componentDidMount() { + this.interval = setInterval(() => this.setState({ time: Date.now() }), 1000); + } + + componentWillUnmount() { + clearInterval(this.interval); + } + + render() { + const now = moment(this.state.time); + return ( + <> + + + {now.format('h')} + : + + {now.format('mm A')} + + {now.format('MMM D')}{now.format('Do').replace(now.format('D'), '')} + + ); + } +} + +class Clock extends React.PureComponent { constructor(props) { super(props); - this.animate = this.animate.bind(this); - this.canvasRef = React.createRef(); - this.canvas = null; this.angle = 0; this.referenceTime = moment().startOf('day').subtract(6, 'hours'); this.state = { + time: Date.now(), lat: 0, lon: 0, geolocationSuccess: false, @@ -164,246 +229,88 @@ class Clock extends React.Component { } componentDidMount() { - this.canvas = initCanvas( - this.canvasRef, - { x: innerSize, y: innerSize }, - 4 - ); - this.initGeolocation(); - this.animate(); + this.interval = setInterval(() => this.setState({ time: Date.now() }), 60000); } componentWillUnmount() { - if (this.animationTimer) { - window.clearTimeout(this.animationTimer); - } - } - - animate() { - this.animationTimer = - window.setTimeout(() => window.requestAnimationFrame(this.animate), 1000); - - const { state } = this; - const time = new Date(); - const ctx = this.canvas.getContext('2d'); - ctx.clearRect(0, 0, ctx.width, ctx.height); - ctx.save(); - - const ctr = innerSize / 2; - - // Sun+moon calculations - const cx = ctr; - const cy = ctr; - this.angle = degToRad(convert(time, this.referenceTime)); - const newX = cx + (ctr - 15) * Math.cos(this.angle); - const newY = cy + (ctr - 15) * Math.sin(this.angle); - - // Center white circle with time and date - circle( - ctx, - ctr, - ctr, - ctr, - -1, - 2 * Math.PI, - background - ); - - // Day - degArc( - ctx, - ctr, - ctr, - ctr / 2, - state.sunriseEnd, - state.sunset, - 'rgba(33, 157, 255, .2)' - ); - - // Sunrise - degArc( - ctx, - ctr, - ctr, - ctr / 2, - state.sunsetStart, - state.sunriseEnd, - '#FFC700' - ); - - // Sunset - degArc( - ctx, - ctr, - ctr, - ctr / 2, - state.dusk, - state.dawn, - 'rgba(255, 65, 54, .8)' - ); - - // Night - degArc( - ctx, - ctr, - ctr, - ctr / 2, - state.night, - state.nightEnd, - 'rgba(0, 0, 0, .8)' - ); - - 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, - 8, - 0, - 2 * Math.PI, - '#FCC440' - ); - - // Sun circle border - circleOutline( - ctx, - newX-1/2, - newY-1/2, - 8, - 0, - 2 * Math.PI, - 'rgba(0,0,0,0.1)', - 1 - ); - } else { - // Moon circle - circle( - ctx, - newX-1/2, - newY-1/2, - 8, - 0, - 2 * Math.PI, - '#FFFFFF' - ); - // Moon circle outline - circleOutline( - ctx, - newX-1/2, - newY-1/2, - 8, - 0, - 2 * Math.PI, - '#000000', - 1 - ); - } - - - // Outer borders - circleOutline( - ctx, - ctr, - ctr, - ctr-1, - -1, - 2 * Math.PI, - 'none', - 0 - ); - - // Center white circle border - circleOutline( - ctx, - ctr, - ctr, - ctr/1.85, - -1, - 2 * Math.PI, - 'none', - 0 - ); - - // Inner hole - circle( - ctx, - ctr, - ctr, - ctr/1.85, - -1, - 2 * Math.PI, - background - ); - - // 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 = timeTextColor; - ctx.font = '12px Inter'; - ctx.fillText(timeText, ctr, ctr + 6 - 7); - ctx.fillStyle = dateTextColor; - ctx.font = '12px Inter'; - ctx.fillText(dateText, ctr, ctr + 6 + 7); - - ctx.restore(); + clearInterval(this.interval); } render() { + const now = moment(this.state.time); + const angle = convert(now, this.referenceTime); + return ( - this.canvasRef = canvasRef } - id="clock-canvas" - /> + + + + + + + + + + + + + + + + + + + + + + + + + + + {angle > this.state.nightEnd && angle < this.state.sunset + ? + : + } + + + + ); } } -export default class ClockTile extends React.Component { - constructor(props) { - super(props); - } - renderWrapper(child) { - return ( - - {child} - - ); - } - render() { - const data = this.props.location ? this.props.location : {}; - return this.renderWrapper(( - - )); - } -} - -const initCanvas = (canvas, size, ratio) => { - const { x, y } = size; - // 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; -}; +const ClockTile = ({ location = {} }) => ( + + + +); +export default ClockTile; \ No newline at end of file