diff --git a/src/lib/canvas/Animator.ts b/src/lib/canvas/Animator.ts new file mode 100644 index 00000000..7fe5fe24 --- /dev/null +++ b/src/lib/canvas/Animator.ts @@ -0,0 +1,9 @@ +export abstract class Animator { + ctx: CanvasRenderingContext2D; + + constructor(context: CanvasRenderingContext2D) { + this.ctx = context; + } + + abstract animate(): void; +} diff --git a/src/lib/canvas/weather/animate/Bluebody.ts b/src/lib/canvas/weather/animate/Bluebody.ts new file mode 100644 index 00000000..d4f0478c --- /dev/null +++ b/src/lib/canvas/weather/animate/Bluebody.ts @@ -0,0 +1,97 @@ +import { Animator } from "$lib/canvas/Animator"; + +export class Bluebody extends Animator { + radialGradient: CanvasGradient; + orbitPosX: number; + orbitPosY: number; + orbitCenX: number; + orbitCenY: number; + orbitRadius: number; + orbitBodyRadius: number; + canvasWidth: number; + canvasHeight: number; + + constructor( + context: CanvasRenderingContext2D, + time: Date, + orbitCenX: number, + orbitCenY: number, + orbitRadius: number, + orbitBodyRadius: number, + canvasWidth: number, + canvasHeight: number, + ) { + super(context); + this.canvasHeight = canvasHeight; + this.canvasWidth = canvasWidth; + // ctx.beginPath(); + // ctx.arc( + // orbitCentreX, + // orbitCentreY, + // orbitRadius, + // Math.PI, Math.PI * 2, false); + // ctx.stroke(); + this.orbitPosX = 0; + this.orbitPosY = 0; + this.orbitCenY = orbitCenY; + this.orbitCenX = orbitCenX; + + this.orbitRadius = orbitRadius; + this.orbitBodyRadius = orbitBodyRadius; + this.updateTime(time); + this.radialGradient = context.createRadialGradient(0, 0, 0, 0, 0, 0); + this.ctx = context; + // sun or moon + + // context.fillRect(orbitPositionX, orbitPositionY, orbitBodyRadius * 2, orbitBodyRadius * 2); + } + + updateTime(time: Date) { + const hours = time.getHours(); + const minutes = time.getMinutes(); + + const decimal = hours + (minutes / 60); + const moduloTime = decimal + (6 % 24); + const orbitAngle = Math.PI - ((((2 * Math.PI) / 24) * moduloTime) % Math.PI) - Math.PI / 2; + + + const orbitLenX = Math.sin(orbitAngle) * this.orbitRadius; + const orbitLenY = Math.cos(orbitAngle) * this.orbitRadius; + this.orbitPosX = this.orbitCenX - orbitLenX; + this.orbitPosY = this.orbitCenY - orbitLenY; + + this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight) + + if (hours >= 6 && hours < 18) { + this.radialGradient = this.getSunRadialGradient(this.ctx, this.orbitPosX, this.orbitPosY, this.orbitBodyRadius); + } else { + this.radialGradient = this.getMoonRadialGradient(this.ctx, this.orbitPosX, this.orbitPosY, this.orbitBodyRadius); + } + this.draw(); + + } + + animate = () => { + this.draw(); + }; + + draw = () => { + this.ctx.fillStyle = this.radialGradient; + this.ctx.arc(this.orbitPosX, this.orbitPosY, this.orbitBodyRadius, 0, 2 * Math.PI, false); + this.ctx.fill(); + }; + + getSunRadialGradient(context: CanvasRenderingContext2D, x: number, y: number, r: number) { + const radialGradient = context.createRadialGradient(x, y, r * 0.8, x, y, r); + radialGradient.addColorStop(0, "rgba(255, 240, 210, 1)"); + radialGradient.addColorStop(1, "rgba(255, 255, 0, 0)"); + return radialGradient; + } + + getMoonRadialGradient(context: CanvasRenderingContext2D, x: number, y: number, r: number) { + const radialGradient = context.createRadialGradient(x, y, r * 0.5, x, y, r * 0.8); + radialGradient.addColorStop(0, "rgba(255, 255, 255, 1)"); + radialGradient.addColorStop(1, "rgba(50, 50, 50, 0)"); + return radialGradient; + } +} diff --git a/src/lib/canvas/weather/animate/Cloud.ts b/src/lib/canvas/weather/animate/Cloud.ts new file mode 100644 index 00000000..b6562675 --- /dev/null +++ b/src/lib/canvas/weather/animate/Cloud.ts @@ -0,0 +1,95 @@ +import { Animator } from "$lib/canvas/Animator"; +import { Vector2D } from "$lib/mechanics/vector"; +import { randBetween } from "$lib/utils/maths"; + +const TWO_PI = Math.PI * 2; +const ORB_SIZE_VARIANCE = [0.8, 1.2]; +const ORB_X_SPREAD_FACTOR = [-2, 2]; +const ORB_Y_SPREAD_FACTOR = [-0.5, 1]; + +export class Cloud extends Animator { + position: Vector2D; + speed: number; + orbs: Orb[] = []; + width: number; + radius: number; + size: number; + color: string; + + constructor(context: CanvasRenderingContext2D, x: number, y: number, radius: number, size: number, speed: number, color: string) { + super(context); + this.position = new Vector2D(x, y); + this.speed = speed; + this.size = size; + this.ctx = context; + this.width = 0; + this.color = color; + this.radius = radius; + } + + animate = () => { + this.position.x += this.speed; + // regenerate once cloud has left screen + if (this.position.x > this.ctx.canvas.width + this.width) { + this.generate(); + this.position.x = -this.width; + // vary height, keep within top half of canvas + this.position.y += randBetween(-this.width, this.width); + this.position.y = Math.max(0, this.position.y); + this.position.y = Math.min(this.ctx.canvas.height / 2, this.position.y); + } + this.draw(); + }; + + draw = () => { + this.orbs.forEach((orb) => orb.draw(this.ctx, this.position.x, this.position.y)); + }; + + generate = () => { + let smallestX = 0; + let largestX = 0; + + this.orbs.splice(0, this.orbs.length); + + for (let i = 0; i < this.size; i++) { + const orbRadius = randBetween(this.radius * ORB_SIZE_VARIANCE[0], this.radius * ORB_SIZE_VARIANCE[1]); + const orbX = randBetween(ORB_X_SPREAD_FACTOR[0] * this.radius, ORB_X_SPREAD_FACTOR[1] * this.radius); + const orbY = randBetween(ORB_Y_SPREAD_FACTOR[0] * this.radius, ORB_Y_SPREAD_FACTOR[1] * this.radius); + if (orbX < smallestX) { + smallestX = orbX; + } + if (orbX > largestX) { + largestX = orbX; + } + this.orbs.push(new Orb(orbX, orbY, orbRadius, this.color)); + } + + this.width = largestX - smallestX; + }; +} + +class Orb { + pos: Vector2D; + color: string; + radius: number; + + constructor(x: number, y: number, radius: number, color: string) { + this.color = color; + this.radius = radius; + this.pos = new Vector2D(x, y); + } + + draw = (ctx: CanvasRenderingContext2D, x: number, y: number) => { + const absX = x + this.pos.x; + const absY = y + this.pos.y; + + const radialGradient = ctx.createRadialGradient(absX, absY, 0, absX, absY, this.radius); + radialGradient.addColorStop(0, this.color); + radialGradient.addColorStop(1, "rgba(255, 255, 255, 0)"); + ctx.beginPath(); + ctx.fillStyle = radialGradient; + ctx.arc(absX, absY, this.radius, 0, TWO_PI, true); + ctx.fill(); + ctx.closePath(); + }; +} diff --git a/src/lib/canvas/weather/animate/Precipitater.ts b/src/lib/canvas/weather/animate/Precipitater.ts new file mode 100644 index 00000000..8f135511 --- /dev/null +++ b/src/lib/canvas/weather/animate/Precipitater.ts @@ -0,0 +1,58 @@ +import { Animator } from "$lib/canvas/Animator"; +import { Vector2D } from "$lib/mechanics/vector"; + +export abstract class Precipitator extends Animator { + canvasWidth: number; + canvasHeight: number; + drops: Drop[] = []; + dropSpeed: number; + windSpeed: number; + size: number; + + constructor(context: CanvasRenderingContext2D, speed: number, density: number, windSpeed: number, size: number) { + super(context); + this.canvasWidth = context.canvas.width; + this.canvasHeight = context.canvas.height; + this.dropSpeed = speed; + this.size = size; + this.windSpeed = windSpeed; + const dropSpacing = this.size / density; + const columns = Math.floor(this.canvasWidth / (this.size + dropSpacing)); + + for (let i = 0; i < columns; i++) { + this.drops.push(this.generateDrop(i * (dropSpacing + this.size), Math.random() * this.canvasHeight)); + } + } + + abstract generateDrop(x: number, y: number): Drop; + + animate = () => { + this.drops.forEach((drop) => { + drop.position.y += this.dropSpeed; + drop.position.x += this.windSpeed / 10; + + if (drop.position.y > this.canvasHeight) { + // vary starting position for variety of y position + drop.position.y = 0 - Math.random() * this.canvasHeight; + // shift drop position left or right up to drop width + drop.position.x = drop.position.x + (Math.random() * 2 - 1.0) * 3 * this.size; + } + if (drop.position.x > this.canvasWidth) { + drop.position.x = 0; + } + drop.draw(this.ctx); + }); + }; +} + +export abstract class Drop { + position: Vector2D; + size: number; + + constructor(size: number, initPos: Vector2D) { + this.position = initPos; + this.size = size; + } + + abstract draw(context: CanvasRenderingContext2D): void; +} diff --git a/src/lib/canvas/weather/animate/Rain.ts b/src/lib/canvas/weather/animate/Rain.ts new file mode 100644 index 00000000..fddbd049 --- /dev/null +++ b/src/lib/canvas/weather/animate/Rain.ts @@ -0,0 +1,37 @@ +import { Vector2D } from "$lib/mechanics/vector"; +import { Drop, Precipitator } from "./Precipitater"; + +const RAIN_COLOR = "rgb(90, 140, 210)"; +const DROP_SIZE = 10; + +export class Rain extends Precipitator { + constructor(context: CanvasRenderingContext2D, speed: number, density: number, windSpeed: number, size: number = DROP_SIZE) { + super(context, speed, density, windSpeed, size); + } + + generateDrop(x: number, y: number): Drop { + return new RainDrop(this.size, new Vector2D(x, y)); + } +} + +class RainDrop extends Drop { + height: number; + + constructor(size: number, initPos: Vector2D) { + super(size, initPos); + this.height = size * 2.5; + } + + draw(context: CanvasRenderingContext2D) { + context.beginPath(); + context.fillStyle = RAIN_COLOR; + context.moveTo(this.position.x, this.position.y); + context.lineTo(this.position.x - this.size, this.position.y + this.height); + context.lineTo(this.position.x + this.size, this.position.y + this.height); + context.lineTo(this.position.x, this.position.y); + context.fill(); + context.arc(this.position.x, this.position.y + this.height, this.size, 1.9 * Math.PI, 1.1 * Math.PI); + context.fill(); + context.closePath(); + } +} diff --git a/src/lib/canvas/weather/animate/Snow.ts b/src/lib/canvas/weather/animate/Snow.ts new file mode 100644 index 00000000..002e07b8 --- /dev/null +++ b/src/lib/canvas/weather/animate/Snow.ts @@ -0,0 +1,71 @@ +import { Vector2D } from "$lib/mechanics/vector"; +import { Drop, Precipitator } from "./Precipitater"; + +const SNOW_COLOR = "rgb(180, 190, 255)"; +// const FLAKE_SIZE = 40; +// const FLAKE_BRANCH_LENGTH = FLAKE_SIZE / 2; + +export class Snow extends Precipitator { + constructor(context: CanvasRenderingContext2D, speed: number, density: number, windSpeed: number, size: number) { + super(context, speed, density, windSpeed, size); + } + + generateDrop(x: number, y: number): Drop { + return new SnowFlake(this.size, new Vector2D(x, y)); + } + + draw = () => { + this.drops.forEach((drop) => drop.draw(this.ctx)); + }; +} + +class SnowFlake extends Drop { + constructor(size: number, initPos: Vector2D) { + super(size, initPos); + } + + draw(context: CanvasRenderingContext2D) { + context.beginPath(); + context.strokeStyle = SNOW_COLOR; + context.lineCap = "round"; + context.lineWidth = this.size / 20; + + this.drawBranch(context, this.size / 2, Math.PI / 6); + this.drawBranch(context, this.size / 2, Math.PI / 2); + this.drawBranch(context, this.size / 2, (5 * Math.PI) / 6); + this.drawBranch(context, this.size / 2, (7 * Math.PI) / 6); + this.drawBranch(context, this.size / 2, (3 * Math.PI) / 2); + this.drawBranch(context, this.size / 2, (11 * Math.PI) / 6); + + context.stroke(); + context.closePath(); + } + + drawBranch = (context: CanvasRenderingContext2D, radius: number, angle: number) => { + const offsetX = radius * Math.cos(angle); + const offsetY = radius * Math.sin(angle); + + // draw main branch + context.moveTo(this.position.x, this.position.y); + context.lineTo(this.position.x - offsetX, this.position.y - offsetY); + + const r2 = radius * 0.5; // length from origin to start of sub branch + const r3 = radius * 1.1; // length from + const subBranchAngle = Math.PI / 6; + const r4 = (r2 + r3) * Math.tan(subBranchAngle); + + const x2 = this.position.x - r2 * Math.cos(angle); + const y2 = this.position.y - r2 * Math.sin(angle); + + const x3 = this.position.x - r4 * Math.cos(subBranchAngle + angle); + const y3 = this.position.y - r4 * Math.sin(subBranchAngle + angle); + + const x4 = this.position.x - r4 * Math.cos(angle - subBranchAngle); + const y4 = this.position.y - r4 * Math.sin(angle - subBranchAngle); + + context.moveTo(x2, y2); + context.lineTo(x3, y3); + context.moveTo(x2, y2); + context.lineTo(x4, y4); + }; +} diff --git a/src/lib/canvas/weather/animate/Thunder.ts b/src/lib/canvas/weather/animate/Thunder.ts new file mode 100644 index 00000000..557811f0 --- /dev/null +++ b/src/lib/canvas/weather/animate/Thunder.ts @@ -0,0 +1,55 @@ +import { Animator } from "$lib/canvas/Animator"; +import { randIntBetween } from "$lib/utils/maths"; + +export class Thunder extends Animator { + frequency: number; + width: number; + height: number; + + countTo: number = 20; + count: number = 0; + flashCount: number = 0; + flashes: number = 2; + + constructor(context: CanvasRenderingContext2D, canvasHeight: number, canvasWidth: number, frequency: number) { + super(context); + this.frequency = frequency; + this.width = canvasWidth; + this.height = canvasHeight; + this.resetCounts(); + } + + animate() { + if (this.count >= this.countTo) { + if (this.flashCount <= this.flashes) { + if (this.flashCount % 8 == 0) { + this.drawFlash(); + } else if (this.flashCount % 4 == 0) { + this.drawBlank(); + } + this.flashCount++; + } else { + this.drawBlank(); + this.resetCounts(); + } + } else { + this.count++; + } + } + + resetCounts() { + this.count = 0; + this.flashCount = 0; + this.flashes = randIntBetween(4, 20); + this.countTo = randIntBetween(80, 400); + } + + drawFlash() { + this.ctx.fillStyle = "white"; + this.ctx.fillRect(0, 0, this.width, this.height); + } + drawBlank() { + this.ctx.fillStyle = "none"; + this.ctx.fillRect(0, 0, this.width, this.height); + } +} diff --git a/src/lib/canvas/weather/animate/utils.ts b/src/lib/canvas/weather/animate/utils.ts new file mode 100644 index 00000000..6d1bc116 --- /dev/null +++ b/src/lib/canvas/weather/animate/utils.ts @@ -0,0 +1,22 @@ +export class FrameRate { + + startTime: number = Date.now(); + totalElapsedTime: number = 0; + frames: number = 0; + frameRate: number = 0; + + calculateFrameRate() { + this.frames += 1; + this.totalElapsedTime = Date.now() - this.startTime; + this.frameRate = this.frames / this.totalElapsedTime * 1000; + } + + getFrameRate() { + return this.frameRate; + } + + getElapsedTime() { + return this.totalElapsedTime; + } + +} \ No newline at end of file diff --git a/src/routes/weather/DrawCharacter.ts b/src/lib/canvas/weather/draw/character.ts similarity index 90% rename from src/routes/weather/DrawCharacter.ts rename to src/lib/canvas/weather/draw/character.ts index 872c102a..8a4e1c12 100644 --- a/src/routes/weather/DrawCharacter.ts +++ b/src/lib/canvas/weather/draw/character.ts @@ -1,16 +1,16 @@ -import { Artist } from '../../lib/canvas/Artist'; +import { Artist } from "../../Artist"; -const AMBER = 'rgb(245, 167, 66)'; +const AMBER = "rgb(245, 167, 66)"; // const DARK_ORANGE = "rgb(200, 100, 100)"; -const WHITE = 'white'; -const BLACK = 'black'; +const WHITE = "white"; +const BLACK = "black"; // const GREY = "rgb(50, 50, 50)"; -const ORANGE_BROWN = 'rgb(220, 150, 110)'; -const SHADOW_BROWN = 'rgb(120, 80, 20)'; -const DARK_SHADOW_BROWN = 'rgb(71, 50, 44)'; -const DARK_GREY = 'rgb(50, 50, 50)'; -const PINK = 'rgb(220, 100, 150)'; -const DARK_PINK = 'rgb(150, 50, 100)'; +const ORANGE_BROWN = "rgb(220, 150, 110)"; +const SHADOW_BROWN = "rgb(120, 80, 20)"; +const DARK_SHADOW_BROWN = "rgb(71, 50, 44)"; +const DARK_GREY = "rgb(50, 50, 50)"; +const PINK = "rgb(220, 100, 150)"; +const DARK_PINK = "rgb(150, 50, 100)"; export function drawCharacter(context: CanvasRenderingContext2D, x: number, y: number) { const a = new Artist(context, x, y); @@ -30,7 +30,7 @@ export function drawCharacter(context: CanvasRenderingContext2D, x: number, y: n // chin [-10, 10], [-10, 5], - [-10, 0] + [-10, 0], ]; const HEAD_PATCH_POINTS: Points = [ @@ -40,7 +40,7 @@ export function drawCharacter(context: CanvasRenderingContext2D, x: number, y: n [10, 35], [-20, -15], [-15, -8], - [-20, 0] + [-20, 0], ]; const RIGHT_EAR_POINTS: Points = [ @@ -61,7 +61,7 @@ export function drawCharacter(context: CanvasRenderingContext2D, x: number, y: n [-5, -10], [-5, -10], [5, -20], - [-5, -20] + [-5, -20], ]; const LEFT_EAR_POINTS: Points = [ @@ -79,7 +79,7 @@ export function drawCharacter(context: CanvasRenderingContext2D, x: number, y: n [10, 5], [12, -20], [0, -25], - [5, -10] + [5, -10], ]; const NOSE: Points = [ @@ -89,7 +89,7 @@ export function drawCharacter(context: CanvasRenderingContext2D, x: number, y: n [-2, 4], [-4, 2], [-3, 0], - [-3, -1] + [-3, -1], ]; const TONGUE: Points = [ @@ -98,7 +98,7 @@ export function drawCharacter(context: CanvasRenderingContext2D, x: number, y: n [-1, 10], [-4, 5], [-4, 3], - [-4, 0] + [-4, 0], ]; a.drawShape(HEAD_POINTS, true, 0, 0, 2, ORANGE_BROWN, ORANGE_BROWN); @@ -109,7 +109,7 @@ export function drawCharacter(context: CanvasRenderingContext2D, x: number, y: n a.endX = a.startX + 20; a.endY = a.startY + 35; context.bezierCurveTo(a.startX, a.startY, a.startX, a.startY + 15, a.endX, a.endY); - context.lineCap = 'round'; + context.lineCap = "round"; context.lineWidth = 4; context.strokeStyle = SHADOW_BROWN; context.stroke(); @@ -119,7 +119,7 @@ export function drawCharacter(context: CanvasRenderingContext2D, x: number, y: n a.endX = a.startX - 20; a.endY = a.startY + 35; context.bezierCurveTo(a.startX, a.startY, a.startX, a.startY + 15, a.endX, a.endY); - context.lineCap = 'round'; + context.lineCap = "round"; context.lineWidth = 4; context.strokeStyle = SHADOW_BROWN; context.stroke(); @@ -178,7 +178,7 @@ function drawFaceLines(context: CanvasRenderingContext2D, x: number, y: number) endY = startY - 5; context.bezierCurveTo(startX, startY, startX - 2, startY - 7, endX, endY); - context.lineCap = 'round'; + context.lineCap = "round"; context.lineWidth = 4; context.strokeStyle = SHADOW_BROWN; context.stroke(); diff --git a/src/lib/canvas/weather/draw/tree.ts b/src/lib/canvas/weather/draw/tree.ts new file mode 100644 index 00000000..f65f1413 --- /dev/null +++ b/src/lib/canvas/weather/draw/tree.ts @@ -0,0 +1,47 @@ +import { randRangeRGBString } from "$lib/utils/colour"; + +// inspired by +// https://github.com/PavlyukVadim/amadev/blob/master/RecursiveTree/script.js +export function drawTree( + context: CanvasRenderingContext2D, + startX: number, + startY: number, + length: number, + angle: number, + depth: number, + branchWidth: number, +) { + let newLength, newAngle; + const rand = Math.random; + const maxAngle = (2 * Math.PI) / 6; + const maxBranch = 3; + const endX = startX + length * Math.cos(angle); + const endY = startY + length * Math.sin(angle); + + context.beginPath(); + context.moveTo(startX, startY); + context.lineCap = "round"; + context.lineWidth = branchWidth; + context.lineTo(endX, endY); + + if (depth <= 4) { + context.strokeStyle = randRangeRGBString(30, [64, 180], 0); + } else { + context.strokeStyle = randRangeRGBString([70, 80], 60, [30, 50]); + } + + context.stroke(); + const newDepth = depth - 1; + + if (!newDepth) { + return; + } + const subBranches = rand() * (maxBranch - 1) + 1; + branchWidth *= 0.7; + + for (let i = 0; i < subBranches; i++) { + newAngle = angle + rand() * maxAngle - maxAngle * 0.5; + newLength = length * (0.7 + rand() * 0.3); + drawTree(context, endX, endY, newLength, newAngle, newDepth, branchWidth); + } +} diff --git a/src/lib/canvas/weather/weatherTime.ts b/src/lib/canvas/weather/weatherTime.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/lib/components/creative/clock/Clock.svelte b/src/lib/components/creative/clock/Clock.svelte new file mode 100644 index 00000000..2f8da85f --- /dev/null +++ b/src/lib/components/creative/clock/Clock.svelte @@ -0,0 +1,113 @@ + + +
+
+
+ + : + +
+
+
+ + diff --git a/src/lib/components/creative/clock/PaddedNumberSpinnerInput.svelte b/src/lib/components/creative/clock/PaddedNumberSpinnerInput.svelte new file mode 100644 index 00000000..7ef75294 --- /dev/null +++ b/src/lib/components/creative/clock/PaddedNumberSpinnerInput.svelte @@ -0,0 +1,85 @@ + + +
+ + parseChange(e)}/> + +
+ + diff --git a/src/lib/components/creative/weather/BigWeatherButton.svelte b/src/lib/components/creative/weather/BigWeatherButton.svelte new file mode 100644 index 00000000..974510d9 --- /dev/null +++ b/src/lib/components/creative/weather/BigWeatherButton.svelte @@ -0,0 +1,14 @@ + + + diff --git a/src/lib/components/creative/weather/WeatherControls.svelte b/src/lib/components/creative/weather/WeatherControls.svelte new file mode 100644 index 00000000..4f464f9e --- /dev/null +++ b/src/lib/components/creative/weather/WeatherControls.svelte @@ -0,0 +1,30 @@ + + +
+
+ {#each WEATHERS as weather} + setCurrentWeather(weather)} /> + {/each} +
+
+ + diff --git a/src/lib/components/utils/BigIconButton.svelte b/src/lib/components/utils/BigIconButton.svelte new file mode 100644 index 00000000..579eda00 --- /dev/null +++ b/src/lib/components/utils/BigIconButton.svelte @@ -0,0 +1,50 @@ + + + + + diff --git a/src/lib/data/weatherData.ts b/src/lib/data/weatherData.ts new file mode 100644 index 00000000..36d59158 --- /dev/null +++ b/src/lib/data/weatherData.ts @@ -0,0 +1,92 @@ +export enum Weather { + Clear = "Clear", + Cloudy = "Cloudy", + Overcast = "Overcast", + Fog = "Fog", + Drizzle = "Drizzle", + Rain = "Rain", + Snow = "Snow", + Thunder = "Thunder", +} + +export const WEATHERS = Object.entries(Weather).map((w) => w[1]); + +export enum Direction { + N = "North", + NE = "North East", + E = "East", + SE = "South East", + S = "South", + SW = "South West", + W = "West", + NW = "North West", +} + +export interface WeatherAttributes { + name: string; + icon: string; + color: string; +} + +export const WEATHER_ATTRIBUTES: Record = { + Clear: { name: "Clear", color: "gold", icon: "sun" }, + Cloudy: { name: "Cloudy", color: "lightgray", icon: "cloud" }, + Overcast: { name: "Overcast", color: "gray", icon: "cloud" }, + Fog: { name: "Fog", color: "gray", icon: "smog" }, + Drizzle: { name: "Drizzle", color: "lightblue", icon: "cloud-rain" }, + Rain: { name: "Rain", color: "dodgerblue", icon: "cloud-rain" }, + Snow: { name: "Snow", color: "aliceblue", icon: "snowflake" }, + Thunder: { name: "Thunder", color: "darkgray", icon: "cloud-bolt" }, +}; + +/* +Code Description +0 Clear sky +1, 2, 3 Mainly clear, partly cloudy, and overcast +45, 48 Fog and depositing rime fog +51, 53, 55 Drizzle: Light, moderate, and dense intensity +56, 57 Freezing Drizzle: Light and dense intensity +61, 63, 65 Rain: Slight, moderate and heavy intensity +66, 67 Freezing Rain: Light and heavy intensity +71, 73, 75 Snow fall: Slight, moderate, and heavy intensity +77 Snow grains +80, 81, 82 Rain showers: Slight, moderate, and violent +85, 86 Snow showers slight and heavy +95 * Thunderstorm: Slight or moderate +96, 99 * Thunderstorm with slight and heavy hail +*/ + +export function getWeatherFromCode(code: number): Weather { + switch (code) { + case 0: + return Weather.Clear; + case 1 | 2: + return Weather.Cloudy; + case 3: + return Weather.Overcast; + case 45 | 48: + return Weather.Fog; + case 51 | 53 | 55 | 56 | 57: + return Weather.Drizzle; + case 61 | 63 | 65 | 66 | 67 | 80 | 81 | 82: + return Weather.Rain; + case 71 | 73 | 75 | 77 | 85 | 86: + return Weather.Snow; + case 95 | 96 | 99: + return Weather.Thunder; + default: + return Weather.Cloudy; + } +} + +export function getDirectionFromAngle(angle: number): Direction { + if (angle >= 337.5 || angle < 22.5) return Direction.N; + if (angle >= 22.5 && angle < 67.5) return Direction.NE; + if (angle >= 67.5 && angle < 112.5) return Direction.E; + if (angle >= 112.5 && angle < 157.5) return Direction.SE; + if (angle >= 157.5 && angle < 202.5) return Direction.S; + if (angle >= 202.5 && angle < 247.5) return Direction.SW; + if (angle >= 247.5 && angle < 292.5) return Direction.W; + if (angle >= 292.5 && angle < 337.5) return Direction.NW; + return Direction.N; +} diff --git a/src/lib/theme.ts b/src/lib/theme.ts index ee06b737..03171c49 100644 --- a/src/lib/theme.ts +++ b/src/lib/theme.ts @@ -9,23 +9,29 @@ export function applyDarkTheme(darkTheme: boolean) { if (darkTheme) { root.style.setProperty('--primary', 'var(--white)'); root.style.setProperty('--primary-50', 'var(--white-50)'); + root.style.setProperty('--primary-25', 'var(--white-25)'); root.style.setProperty('--secondary', 'var(--black)'); root.style.setProperty('--secondary-50', 'var(--black-50)'); + root.style.setProperty('--secondary-25', 'var(--black-25)'); root.style.setProperty('--tertiary', 'var(--dark-grey)'); root.style.setProperty('--bg0', 'var(--dark-grey)'); root.style.setProperty('--bg1', 'var(--grey)'); root.style.setProperty('--caption', 'var(--light-grey)'); root.style.setProperty('--highlight', `var(--${get(currentColourTheme)}-bright)`); + root.style.setProperty('--highlight-inverse', 'var(--highlight'); } else { root.style.setProperty('--primary', 'var(--black)'); root.style.setProperty('--primary-50', 'var(--black-50)'); + root.style.setProperty('--primary-25', 'var(--black-25)'); root.style.setProperty('--secondary', 'var(--white)'); root.style.setProperty('--secondary-50', 'var(--white-50)'); + root.style.setProperty('--secondary-25', 'var(--white-25)'); root.style.setProperty('--tertiary', 'var(--grey)'); root.style.setProperty('--bg0', 'var(--light-tan)'); root.style.setProperty('--bg1', 'var(--white)'); root.style.setProperty('--caption', 'var(--dark-grey)'); root.style.setProperty('--highlight', `var(--${get(currentColourTheme)}-dark)`); + root.style.setProperty('--highlight-inverse', `var(--${get(currentColourTheme)}-bright)`); } } } diff --git a/src/lib/utils/colour.ts b/src/lib/utils/colour.ts new file mode 100644 index 00000000..6953995c --- /dev/null +++ b/src/lib/utils/colour.ts @@ -0,0 +1,16 @@ +import { randBetween } from "./maths"; + +export const randRangeRGBString = ( + redRange: [number, number] | number = [0, 255], + greenRange: [number, number] | number = [0, 255], + blueRange: [number, number] | number = [0, 255], +) => { + const getValue = (range: [number, number] | number) => { + return Array.isArray(range) ? randBetween(range[0], range[1]) : range; + }; + + const red = getValue(redRange); + const green = getValue(greenRange); + const blue = getValue(blueRange); + return `rgb(${red}, ${green}, ${blue})`; +}; diff --git a/src/lib/utils/date.ts b/src/lib/utils/date.ts index 99e340b8..68171b36 100644 --- a/src/lib/utils/date.ts +++ b/src/lib/utils/date.ts @@ -13,3 +13,4 @@ export function dayOfYear(date: Date) { const diff = date.valueOf() - new Date(date.getFullYear(), 0, 0).valueOf(); return Math.floor(diff / (1000 * 60 * 60 * 24)); } + diff --git a/src/lib/utils/maths.ts b/src/lib/utils/maths.ts index 17735332..826f8502 100644 --- a/src/lib/utils/maths.ts +++ b/src/lib/utils/maths.ts @@ -6,3 +6,17 @@ export function randomMinMax(min: number, max: number) { export function randomDirection(): 1 | -1 { return Math.random() >= 0.5 ? 1 : -1; } + +export function randBetween(min: number, max: number) { + return Math.random() * (max - min) + min; +} + +export function randIntBetween(min: number, max: number) { + return Math.floor(randBetween(min - 1, max)); +} + +export function randVariance(value: number, variance: number) { + const min = value * (1 - variance); + const max = value * (1 + variance); + return randBetween(min, max); +} diff --git a/src/routes/weather/weatherTime.ts b/src/lib/utils/time.ts similarity index 65% rename from src/routes/weather/weatherTime.ts rename to src/lib/utils/time.ts index 42ab0f40..24abcca2 100644 --- a/src/routes/weather/weatherTime.ts +++ b/src/lib/utils/time.ts @@ -1,12 +1,16 @@ export function timeNoun(time: Date): string { const hour = time.getHours(); + return hourNoun(hour); +} + +export function hourNoun(hour: number): string { switch (hour) { case 6: - return 'dawn'; + return "dawn"; case 7: - return 'sunrise'; + return "sunrise"; case 8: - return 'morning'; + return "morning"; case 9: case 10: case 11: @@ -17,13 +21,13 @@ export function timeNoun(time: Date): string { case 16: case 17: case 18: - return 'day'; + return "day"; case 19: - return 'evening'; + return "evening"; case 20: - return 'sunset'; + return "sunset"; case 21: - return 'dusk'; + return "dusk"; case 22: case 23: case 24: @@ -33,9 +37,9 @@ export function timeNoun(time: Date): string { case 3: case 4: case 5: - return 'night'; + return "night"; default: - return 'day'; + return "day"; } } diff --git a/src/posts/weather-widget.md b/src/posts/weather-widget.md index 6becb8e6..744c54dd 100644 --- a/src/posts/weather-widget.md +++ b/src/posts/weather-widget.md @@ -1,9 +1,10 @@ --- title: Weather - Widget description: A fun little widget that almost works well. -date: 01/01/2023 +date: 01/17/2026 projectId: programming published: false dev: true technologies: [Typescript, HTML5] --- + diff --git a/src/routes/Header.svelte b/src/routes/Header.svelte index 5fd05488..7ce091d2 100644 --- a/src/routes/Header.svelte +++ b/src/routes/Header.svelte @@ -18,9 +18,9 @@ > Projects - + diff --git a/src/routes/weather/+page.server.ts b/src/routes/weather/+page.server.ts index f3290860..ee6b532a 100644 --- a/src/routes/weather/+page.server.ts +++ b/src/routes/weather/+page.server.ts @@ -1,39 +1,60 @@ -import { error } from '@sveltejs/kit'; -import type { PageServerLoad } from './$types'; +// import { error } from '@sveltejs/kit'; +import { error } from "@sveltejs/kit"; +import type { PageServerLoad } from "./$types"; export const prerender = false; -const openMeteoBaseUrl = 'https://api.open-meteo.com/v1/forecast'; +/* eslint-disable */ +const openMeteoBaseUrl = "https://api.open-meteo.com/v1/forecast"; // default to edinburgh let location = { latitude: 55.953251, - longitude: -3.188267 + longitude: -3.188267, }; const fetchWeather = async (latitude: number, longitude: number) => { const request = `${openMeteoBaseUrl}?latitude=${latitude}&longitude=${longitude}¤t_weather=true`; const result = await fetch(request); const data = await result.json(); - console.log(data); return data; }; export const load = (async () => { - if (typeof window !== 'undefined' && 'geolocation' in window.navigator) { - window.navigator.geolocation.getCurrentPosition((position) => { - location = { - latitude: position.coords.latitude, - longitude: position.coords.longitude - }; - }); + if ("geolocation" in navigator) { + window.navigator.geolocation.getCurrentPosition( + (position) => { + location = { + latitude: position.coords.latitude, + longitude: position.coords.longitude, + }; + }, + (err) => { + console.log("error", err); + }, + ); } else { - console.log('geolocation permissions blocked, getting weather from Edinburgh instead'); + console.log(typeof window); + console.log("geolocation permissions blocked, getting weather from Edinburgh instead"); } try { + console.log(`using coords: ${location.latitude} ${location.longitude}`); const weather = await fetchWeather(location.latitude, location.longitude); - return weather.current_weather ?? undefined; + const currentWeather = weather.current_weather ?? undefined; + return { + latitude: weather.latitude, + longitude: weather.longitude, + ...currentWeather, + }; } catch { - error(404, "Wasn't able to get any weather data.\n\n It's probably raining"); + throw error(404, "Wasn't able to get any weather data.\n\n It's probably raining"); + return { + temperature: 16.3, + windspeed: 18.4, + winddirection: 78, + weathercode: 17, + is_day: 1, + time: Date.now(), + }; } }) satisfies PageServerLoad; diff --git a/src/routes/weather/+page.svelte b/src/routes/weather/+page.svelte index 666fb1d1..ee92e8f6 100644 --- a/src/routes/weather/+page.svelte +++ b/src/routes/weather/+page.svelte @@ -1,35 +1,69 @@ + let time = new Date(data.time); -
- -
+ let elapsedTime: number; + let frameRate: number; -
-
Time:
-
{time.toLocaleTimeString()}
-
Temperature:
-
{data.temperature} °C
-
Weather:
-
{weather}
-
Wind speed:
-
{data.windspeed} km/h
-
Wind direction:
-
{windDirection}
-
+ let showDiagnostics: boolean = true; + let setHours: number = time.getHours(); + let setMinutes: number = time.getMinutes(); + + $: updateTime(setHours, setMinutes); -
- + function updateTime(hour: number, minutes: number) { + const newTime = time; + newTime?.setHours(hour, minutes, 0); + time = newTime; + } + + +
+ + + {#if import.meta.env.DEV} +
+ + +
+ {/if} +
+
+ +
+ +
+
+
Latt/Long:
+
{`${data.latitude}, ${data.longitude}`}
+
Time:
+
{time.toLocaleTimeString()}
+
Temperature:
+
{data.temperature} °C
+
Weather:
+
{weather}
+
Wind speed:
+
{data.windspeed} km/h
+
Wind direction:
+
{windDirection}
+
+ {#if showDiagnostics && frameRate} +
+
+ fps: {frameRate?.toFixed(1)} +
+
+ {/if}
diff --git a/src/routes/weather/Compass.svelte b/src/routes/weather/Compass.svelte index f0083966..32544467 100644 --- a/src/routes/weather/Compass.svelte +++ b/src/routes/weather/Compass.svelte @@ -1,189 +1,83 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/routes/weather/Scene.svelte b/src/routes/weather/Scene.svelte index 57ad9496..a02af43f 100644 --- a/src/routes/weather/Scene.svelte +++ b/src/routes/weather/Scene.svelte @@ -1,39 +1,79 @@ - - +
+ + + +
diff --git a/src/routes/weather/WeatherSceneAnimator.ts b/src/routes/weather/WeatherSceneAnimator.ts new file mode 100644 index 00000000..d6b007f8 --- /dev/null +++ b/src/routes/weather/WeatherSceneAnimator.ts @@ -0,0 +1,140 @@ +import { Animator } from "$lib/canvas/Animator"; +import { Cloud } from "$lib/canvas/weather/animate/Cloud"; +import { Rain } from "$lib/canvas/weather/animate/Rain"; +import { Snow } from "$lib/canvas/weather/animate/Snow"; +import { Thunder } from "$lib/canvas/weather/animate/Thunder"; +import { FrameRate } from "$lib/canvas/weather/animate/utils"; +import { randIntBetween } from "$lib/utils/maths"; +import { Weather } from "../../lib/data/weatherData"; + +export class WeatherSceneController extends Animator { + height: number; + width: number; + animators: Animator[]; + windspeed: number; + currentWeather: Weather; + frameRate: FrameRate; + + constructor(context: CanvasRenderingContext2D, canvasWidth: number, canvasHeight: number, windspeed: number, weather: Weather) { + super(context); + this.animators = []; + this.height = canvasHeight; + this.width = canvasWidth; + this.windspeed = windspeed; + this.currentWeather = weather; + + this.frameRate = new FrameRate(); + this.setWeather(weather); + } + + animate(): void { + this.ctx.clearRect(0, 0, this.width, this.height); + this.animators.forEach((a) => a.animate()); + this.frameRate.calculateFrameRate(); + } + + clearAnimators() { + this.animators.splice(0, this.animators.length); + } + + setWindspeed(windspeed: number) { + this.windspeed = windspeed; + this.setWeather(this.currentWeather); + } + + setWeather(weather: Weather) { + this.currentWeather = weather; + this.clearAnimators(); + + switch (weather) { + case Weather.Clear: + this.setClear(); + break; + case Weather.Cloudy: + this.setCloudy(15, this.windspeed / 2); + break; + case Weather.Overcast: + this.setOvercast(); + break; + case Weather.Rain: + this.setCloudy(); + this.setRain(); + this.setCloudy(); + break; + case Weather.Drizzle: + this.setCloudy(); + this.setDrizzle(); + break; + case Weather.Snow: + this.setCloudy(); + this.setSnow(); + break; + case Weather.Thunder: + this.setRain(); + this.setCloudy(); + this.setThunder(); + break; + case Weather.Fog: + this.setFog(); + break; + default: + break; + } + } + + setThunder() { + const thunder = new Thunder(this.ctx, this.height, this.width, 0.5); + this.animators.push(thunder); + } + + setClear() { + return; + } + + setCloudy(count: number = 10, speed: number = this.windspeed) { + for (let i = 0; i < count; i++) { + const gray = randIntBetween(100, 170) + this.animators.push( + new Cloud( + this.ctx, + randIntBetween(0, this.width - 100), + randIntBetween(0, this.height / 4), + randIntBetween(20, 60), + randIntBetween(10, 30), + Math.max(1.1, speed), + `rgb(${gray}, ${gray}, ${gray})`, + ), + ); + } + } + + setOvercast() { + this.setCloudy(30, this.windspeed / 10); + } + + setRain() { + const rainFg = new Rain(this.ctx, 20, 0.3, this.windspeed, 10); + const rainBg = new Rain(this.ctx, 10, 0.8, this.windspeed, 5); + + this.animators.push(rainFg); + // this.animators.push(rainBg); + } + + setDrizzle() { + const rain = new Rain(this.ctx, 10, 0.3, this.windspeed, 5); + + this.animators.push(rain); + } + + setSnow() { + const snowFg = new Snow(this.ctx, 4, 0.9, this.windspeed, 40); + const snowBg = new Snow(this.ctx, 2, 0.9, this.windspeed, 20); + + this.animators.push(snowFg); + this.animators.push(snowBg); + } + + setFog() { + return; + } +} diff --git a/src/routes/weather/weatherData.ts b/src/routes/weather/weatherData.ts deleted file mode 100644 index a638431b..00000000 --- a/src/routes/weather/weatherData.ts +++ /dev/null @@ -1,56 +0,0 @@ -export enum Weather { - Clear = 'Clear', - Cloudy = 'Cloudy', - Overcast = 'Overcast', - Fog = 'Fog', - Drizzle = 'Drizzle', - Rain = 'Rain', - Snow = 'Snow', - Thunder = 'Thunder' -} - -export enum Direction { - N = 'North', - NE = 'North East', - E = 'East', - SE = 'South East', - S = 'South', - SW = 'South West', - W = 'West', - NW = 'North West' -} - -export function getWeatherFromCode(code: number): Weather { - switch (code) { - case 0: - return Weather.Clear; - case 1 | 2: - return Weather.Cloudy; - case 3: - return Weather.Overcast; - case 45 | 48: - return Weather.Fog; - case 51 | 53 | 55 | 56 | 57: - return Weather.Drizzle; - case 61 | 63 | 65 | 66 | 67 | 80 | 81 | 82: - return Weather.Rain; - case 71 | 73 | 75 | 77 | 85 | 86: - return Weather.Snow; - case 95 | 96 | 99: - return Weather.Thunder; - default: - return Weather.Cloudy; - } -} - -export function getDirectionFromAngle(angle: number): Direction { - if (angle >= 337.5 || angle < 22.5) return Direction.N; - if (angle >= 22.5 && angle < 67.5) return Direction.NE; - if (angle >= 67.5 && angle < 112.5) return Direction.E; - if (angle >= 112.5 && angle < 157.5) return Direction.SE; - if (angle >= 157.5 && angle < 202.5) return Direction.S; - if (angle >= 202.5 && angle < 247.5) return Direction.SW; - if (angle >= 247.5 && angle < 292.5) return Direction.W; - if (angle >= 292.5 && angle < 337.5) return Direction.NW; - return Direction.N; -} diff --git a/src/routes/weather/weatherScene.ts b/src/routes/weather/weatherScene.ts deleted file mode 100644 index 3407e6c5..00000000 --- a/src/routes/weather/weatherScene.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { drawCircle } from '$lib/canvas/canvasUtils'; - -export function drawOrbit( - context: CanvasRenderingContext2D, - time: Date, - orbitCenX: number, - orbitCenY: number, - orbitRadius: number, - orbitBodyRadius: number -) { - // ctx.beginPath(); - // ctx.arc( - // orbitCentreX, - // orbitCentreY, - // orbitRadius, - // Math.PI, Math.PI * 2, false); - // ctx.stroke(); - const hours = time.getHours(); - const moduloTime = hours + (6 % 24); - const orbitAngle = Math.PI - ((((2 * Math.PI) / 24) * moduloTime) % Math.PI) - Math.PI / 2; - const orbitLenX = Math.sin(orbitAngle) * orbitRadius; - const orbitLenY = Math.cos(orbitAngle) * orbitRadius; - const orbitPosX = orbitCenX - orbitLenX; - const orbitPosY = orbitCenY - orbitLenY; - - // sun or moon - let radialGradient; - if (hours >= 6 && hours < 18) { - radialGradient = getSunRadialGradient(context, orbitPosX, orbitPosY, orbitBodyRadius); - } else { - radialGradient = getMoonRadialGradient(context, orbitPosX, orbitPosY, orbitBodyRadius); - } - context.fillStyle = radialGradient; - context.arc(orbitPosX, orbitPosY, orbitBodyRadius, 0, 2 * Math.PI, false); - context.fill(); - - // context.fillRect(orbitPositionX, orbitPositionY, orbitBodyRadius * 2, orbitBodyRadius * 2); -} - -function getSunRadialGradient(context: CanvasRenderingContext2D, x: number, y: number, r: number) { - const radialGradient = context.createRadialGradient(x, y, r * 0.8, x, y, r); - radialGradient.addColorStop(0, 'rgba(255, 240, 210, 1)'); - radialGradient.addColorStop(1, 'rgba(255, 255, 0, 0)'); - return radialGradient; -} - -function getMoonRadialGradient(context: CanvasRenderingContext2D, x: number, y: number, r: number) { - const radialGradient = context.createRadialGradient(x, y, r * 0.5, x, y, r * 0.8); - radialGradient.addColorStop(0, 'rgba(255, 255, 255, 1)'); - radialGradient.addColorStop(1, 'rgba(50, 50, 50, 0)'); - return radialGradient; -} - -// https://github.com/PavlyukVadim/amadev/blob/master/RecursiveTree/script.js -export function drawTree( - context: CanvasRenderingContext2D, - startX: number, - startY: number, - length: number, - angle: number, - depth: number, - branchWidth: number -) { - let newLength, newAngle; - const rand = Math.random; - const maxAngle = (2 * Math.PI) / 6; - const maxBranch = 3; - const endX = startX + length * Math.cos(angle); - const endY = startY + length * Math.sin(angle); - - context.beginPath(); - context.moveTo(startX, startY); - context.lineCap = 'round'; - context.lineWidth = branchWidth; - context.lineTo(endX, endY); - - if (depth <= 2) { - context.strokeStyle = `rgb(30, ${(rand() * 64 + 128) >> 0}, 0)`; - } else { - context.strokeStyle = `rgb(30, ${(rand() * 64 + 64) >> 0}, 20)`; - } - - context.stroke(); - const newDepth = depth - 1; - - if (!newDepth) { - return; - } - const subBranches = rand() * (maxBranch - 1) + 1; - branchWidth *= 0.7; - - for (let i = 0; i < subBranches; i++) { - newAngle = angle + rand() * maxAngle - maxAngle * 0.5; - newLength = length * (0.7 + rand() * 0.3); - drawTree(context, endX, endY, newLength, newAngle, newDepth, branchWidth); - } -} - -// export function drawClouds(context: CanvasRenderingContext2D, direction: number, density: number) { - -// } - -export function drawCloud(context: CanvasRenderingContext2D, x: number, y: number, size: number) { - const sizeVarFactor = 0.8; - const rowVarFactor = 0.9; - const sizeVariance = size * sizeVarFactor; - // get random size between 0.75 and 1.5 x provided size - const getBlobSize = () => Math.floor(Math.random() * sizeVariance + sizeVariance); - const getRowLength = (middle: number) => - Math.floor(Math.random() * middle * rowVarFactor + middle * rowVarFactor); - const numberOfRows = Math.floor(Math.random() * 3 + 3); - - let rowLength = 4; - let newSize = size; - let newPosX; - let newPosY; - const cloudColour = 'rgba(255, 255, 255, 0.8)'; - for (let cy = 0; cy <= numberOfRows * size; cy += size) { - rowLength = getRowLength(rowLength); - for (let cx = 0; cx <= rowLength * size; cx += size) { - const offset = Math.random() * newSize * 2 + newSize; - newSize = getBlobSize(); - newPosX = cx + x + offset; - newPosY = cy + y; - drawCircle(context, newPosX, newPosY, newSize, undefined, cloudColour); - } - } -} diff --git a/static/fonts/Silkscreen/Silkscreen-Bold.ttf b/static/fonts/Silkscreen/Silkscreen-Bold.ttf new file mode 100644 index 00000000..e5a5b819 Binary files /dev/null and b/static/fonts/Silkscreen/Silkscreen-Bold.ttf differ diff --git a/static/fonts/Silkscreen/Silkscreen-Regular.ttf b/static/fonts/Silkscreen/Silkscreen-Regular.ttf new file mode 100644 index 00000000..8abaa7c5 Binary files /dev/null and b/static/fonts/Silkscreen/Silkscreen-Regular.ttf differ diff --git a/static/styles/fonts.css b/static/styles/fonts.css index f3afb170..c75673ba 100644 --- a/static/styles/fonts.css +++ b/static/styles/fonts.css @@ -199,4 +199,16 @@ src: url("/fonts/Fira_Mono/FiraMono-Bold.ttf"); font-weight: 700; font-style: "normal"; -} \ No newline at end of file +} + +/* Silkscreen */ +@font-face { + font-family: "Silkscreen"; + src: url("/fonts/Silkscreen/Silkscreen-Regular.ttf"); + font-weight: 400; +} +@font-face { + font-family: "Silkscreen"; + src: url("/fonts/Silkscreen/Silkscreen-Bold.ttf"); + font-weight: 700; +} diff --git a/static/styles/global.css b/static/styles/global.css index f60f264f..2302bc54 100644 --- a/static/styles/global.css +++ b/static/styles/global.css @@ -9,8 +9,10 @@ /* Monochromes */ --white: rgb(250, 235, 215); --white-50: rgba(250, 235, 215, 0.5); + --white-25: rgba(250, 235, 215, 0.25); --black: rgb(0, 0, 0); --black-50: rgb(0, 0, 0, 0.5); + --black-25: rgb(0, 0, 0, 0.25); --light-tan: rgb(220, 215, 200); --light-grey: rgb(150, 150, 150); @@ -20,7 +22,7 @@ /* Themes */ --gold-bright: rgb(255, 177, 8); - --gold-dark: rgb(194, 132, 0); + --gold-dark: rgb(155, 107, 3); --teal-bright: rgb(18, 235, 152); --teal-dark: rgb(4, 168, 105); --sky-bright: rgb(124, 207, 255); @@ -35,8 +37,11 @@ --secondary: var(--black); --tertiary: var(--dark-grey); --highlight: var(--gold-bright); + --highlight-inverse: var(--gold-dark); --primary-50: var(--white-50); + --primary-25: var(--white-25); --secondary-50: var(--black-50); + --secondary-25: var(--black-25); --caption: var(--light-grey); /* Sizes */