From f7bc1bdc56f4c74287c75a0195b0ecefaad96a96 Mon Sep 17 00:00:00 2001 From: Atul Varma Date: Sun, 4 Jul 2021 16:34:17 -0400 Subject: [PATCH] Add some hills to the waves page. (#177) This minimally fixes #143 by adding optional hills to the waves page (they default to being disabled but can be enabled via a checkbox). It also adds haze to the waves, so that they are blended with the color of the sky as they recede into the distance. --- lib/color-util.ts | 14 +++++++ lib/pages/waves-page.tsx | 90 +++++++++++++++++++++++++++++++++++++++- lib/util.ts | 8 ++++ 3 files changed, 110 insertions(+), 2 deletions(-) diff --git a/lib/color-util.ts b/lib/color-util.ts index 2af8651..940ea70 100644 --- a/lib/color-util.ts +++ b/lib/color-util.ts @@ -1,3 +1,5 @@ +import { lerp } from "./util"; + /** * Clamp the given number to be between 0 and 255, then * convert it to hexadecimal. @@ -32,3 +34,15 @@ export function parseHexColor(value: string): [number, number, number] { const blue = parseInt(value.substring(5, 7), 16); return [red, green, blue]; } + +/** + * Mix two colors together. The first color will be tinted by + * the second color by the given percentage (from 0 to 1). + */ +export function mixColor(a: string, b: string, amount: number) { + const aRGB = parseHexColor(a); + const bRGB = parseHexColor(b); + return clampedBytesToRGBColor( + aRGB.map((aValue, i) => Math.floor(lerp(aValue, bRGB[i], amount))) + ); +} diff --git a/lib/pages/waves-page.tsx b/lib/pages/waves-page.tsx index 206c1fd..225ce0d 100644 --- a/lib/pages/waves-page.tsx +++ b/lib/pages/waves-page.tsx @@ -1,8 +1,11 @@ import React, { useState } from "react"; import { Checkbox } from "../checkbox"; +import { mixColor } from "../color-util"; import { ColorWidget } from "../color-widget"; import { NumericSlider } from "../numeric-slider"; import { Page } from "../page"; +import { Random } from "../random"; +import { lerp, range } from "../util"; const WAVE_STROKE = "#79beda"; const WAVE_FILL = "#2b7c9e"; @@ -57,7 +60,9 @@ const Wave: React.FC<{ ); -const NUM_WAVES = 8; +const BG_COLOR = "#FFFFFF"; +const BG_MAX_BLEND = 0.33; +const NUM_WAVES = 10; const WAVE_DURATION = 1; const WAVE_PARALLAX_SCALE_START = 1.2; const WAVE_PARALLAX_TRANSLATE_START = 10; @@ -65,7 +70,51 @@ const WAVE_PARALLAX_SCALE_VELOCITY = 1.25; const WAVE_PARALLAX_TRANSLATE_VELOCITY = 30; const WAVE_PARALLAX_TRANSLATE_ACCEL = 10; +type HillProps = { + idPrefix: string; + xScale: number; + yScale: number; + cx: number; + cy: number; + r: number; + highlight: string; + shadow: string; +}; + +const DEFAULT_HILL_PROPS: HillProps = { + idPrefix: "", + xScale: 1, + yScale: 1, + cx: 50, + cy: 50, + r: 50, + highlight: "#aeb762", + shadow: "#616934", +}; + +const Hill: React.FC> = (props) => { + const { idPrefix, xScale, yScale, cx, cy, r, highlight, shadow } = { + ...DEFAULT_HILL_PROPS, + ...props, + }; + const gradientId = `${idPrefix}HillGradient`; + const gradientUrl = `url(#${gradientId})`; + + return ( + + + + + + + + ); +}; + const Waves: React.FC<{}> = () => { + const [randomSeed, setRandomSeed] = useState(Date.now()); + const newRandomSeed = () => setRandomSeed(Date.now()); + const rng = new Random(randomSeed); const [stroke, setStroke] = useState(WAVE_STROKE); const [fill, setFill] = useState(WAVE_FILL); const [numWaves, setNumWaves] = useState(NUM_WAVES); @@ -73,6 +122,7 @@ const Waves: React.FC<{}> = () => { const [initialYVel, setInitialYVel] = useState( WAVE_PARALLAX_TRANSLATE_VELOCITY ); + const [showHills, setShowHills] = useState(false); const [yAccel, setYAccel] = useState(WAVE_PARALLAX_TRANSLATE_ACCEL); const [scaleVel, setScaleVel] = useState(WAVE_PARALLAX_SCALE_VELOCITY); const [useMask, setUseMask] = useState(false); @@ -83,10 +133,40 @@ const Waves: React.FC<{}> = () => { let waves: JSX.Element[] = []; for (let i = 0; i < numWaves; i++) { + const numHills = Math.floor(rng.inInterval({ min: 0, max: numWaves - i })); + const hazeAmt = lerp( + 0, + BG_MAX_BLEND, + // The furthest-away waves (the first ones we draw) should be the + // most hazy. Scale the amount quadratically so that the waves in + // front tend to be less hazy. + Math.pow(1 - i / Math.max(numWaves - 1, 1), 2) + ); + const blendedFill = mixColor(fill, BG_COLOR, hazeAmt); + const blendedStroke = mixColor(stroke, BG_COLOR, hazeAmt); + waves.push( + {showHills && + range(numHills).map((j) => { + return ( + + ); + })} - + = () => { value={useMask} onChange={setUseMask} /> + + {showHills && ( + + )} ); diff --git a/lib/util.ts b/lib/util.ts index 1cf2aac..e2b0c3b 100644 --- a/lib/util.ts +++ b/lib/util.ts @@ -124,3 +124,11 @@ export function withoutNulls(arr: (T | null)[]): T[] { return result; } + +/** + * Linearly interpolate between the given numbers by the + * given percentage (from 0 to 1). + */ +export function lerp(a: number, b: number, amount: number) { + return a + amount * (b - a); +}