Initial commit
This commit is contained in:
commit
df9c300e15
11 changed files with 3096 additions and 0 deletions
28
.gitignore
vendored
Normal file
28
.gitignore
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
|
||||
dist
|
||||
.solid
|
||||
.output
|
||||
.vercel
|
||||
.netlify
|
||||
.vinxi
|
||||
|
||||
# Environment
|
||||
.env
|
||||
.env*.local
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
*.launch
|
||||
.settings/
|
||||
|
||||
# Temp
|
||||
gitignore
|
||||
|
||||
# System Files
|
||||
.DS_Store
|
||||
Thumbs.db
|
36
README.md
Normal file
36
README.md
Normal file
|
@ -0,0 +1,36 @@
|
|||
## Usage
|
||||
|
||||
Those templates dependencies are maintained via [pnpm](https://pnpm.io) via `pnpm up -Lri`.
|
||||
|
||||
This is the reason you see a `pnpm-lock.yaml`. That being said, any package manager will work. This file can be safely be removed once you clone a template.
|
||||
|
||||
```bash
|
||||
$ npm install # or pnpm install or yarn install
|
||||
```
|
||||
|
||||
### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs)
|
||||
|
||||
## Available Scripts
|
||||
|
||||
In the project directory, you can run:
|
||||
|
||||
### `npm run dev` or `npm start`
|
||||
|
||||
Runs the app in the development mode.<br>
|
||||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
||||
|
||||
The page will reload if you make edits.<br>
|
||||
|
||||
### `npm run build`
|
||||
|
||||
Builds the app for production to the `dist` folder.<br>
|
||||
It correctly bundles Solid in production mode and optimizes the build for the best performance.
|
||||
|
||||
The build is minified and the filenames include the hashes.<br>
|
||||
Your app is ready to be deployed!
|
||||
|
||||
## Deployment
|
||||
|
||||
You can deploy the `dist` folder to any static host provider (netlify, surge, now, etc.)
|
||||
|
||||
## This project was created with the [Solid CLI](https://solid-cli.netlify.app)
|
16
index.html
Normal file
16
index.html
Normal file
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<link rel="shortcut icon" type="image/ico" href="/src/assets/favicon.ico" />
|
||||
<title>Solid App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
|
||||
<script src="/src/index.tsx" type="module"></script>
|
||||
</body>
|
||||
</html>
|
24
package.json
Normal file
24
package.json
Normal file
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"name": "vite-template-solid",
|
||||
"version": "0.0.0",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"start": "vite",
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"serve": "vite preview"
|
||||
},
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"solid-devtools": "^0.29.2",
|
||||
"typescript": "^5.3.3",
|
||||
"unocss": "^0.59.0",
|
||||
"vite": "^5.0.11",
|
||||
"vite-plugin-solid": "^2.8.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@solid-primitives/scheduled": "^1.4.3",
|
||||
"solid-js": "^1.8.11"
|
||||
}
|
||||
}
|
2754
pnpm-lock.yaml
generated
Normal file
2754
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load diff
175
src/App.tsx
Normal file
175
src/App.tsx
Normal file
|
@ -0,0 +1,175 @@
|
|||
import { debounce } from "@solid-primitives/scheduled";
|
||||
import { Component, createEffect, createMemo, createSignal } from "solid-js";
|
||||
|
||||
export const App: Component = () => {
|
||||
const [pixelSize, setPixelSize] = createSignal(8);
|
||||
const [canvasRef, setCanvasRef] = createSignal<HTMLCanvasElement>();
|
||||
|
||||
const dataCanvas = document.createElement("canvas");
|
||||
const dataCanvasContext = dataCanvas.getContext("2d");
|
||||
|
||||
const [data, setData] = createSignal<string>();
|
||||
const imageElement = new Image();
|
||||
|
||||
createEffect(() => {
|
||||
const src = data();
|
||||
|
||||
if (src) {
|
||||
imageElement.src = src;
|
||||
}
|
||||
});
|
||||
|
||||
const [imageData, setImageData] = createSignal<ImageData>();
|
||||
|
||||
imageElement.addEventListener("load", () => {
|
||||
const { naturalWidth: width, naturalHeight: height } = imageElement;
|
||||
[dataCanvas.width, dataCanvas.height] = [width, height];
|
||||
|
||||
dataCanvasContext?.drawImage(imageElement, 0, 0);
|
||||
|
||||
setImageData(dataCanvasContext?.getImageData(0, 0, width - 1, height - 1));
|
||||
});
|
||||
|
||||
const context = () => canvasRef()?.getContext("2d");
|
||||
|
||||
const mosaicPixels = createMemo(() => {
|
||||
const pixels = imageData();
|
||||
const ctx = context();
|
||||
const currentPixelSize = pixelSize();
|
||||
|
||||
if (currentPixelSize === 1) {
|
||||
return pixels;
|
||||
}
|
||||
|
||||
if (pixels && ctx) {
|
||||
const mosaicPixelsImageData = ctx.createImageData(
|
||||
pixels.width,
|
||||
pixels.height
|
||||
);
|
||||
|
||||
const lastRowHeight = pixels.height % currentPixelSize;
|
||||
const rows = (pixels.height - lastRowHeight) / currentPixelSize + 1;
|
||||
|
||||
const lastColWidth = pixels.width % currentPixelSize;
|
||||
const cols = (pixels.width - lastColWidth) / currentPixelSize + 1;
|
||||
|
||||
// advance the new rows
|
||||
for (let row = 0; row < rows; row++) {
|
||||
// advance the new cols
|
||||
for (let col = 0; col < cols; col++) {
|
||||
const affectedPixelStartPositions: number[] = [];
|
||||
let redSum = 0,
|
||||
greenSum = 0,
|
||||
blueSum = 0,
|
||||
alphaSum = 0;
|
||||
|
||||
let pixelWidth = currentPixelSize;
|
||||
|
||||
if (col === cols - 1) {
|
||||
pixelWidth = lastColWidth;
|
||||
}
|
||||
|
||||
let pixelHeight = currentPixelSize;
|
||||
|
||||
if (row === rows - 1) {
|
||||
pixelHeight = lastRowHeight;
|
||||
}
|
||||
|
||||
let divider = pixelWidth * pixelHeight;
|
||||
|
||||
// loop though the vertical pixels
|
||||
for (let k = 0; k < pixelWidth * 4; k += 4) {
|
||||
// for each vertical pixels we also get all the pixels we need below it
|
||||
// each pixel consists of 4 values (r, g, b, a)
|
||||
for (let l = 0; l < pixelHeight; l++) {
|
||||
const pixelStartPosition =
|
||||
row * currentPixelSize * (pixels.width * 4) +
|
||||
col * currentPixelSize * 4 +
|
||||
k +
|
||||
l * (pixels.width * 4);
|
||||
|
||||
affectedPixelStartPositions.push(pixelStartPosition);
|
||||
|
||||
redSum += pixels.data[pixelStartPosition];
|
||||
greenSum += pixels.data[pixelStartPosition + 1];
|
||||
blueSum += pixels.data[pixelStartPosition + 2];
|
||||
alphaSum += pixels.data[pixelStartPosition + 3];
|
||||
}
|
||||
}
|
||||
|
||||
const red = redSum / divider;
|
||||
const green = greenSum / divider;
|
||||
const blue = blueSum / divider;
|
||||
const alpha = alphaSum / divider;
|
||||
|
||||
for (const startPosition of affectedPixelStartPositions) {
|
||||
mosaicPixelsImageData.data[startPosition] = red;
|
||||
mosaicPixelsImageData.data[startPosition + 1] = green;
|
||||
mosaicPixelsImageData.data[startPosition + 2] = blue;
|
||||
mosaicPixelsImageData.data[startPosition + 3] = alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mosaicPixelsImageData;
|
||||
}
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
const pixels = mosaicPixels();
|
||||
const ctx = context();
|
||||
|
||||
if (pixels && ctx) {
|
||||
canvasRef()!.width = pixels.width;
|
||||
canvasRef()!.height = pixels.height;
|
||||
|
||||
ctx.putImageData(pixels, 0, 0);
|
||||
}
|
||||
});
|
||||
|
||||
const triggerPixelSizeChange = debounce(
|
||||
(pixelSize: number) => setPixelSize(pixelSize),
|
||||
250
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div class="w-screen h-screen flex flex-col">
|
||||
<form class="m2">
|
||||
<input
|
||||
required
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={(event) => {
|
||||
const file = event.currentTarget.files?.[0];
|
||||
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.addEventListener("load", (event) => {
|
||||
const data = event.target!.result;
|
||||
|
||||
setData(data as string);
|
||||
});
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<input
|
||||
type="range"
|
||||
min="1"
|
||||
max="20"
|
||||
value={pixelSize()}
|
||||
onChange={(event) => {
|
||||
triggerPixelSizeChange(event.currentTarget.valueAsNumber);
|
||||
}}
|
||||
/>
|
||||
</form>
|
||||
<canvas ref={setCanvasRef} class="grow" />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
4
src/index.css
Normal file
4
src/index.css
Normal file
|
@ -0,0 +1,4 @@
|
|||
* {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
17
src/index.tsx
Normal file
17
src/index.tsx
Normal file
|
@ -0,0 +1,17 @@
|
|||
/* @refresh reload */
|
||||
import { render } from "solid-js/web";
|
||||
|
||||
import App from "./App";
|
||||
|
||||
import "./index.css";
|
||||
import "virtual:uno.css";
|
||||
|
||||
const root = document.getElementById("root");
|
||||
|
||||
if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
|
||||
throw new Error(
|
||||
"Root element not found. Did you forget to add it to your index.html? Or maybe the id attribute got misspelled?"
|
||||
);
|
||||
}
|
||||
|
||||
render(() => <App />, root!);
|
15
tsconfig.json
Normal file
15
tsconfig.json
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"jsx": "preserve",
|
||||
"jsxImportSource": "solid-js",
|
||||
"types": ["vite/client"],
|
||||
"noEmit": true,
|
||||
"isolatedModules": true,
|
||||
},
|
||||
}
|
5
uno.config.ts
Normal file
5
uno.config.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { defineConfig, presetUno } from "unocss";
|
||||
|
||||
defineConfig({
|
||||
presets: [presetUno],
|
||||
});
|
22
vite.config.ts
Normal file
22
vite.config.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { defineConfig } from "vite";
|
||||
import solidPlugin from "vite-plugin-solid";
|
||||
// import devtools from 'solid-devtools/vite';
|
||||
import UnoCSS from "unocss/vite";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
/*
|
||||
Uncomment the following line to enable solid-devtools.
|
||||
For more info see https://github.com/thetarnav/solid-devtools/tree/main/packages/extension#readme
|
||||
*/
|
||||
// devtools(),
|
||||
solidPlugin(),
|
||||
UnoCSS(),
|
||||
],
|
||||
server: {
|
||||
port: 3000,
|
||||
},
|
||||
build: {
|
||||
target: "esnext",
|
||||
},
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue