migrated to sveltekit including skeleton
This commit is contained in:
parent
6b0f1726e2
commit
400e27c8f9
13
.eslintignore
Normal file
13
.eslintignore
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
/build
|
||||||
|
/.svelte-kit
|
||||||
|
/package
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
# Ignore files for PNPM, NPM and YARN
|
||||||
|
pnpm-lock.yaml
|
||||||
|
package-lock.json
|
||||||
|
yarn.lock
|
30
.eslintrc.cjs
Normal file
30
.eslintrc.cjs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:svelte/recommended',
|
||||||
|
'prettier'
|
||||||
|
],
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
plugins: ['@typescript-eslint'],
|
||||||
|
parserOptions: {
|
||||||
|
sourceType: 'module',
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
extraFileExtensions: ['.svelte']
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
es2017: true,
|
||||||
|
node: true
|
||||||
|
},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['*.svelte'],
|
||||||
|
parser: 'svelte-eslint-parser',
|
||||||
|
parserOptions: {
|
||||||
|
parser: '@typescript-eslint/parser'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
3336
package-lock.json
generated
3336
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@ -8,16 +8,26 @@
|
|||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||||
"lint": "prettier --plugin-search-dir . --check .",
|
"lint": "prettier --plugin-search-dir . --check . && eslint .",
|
||||||
"format": "prettier --plugin-search-dir . --write ."
|
"format": "prettier --plugin-search-dir . --write ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@floating-ui/dom": "^1.2.7",
|
||||||
|
"@skeletonlabs/skeleton": "^1.5.1",
|
||||||
"@sveltejs/adapter-static": "^2.0.2",
|
"@sveltejs/adapter-static": "^2.0.2",
|
||||||
"@sveltejs/kit": "^1.5.0",
|
"@sveltejs/kit": "^1.5.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
||||||
|
"@typescript-eslint/parser": "^5.45.0",
|
||||||
|
"autoprefixer": "^10.4.14",
|
||||||
|
"eslint": "^8.28.0",
|
||||||
|
"eslint-config-prettier": "^8.5.0",
|
||||||
|
"eslint-plugin-svelte": "^2.26.0",
|
||||||
|
"postcss": "^8.4.23",
|
||||||
"prettier": "^2.8.0",
|
"prettier": "^2.8.0",
|
||||||
"prettier-plugin-svelte": "^2.8.1",
|
"prettier-plugin-svelte": "^2.8.1",
|
||||||
"svelte": "^3.54.0",
|
"svelte": "^3.54.0",
|
||||||
"svelte-check": "^3.0.1",
|
"svelte-check": "^3.0.1",
|
||||||
|
"tailwindcss": "^3.3.2",
|
||||||
"tslib": "^2.4.1",
|
"tslib": "^2.4.1",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^5.0.0",
|
||||||
"vite": "^4.2.0"
|
"vite": "^4.2.0"
|
||||||
|
6
postcss.config.cjs
Normal file
6
postcss.config.cjs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
65
src/app.html
65
src/app.html
@ -1,71 +1,12 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en" class="light">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
<style>
|
|
||||||
.myPreview {
|
|
||||||
overflow: hidden;
|
|
||||||
width: 300px;
|
|
||||||
height: 300px;
|
|
||||||
background: lightgray;
|
|
||||||
}
|
|
||||||
|
|
||||||
.myPreview__image {
|
|
||||||
width: 300px;
|
|
||||||
height: 300px;
|
|
||||||
transform-origin: 0 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body data-sveltekit-preload-data="hover">
|
<body data-sveltekit-preload-data="hover" data-theme="rocket">
|
||||||
<div style="display: contents">%sveltekit.body%</div>
|
<div style="display: contents" class="h-full overflow-hidden">%sveltekit.body%</div>
|
||||||
<div style="display: flex; flex-flow: row">
|
|
||||||
<svg width="300" height="300">
|
|
||||||
<path d="" fill="none" stroke="red" />
|
|
||||||
<circle cx="211" cy="41" r="5" />
|
|
||||||
<circle cx="130" cy="50" r="5" />
|
|
||||||
<circle cx="85" cy="247" r="5" />
|
|
||||||
<circle cx="173" cy="261" r="5" />
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<div class="myPreview" style="display: none">
|
|
||||||
<img class="myPreview__image" src="images/sample.jpg" alt="sample" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<canvas width="300" height="300" class="myCanvas"></canvas>
|
|
||||||
|
|
||||||
<canvas width="300" height="300" id="resultCanvas"></canvas>
|
|
||||||
</div>
|
|
||||||
<script src="https://unpkg.com/perspective-transform@1.1.3/dist/perspective-transform.js"></script>
|
|
||||||
<script src="js/editor.js"></script>
|
|
||||||
<script src="js/preview.js"></script>
|
|
||||||
<script src="js/webgl.js"></script>
|
|
||||||
<script>
|
|
||||||
var download = function(){
|
|
||||||
var link = document.createElement('a');
|
|
||||||
link.download = 'filename.png';
|
|
||||||
link.href = document.getElementById("resultCanvas").toDataURL();
|
|
||||||
link.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
const resultCanvas = document.getElementById("resultCanvas");
|
|
||||||
const rctx = resultCanvas.getContext("2d");
|
|
||||||
|
|
||||||
make_base();
|
|
||||||
|
|
||||||
function make_base() {
|
|
||||||
srcCtx = document.querySelector(".myCanvas");
|
|
||||||
rctx.drawImage(srcCtx, 0, 0);
|
|
||||||
|
|
||||||
base_image = new Image();
|
|
||||||
base_image.src = "images/mockup-test.png";
|
|
||||||
base_image.onload = function() {
|
|
||||||
rctx.drawImage(base_image, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
2
src/app.postcss
Normal file
2
src/app.postcss
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/*place global styles here */
|
||||||
|
html, body { @apply h-full overflow-hidden; }
|
46
src/routes/+layout.svelte
Normal file
46
src/routes/+layout.svelte
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<script lang='ts'>
|
||||||
|
import '@skeletonlabs/skeleton/themes/theme-skeleton.css';
|
||||||
|
import '@skeletonlabs/skeleton/styles/skeleton.css';
|
||||||
|
import '../app.postcss';
|
||||||
|
import { AppShell, AppBar } from '@skeletonlabs/skeleton';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- App Shell -->
|
||||||
|
<AppShell>
|
||||||
|
<svelte:fragment slot="header">
|
||||||
|
<!-- App Bar -->
|
||||||
|
<AppBar>
|
||||||
|
<svelte:fragment slot="lead">
|
||||||
|
<strong class="text-xl">mockup-generator.com</strong>
|
||||||
|
</svelte:fragment>
|
||||||
|
<svelte:fragment slot="trail">
|
||||||
|
<a
|
||||||
|
class="btn btn-sm"
|
||||||
|
href="#"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
Devices
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
class="btn btn-sm"
|
||||||
|
href="#"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
Print
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
class="btn btn-sm"
|
||||||
|
href="#"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
Social Media
|
||||||
|
</a>
|
||||||
|
</svelte:fragment>
|
||||||
|
</AppBar>
|
||||||
|
</svelte:fragment>
|
||||||
|
<!-- Page Route Content -->
|
||||||
|
<slot />
|
||||||
|
</AppShell>
|
@ -0,0 +1,290 @@
|
|||||||
|
<script>
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
|
||||||
|
// webgl.js
|
||||||
|
const canvas = document.querySelector(".myCanvas");
|
||||||
|
const image = document.querySelector(".myPreview__image");
|
||||||
|
const gl = canvas.getContext("webgl", {
|
||||||
|
preserveDrawingBuffer: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const plane = createPlane(gl);
|
||||||
|
const texture = createTexture(gl);
|
||||||
|
const shader = createImageShader(gl);
|
||||||
|
let matrix = [];
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
gl.clearColor(0.0, 0.0, 0.0, 0.0);
|
||||||
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
shader.use().setProjection(matrix).setTexture(texture);
|
||||||
|
plane.draw(shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadTexture() {
|
||||||
|
drawToTexure(gl, texture, image);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
function toMatrix(t) {
|
||||||
|
// prettier-ignore
|
||||||
|
return [
|
||||||
|
t[0], t[1], 0, t[2],
|
||||||
|
t[3], t[4], 0, t[5],
|
||||||
|
0, 0, 1, 0,
|
||||||
|
t[6], t[7], 0, t[8]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (image.naturalWidth && image.naturalHeight) {
|
||||||
|
loadTexture();
|
||||||
|
} else {
|
||||||
|
image.addEventListener("load", () => {
|
||||||
|
loadTexture();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event handlers
|
||||||
|
*/
|
||||||
|
|
||||||
|
document.addEventListener("myPointsChanged", (event) => {
|
||||||
|
const points = event.detail.reduce(
|
||||||
|
(result, { x, y }) => [...result, x / 150 - 1, 1 - y / 150],
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const transform = new PerspT([1, -1, -1, -1, -1, 1, 1, 1], points);
|
||||||
|
matrix = toMatrix(transform.coeffs);
|
||||||
|
|
||||||
|
update();
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Primitives
|
||||||
|
*/
|
||||||
|
|
||||||
|
function createPlane(gl) {
|
||||||
|
const positionBuffer = createBuffer(
|
||||||
|
gl,
|
||||||
|
[1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0]
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
draw(shader) {
|
||||||
|
shader.bindPositionBuffer(positionBuffer);
|
||||||
|
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createImageShader(gl) {
|
||||||
|
const vertexSource = `
|
||||||
|
attribute vec4 aPosition;
|
||||||
|
|
||||||
|
uniform mat4 uProjection;
|
||||||
|
|
||||||
|
varying highp vec2 vTextureCoord;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vTextureCoord = vec2((aPosition.x - 1.0) * 0.5, (aPosition.y - 1.0) * 0.5);
|
||||||
|
|
||||||
|
gl_Position = vec4(aPosition.x, aPosition.y, 0.0, 1.0) * uProjection;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const fragmentSource = `
|
||||||
|
varying highp vec2 vTextureCoord;
|
||||||
|
|
||||||
|
uniform sampler2D uSampler;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_FragColor = texture2D(uSampler, vTextureCoord);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const program = createProgram(
|
||||||
|
gl,
|
||||||
|
createShader(gl, vertexSource, gl.VERTEX_SHADER),
|
||||||
|
createShader(gl, fragmentSource, gl.FRAGMENT_SHADER)
|
||||||
|
);
|
||||||
|
|
||||||
|
const positionAttribute = gl.getAttribLocation(program, "aPosition");
|
||||||
|
const projectionLocation = gl.getUniformLocation(program, "uProjection");
|
||||||
|
const samplerLocation = gl.getUniformLocation(program, "uSampler");
|
||||||
|
|
||||||
|
const instance = {
|
||||||
|
use() {
|
||||||
|
gl.useProgram(program);
|
||||||
|
gl.enableVertexAttribArray(positionAttribute);
|
||||||
|
return instance;
|
||||||
|
},
|
||||||
|
bindPositionBuffer(buffer) {
|
||||||
|
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
||||||
|
gl.vertexAttribPointer(positionAttribute, 2, gl.FLOAT, false, 0, 0);
|
||||||
|
},
|
||||||
|
setProjection(value) {
|
||||||
|
gl.uniformMatrix4fv(projectionLocation, false, value);
|
||||||
|
return instance;
|
||||||
|
},
|
||||||
|
setTexture(value) {
|
||||||
|
gl.activeTexture(gl.TEXTURE0);
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, value);
|
||||||
|
gl.uniform1i(samplerLocation, 0);
|
||||||
|
return instance;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebGL helpers
|
||||||
|
* https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial
|
||||||
|
*/
|
||||||
|
|
||||||
|
function createBuffer(gl, positions) {
|
||||||
|
const positionBuffer = gl.createBuffer();
|
||||||
|
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
||||||
|
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
|
||||||
|
|
||||||
|
return positionBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createProgram(gl, vertexShader, fragmentShader) {
|
||||||
|
const program = gl.createProgram();
|
||||||
|
gl.attachShader(program, vertexShader);
|
||||||
|
gl.attachShader(program, fragmentShader);
|
||||||
|
gl.linkProgram(program);
|
||||||
|
|
||||||
|
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
||||||
|
const info = gl.getProgramInfoLog(program);
|
||||||
|
throw `Could not compile WebGL program. \n\n${info}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createShader(gl, source, type) {
|
||||||
|
const shader = gl.createShader(type);
|
||||||
|
gl.shaderSource(shader, source);
|
||||||
|
gl.compileShader(shader);
|
||||||
|
|
||||||
|
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
||||||
|
const info = gl.getShaderInfoLog(shader);
|
||||||
|
throw `Could not compile WebGL program. \n\n${info}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createTexture(gl) {
|
||||||
|
const texture = gl.createTexture();
|
||||||
|
const pixel = new Uint8Array([0, 0, 255, 255]);
|
||||||
|
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||||
|
gl.texImage2D(
|
||||||
|
gl.TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
gl.RGBA,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
gl.RGBA,
|
||||||
|
gl.UNSIGNED_BYTE,
|
||||||
|
pixel
|
||||||
|
);
|
||||||
|
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawToTexure(gl, texture, source) {
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||||
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
|
||||||
|
|
||||||
|
if (isPowerOf2(source.naturalWidth) && isPowerOf2(source.naturalHeight)) {
|
||||||
|
gl.generateMipmap(gl.TEXTURE_2D);
|
||||||
|
} else {
|
||||||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPowerOf2(value) {
|
||||||
|
return (value & (value - 1)) === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// var download = function(){
|
||||||
|
// var link = document.createElement('a');
|
||||||
|
// link.download = 'filename.png';
|
||||||
|
// link.href = document.getElementById("resultCanvas").toDataURL();
|
||||||
|
// link.click();
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
const points = [
|
||||||
|
{ x: 211, y: 41 },
|
||||||
|
{ x: 130, y: 50 },
|
||||||
|
{ x: 85, y: 247 },
|
||||||
|
{ x: 173, y: 261 }
|
||||||
|
];
|
||||||
|
|
||||||
|
const event = new CustomEvent("myPointsChanged", { detail: points });
|
||||||
|
document.dispatchEvent(event);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// draw result
|
||||||
|
|
||||||
|
const resultCanvas = document.getElementById("resultCanvas");
|
||||||
|
const rctx = resultCanvas.getContext("2d");
|
||||||
|
|
||||||
|
// draw background
|
||||||
|
let srcCtx = document.querySelector(".myCanvas");
|
||||||
|
rctx.drawImage(srcCtx, 0, 0);
|
||||||
|
|
||||||
|
// draw foreground
|
||||||
|
let base_image = new Image();
|
||||||
|
base_image.src = "/images/mockup-test.png";
|
||||||
|
base_image.onload = function() {
|
||||||
|
rctx.drawImage(base_image, 0, 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<script src="https://unpkg.com/perspective-transform@1.1.3/dist/perspective-transform.js"></script>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<div class="container h-full mx-auto flex justify-center items-center">
|
||||||
|
<div style="display: flex; flex-flow: row">
|
||||||
|
|
||||||
|
<div class="myPreview" style="display: none">
|
||||||
|
<img class="myPreview__image" src="/images/sample.jpg" alt="sample" />
|
||||||
|
|
||||||
|
<canvas width="300" height="300" class="myCanvas"></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<canvas width="300" height="300" id="resultCanvas"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="postcss">
|
||||||
|
/* .myPreview {
|
||||||
|
overflow: hidden;
|
||||||
|
width: 300px;
|
||||||
|
height: 300px;
|
||||||
|
background: lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.myPreview__image {
|
||||||
|
width: 300px;
|
||||||
|
height: 300px;
|
||||||
|
transform-origin: 0 0;
|
||||||
|
} */
|
||||||
|
</style>
|
@ -1,73 +0,0 @@
|
|||||||
(function () {
|
|
||||||
const svg = document.querySelector("svg");
|
|
||||||
const path = svg.querySelector("path");
|
|
||||||
const circles = toArray(svg.querySelectorAll("circle"));
|
|
||||||
let draggedCircle = null;
|
|
||||||
|
|
||||||
function update() {
|
|
||||||
const points = circles.map((circle) => ({
|
|
||||||
x: parseFloat(circle.getAttribute("cx")),
|
|
||||||
y: parseFloat(circle.getAttribute("cy")),
|
|
||||||
}));
|
|
||||||
|
|
||||||
path.setAttribute("d", toSvgPath(points));
|
|
||||||
|
|
||||||
const event = new CustomEvent("myPointsChanged", { detail: points });
|
|
||||||
document.dispatchEvent(event);
|
|
||||||
|
|
||||||
make_base();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event handlers
|
|
||||||
*/
|
|
||||||
|
|
||||||
svg.addEventListener("mousedown", (event) => {
|
|
||||||
if (circles.includes(event.target)) {
|
|
||||||
draggedCircle = event.target;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener("mousemove", (event) => {
|
|
||||||
if (draggedCircle) {
|
|
||||||
const { height, left, top, width } = svg.getBoundingClientRect();
|
|
||||||
const x = clamp(event.clientX - left, 0, width);
|
|
||||||
const y = clamp(event.clientY - top, 0, height);
|
|
||||||
|
|
||||||
draggedCircle.setAttribute("cx", x);
|
|
||||||
draggedCircle.setAttribute("cy", y);
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener("mouseup", () => {
|
|
||||||
draggedCircle = null;
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
|
||||||
update();
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helpers
|
|
||||||
*/
|
|
||||||
|
|
||||||
function clamp(value, min, max) {
|
|
||||||
return Math.max(min, Math.min(value, max));
|
|
||||||
}
|
|
||||||
|
|
||||||
function toArray(value) {
|
|
||||||
return Array.prototype.slice.call(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function toSvgOperation(operation, { x, y }) {
|
|
||||||
return `${operation} ${x} ${y}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function toSvgPath(points) {
|
|
||||||
return [
|
|
||||||
toSvgOperation("M", points[points.length - 1]),
|
|
||||||
...points.map((point) => toSvgOperation("L", point)),
|
|
||||||
].join(" ");
|
|
||||||
}
|
|
||||||
})();
|
|
@ -1,31 +0,0 @@
|
|||||||
(function () {
|
|
||||||
const image = document.querySelector(".myPreview__image");
|
|
||||||
|
|
||||||
function toCssMatrix(transform) {
|
|
||||||
return `matrix3d(${toMatrix(transform.coeffs).join(",")})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function toMatrix(t) {
|
|
||||||
// prettier-ignore
|
|
||||||
return [
|
|
||||||
t[0], t[3], 0, t[6],
|
|
||||||
t[1], t[4], 0, t[7],
|
|
||||||
0, 0, 1, 0,
|
|
||||||
t[2], t[5], 0, t[8]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event handlers
|
|
||||||
*/
|
|
||||||
|
|
||||||
document.addEventListener("myPointsChanged", (event) => {
|
|
||||||
const points = event.detail.reduce(
|
|
||||||
(result, { x, y }) => [...result, x, y],
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
const transform = new PerspT([300, 0, 0, 0, 0, 300, 300, 300], points);
|
|
||||||
image.style.transform = toCssMatrix(transform);
|
|
||||||
});
|
|
||||||
})();
|
|
@ -1,214 +0,0 @@
|
|||||||
(function () {
|
|
||||||
const canvas = document.querySelector(".myCanvas");
|
|
||||||
const image = document.querySelector(".myPreview__image");
|
|
||||||
const gl = canvas.getContext("webgl", {
|
|
||||||
preserveDrawingBuffer: true
|
|
||||||
});
|
|
||||||
|
|
||||||
const plane = createPlane(gl);
|
|
||||||
const texture = createTexture(gl);
|
|
||||||
const shader = createImageShader(gl);
|
|
||||||
let matrix = [];
|
|
||||||
|
|
||||||
function update() {
|
|
||||||
gl.clearColor(255.0, 255.0, 255.0, 1.0);
|
|
||||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
||||||
|
|
||||||
shader.use().setProjection(matrix).setTexture(texture);
|
|
||||||
plane.draw(shader);
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadTexture() {
|
|
||||||
drawToTexure(gl, texture, image);
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
function toMatrix(t) {
|
|
||||||
// prettier-ignore
|
|
||||||
return [
|
|
||||||
t[0], t[1], 0, t[2],
|
|
||||||
t[3], t[4], 0, t[5],
|
|
||||||
0, 0, 1, 0,
|
|
||||||
t[6], t[7], 0, t[8]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (image.naturalWidth && image.naturalHeight) {
|
|
||||||
loadTexture();
|
|
||||||
} else {
|
|
||||||
image.addEventListener("load", () => {
|
|
||||||
loadTexture();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event handlers
|
|
||||||
*/
|
|
||||||
|
|
||||||
document.addEventListener("myPointsChanged", (event) => {
|
|
||||||
const points = event.detail.reduce(
|
|
||||||
(result, { x, y }) => [...result, x / 150 - 1, 1 - y / 150],
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
const transform = new PerspT([1, -1, -1, -1, -1, 1, 1, 1], points);
|
|
||||||
matrix = toMatrix(transform.coeffs);
|
|
||||||
|
|
||||||
update();
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Primitives
|
|
||||||
*/
|
|
||||||
|
|
||||||
function createPlane(gl) {
|
|
||||||
const positionBuffer = createBuffer(
|
|
||||||
gl,
|
|
||||||
[1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0]
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
draw(shader) {
|
|
||||||
shader.bindPositionBuffer(positionBuffer);
|
|
||||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createImageShader(gl) {
|
|
||||||
const vertexSource = `
|
|
||||||
attribute vec4 aPosition;
|
|
||||||
|
|
||||||
uniform mat4 uProjection;
|
|
||||||
|
|
||||||
varying highp vec2 vTextureCoord;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vTextureCoord = vec2((aPosition.x - 1.0) * 0.5, (aPosition.y - 1.0) * 0.5);
|
|
||||||
|
|
||||||
gl_Position = vec4(aPosition.x, aPosition.y, 0.0, 1.0) * uProjection;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const fragmentSource = `
|
|
||||||
varying highp vec2 vTextureCoord;
|
|
||||||
|
|
||||||
uniform sampler2D uSampler;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_FragColor = texture2D(uSampler, vTextureCoord);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const program = createProgram(
|
|
||||||
gl,
|
|
||||||
createShader(gl, vertexSource, gl.VERTEX_SHADER),
|
|
||||||
createShader(gl, fragmentSource, gl.FRAGMENT_SHADER)
|
|
||||||
);
|
|
||||||
|
|
||||||
const positionAttribute = gl.getAttribLocation(program, "aPosition");
|
|
||||||
const projectionLocation = gl.getUniformLocation(program, "uProjection");
|
|
||||||
const samplerLocation = gl.getUniformLocation(program, "uSampler");
|
|
||||||
|
|
||||||
const instance = {
|
|
||||||
use() {
|
|
||||||
gl.useProgram(program);
|
|
||||||
gl.enableVertexAttribArray(positionAttribute);
|
|
||||||
return instance;
|
|
||||||
},
|
|
||||||
bindPositionBuffer(buffer) {
|
|
||||||
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
||||||
gl.vertexAttribPointer(positionAttribute, 2, gl.FLOAT, false, 0, 0);
|
|
||||||
},
|
|
||||||
setProjection(value) {
|
|
||||||
gl.uniformMatrix4fv(projectionLocation, false, value);
|
|
||||||
return instance;
|
|
||||||
},
|
|
||||||
setTexture(value) {
|
|
||||||
gl.activeTexture(gl.TEXTURE0);
|
|
||||||
gl.bindTexture(gl.TEXTURE_2D, value);
|
|
||||||
gl.uniform1i(samplerLocation, 0);
|
|
||||||
return instance;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* WebGL helpers
|
|
||||||
* https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial
|
|
||||||
*/
|
|
||||||
|
|
||||||
function createBuffer(gl, positions) {
|
|
||||||
const positionBuffer = gl.createBuffer();
|
|
||||||
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
|
||||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
|
|
||||||
|
|
||||||
return positionBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createProgram(gl, vertexShader, fragmentShader) {
|
|
||||||
const program = gl.createProgram();
|
|
||||||
gl.attachShader(program, vertexShader);
|
|
||||||
gl.attachShader(program, fragmentShader);
|
|
||||||
gl.linkProgram(program);
|
|
||||||
|
|
||||||
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
||||||
const info = gl.getProgramInfoLog(program);
|
|
||||||
throw `Could not compile WebGL program. \n\n${info}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return program;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createShader(gl, source, type) {
|
|
||||||
const shader = gl.createShader(type);
|
|
||||||
gl.shaderSource(shader, source);
|
|
||||||
gl.compileShader(shader);
|
|
||||||
|
|
||||||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
||||||
const info = gl.getShaderInfoLog(shader);
|
|
||||||
throw `Could not compile WebGL program. \n\n${info}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createTexture(gl) {
|
|
||||||
const texture = gl.createTexture();
|
|
||||||
const pixel = new Uint8Array([0, 0, 255, 255]);
|
|
||||||
|
|
||||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
||||||
gl.texImage2D(
|
|
||||||
gl.TEXTURE_2D,
|
|
||||||
0,
|
|
||||||
gl.RGBA,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
gl.RGBA,
|
|
||||||
gl.UNSIGNED_BYTE,
|
|
||||||
pixel
|
|
||||||
);
|
|
||||||
|
|
||||||
return texture;
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawToTexure(gl, texture, source) {
|
|
||||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
||||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
|
|
||||||
|
|
||||||
if (isPowerOf2(source.naturalWidth) && isPowerOf2(source.naturalHeight)) {
|
|
||||||
gl.generateMipmap(gl.TEXTURE_2D);
|
|
||||||
} else {
|
|
||||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
||||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
||||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isPowerOf2(value) {
|
|
||||||
return (value & (value - 1)) === 0;
|
|
||||||
}
|
|
||||||
})();
|
|
9
tailwind.config.cjs
Normal file
9
tailwind.config.cjs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
darkMode: 'class',
|
||||||
|
content: ['./src/**/*.{html,js,svelte,ts}', require('path').join(require.resolve('@skeletonlabs/skeleton'), '../**/*.{html,js,svelte,ts}')],
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: [...require('@skeletonlabs/skeleton/tailwind/skeleton.cjs')()],
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user