From 0663d032bb37eea0ed66b700fb9d991d1fbc6c46 Mon Sep 17 00:00:00 2001 From: Lyss Date: Mon, 15 Jul 2024 14:59:47 +0200 Subject: [PATCH] Add gridlines and lerp --- .prettierrc | 3 +- bun.lockb | Bin 125483 -> 149631 bytes package.json | 5 ++- postcss.config.js | 6 ++++ src/app.css | 3 ++ src/app.html | 4 +-- src/lib/actions/canvas.ts | 64 +++++++++++++++++++++++++++++++++ src/lib/actions/index.ts | 1 + src/lib/utils/index.ts | 2 +- src/lib/utils/math.ts | 3 ++ src/routes/+layout.svelte | 18 ++++++++++ src/routes/canvas/+page.svelte | 43 ++++++++++++++++++++++ tailwind.config.ts | 9 +++++ tsconfig.json | 4 +-- 14 files changed, 158 insertions(+), 7 deletions(-) create mode 100644 postcss.config.js create mode 100644 src/app.css create mode 100644 src/lib/actions/canvas.ts create mode 100644 src/lib/utils/math.ts create mode 100644 src/routes/+layout.svelte create mode 100644 src/routes/canvas/+page.svelte create mode 100644 tailwind.config.ts diff --git a/.prettierrc b/.prettierrc index 9573023..20c211a 100644 --- a/.prettierrc +++ b/.prettierrc @@ -4,5 +4,6 @@ "trailingComma": "none", "printWidth": 100, "plugins": ["prettier-plugin-svelte"], - "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] + "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }], + "bracketSameLine": true } diff --git a/bun.lockb b/bun.lockb index 5e87b52be319ff190d8d230b9fcb3ddd42cf0ae5..f7ea78b58227f7e66c8575ec03e99a6ca6d5b5c8 100755 GIT binary patch delta 33741 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 Q4W7-^-Ldf&Gg9$?0C+86WdHyG delta 19198 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 diff --git a/package.json b/package.json index f165e8e..bd86d69 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@selenite/commons", - "version": "0.1.4", + "version": "0.2.1", "scripts": { "dev": "vite dev", "build": "vite build && npm run package", @@ -36,16 +36,19 @@ "@sveltejs/vite-plugin-svelte": "^3.0.0", "@types/eslint": "^8.56.7", "@types/lodash-es": "^4.17.12", + "autoprefixer": "^10.4.19", "eslint": "^9.0.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-svelte": "^2.36.0", "globals": "^15.0.0", "lodash-es": "^4.17.21", + "postcss": "^8.4.39", "prettier": "^3.1.1", "prettier-plugin-svelte": "^3.1.2", "publint": "^0.1.9", "svelte": "^5.0.0-next.1", "svelte-check": "^3.6.0", + "tailwindcss": "^3.4.4", "tslib": "^2.4.1", "typescript": "^5.0.0", "typescript-eslint": "^8.0.0-alpha.20", diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..2e7af2b --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/src/app.css b/src/app.css new file mode 100644 index 0000000..bd6213e --- /dev/null +++ b/src/app.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; \ No newline at end of file diff --git a/src/app.html b/src/app.html index f22aeaa..de8f7a8 100644 --- a/src/app.html +++ b/src/app.html @@ -6,7 +6,7 @@ %sveltekit.head% - -
%sveltekit.body%
+ +
%sveltekit.body%
diff --git a/src/lib/actions/canvas.ts b/src/lib/actions/canvas.ts new file mode 100644 index 0000000..94b26a1 --- /dev/null +++ b/src/lib/actions/canvas.ts @@ -0,0 +1,64 @@ +import { lerp } from '$lib/utils'; +import type { Action } from 'svelte/action'; + +export type Transform = { x: number; y: number; k: number }; + +function drawLines({ + canvas, + transform, + spacing: baseSpacing = 80 + }: { + canvas: HTMLCanvasElement; + + transform: Transform; + spacing?: number; + }) { + if (transform.k < 0.01) return; + const ctx = canvas.getContext('2d'); + if (!ctx) { + throw new Error('Canvas is not a 2d canvas.'); + } + ctx.save(); + ctx.beginPath(); + const scalingFactor = Math.floor(1 + -Math.log(transform.k) / Math.log(3)); + const spacing = scalingFactor > 0 ? baseSpacing * scalingFactor : baseSpacing; + console.log('spacing\t', spacing); + const step = spacing * transform.k; + console.log('zoom\t', transform.k); + // Draw vertical lines + for (let x = (transform.x % step) - step; x <= canvas.width; x += step) { + ctx.moveTo(x, 0); + ctx.lineTo(x, canvas.height); + } + + // Draw horizontal lines + for (let y = (transform.y % step) - step; y <= canvas.height; y += step) { + ctx.moveTo(0, y); + ctx.lineTo(canvas.width, y); + } + // Stroke the lines + ctx.strokeStyle = '#ddd'; + ctx.lineWidth = transform.k > 0.1 ? 0.08 : lerp(0.08, 0, (transform.k - 0.01) / 0.09); + ctx.stroke(); + ctx.restore(); + } + +export const gridLines: Action = ( + canvas, + params +) => { + const ctx = canvas.getContext('2d'); + if (!ctx) { + throw new Error('Canvas is not a 2d canvas.'); + } + + drawLines({ ...params, canvas }); + + return { + destroy() {}, + update(params) { + ctx.clearRect(0, 0, canvas.width, canvas.height); + drawLines({ canvas, ...params }); + } + }; +}; diff --git a/src/lib/actions/index.ts b/src/lib/actions/index.ts index 00f349a..007994c 100644 --- a/src/lib/actions/index.ts +++ b/src/lib/actions/index.ts @@ -1,6 +1,7 @@ import type { Action } from 'svelte/action'; export * from './focusTrap.js' export * from './shortcut.js' +export * from './canvas.js' let handleFocusLeaveRefCount = 0; let handleFocusLeaveCallbacks: ((isKeyboard: boolean) => void)[] = []; function handleKeydown(e: KeyboardEvent) { diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index 3e63952..d464d98 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -4,7 +4,7 @@ export * from './string.js'; export * from './eventListeners.js'; export * from './array/index.js' - +export * from './math.js' export { v4 as uuidv4 } from 'uuid'; import {v4 as uuid} from 'uuid' let idCount = 0; diff --git a/src/lib/utils/math.ts b/src/lib/utils/math.ts new file mode 100644 index 0000000..b64a4d4 --- /dev/null +++ b/src/lib/utils/math.ts @@ -0,0 +1,3 @@ +export function lerp(start: number, end: number, alpha: number) { + return start + (end - start) * alpha; +} \ No newline at end of file diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte new file mode 100644 index 0000000..093ea88 --- /dev/null +++ b/src/routes/+layout.svelte @@ -0,0 +1,18 @@ + +
+ +
+ + \ No newline at end of file diff --git a/src/routes/canvas/+page.svelte b/src/routes/canvas/+page.svelte new file mode 100644 index 0000000..88bfa48 --- /dev/null +++ b/src/routes/canvas/+page.svelte @@ -0,0 +1,43 @@ + + + + +
+ +
+ + diff --git a/tailwind.config.ts b/tailwind.config.ts new file mode 100644 index 0000000..d4be033 --- /dev/null +++ b/tailwind.config.ts @@ -0,0 +1,9 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ['./src/**/*.{html,js,ts,svelte}'], + theme: { + extend: {} + }, + plugins: [] +}; + diff --git a/tsconfig.json b/tsconfig.json index 6f788f1..815029b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,7 +9,7 @@ "skipLibCheck": true, "sourceMap": true, "strict": true, - "module": "NodeNext", - "moduleResolution": "NodeNext" + "module": "ES2022", + "moduleResolution": "Bundler" } }