mirror of
https://github.com/DavidHDev/vue-bits.git
synced 2026-03-07 06:29:30 -07:00
Format
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import code from '@content/Animations/MetallicPaint/MetallicPaint.vue?raw'
|
||||
import utility from '@content/Animations/MetallicPaint/parseImage.ts?raw'
|
||||
import type { CodeObject } from '../../../types/code'
|
||||
import code from '@content/Animations/MetallicPaint/MetallicPaint.vue?raw';
|
||||
import utility from '@content/Animations/MetallicPaint/parseImage.ts?raw';
|
||||
import type { CodeObject } from '../../../types/code';
|
||||
|
||||
export const metallicPaint: CodeObject = {
|
||||
cli: `npx jsrepo add https://vue-bits.dev/ui/Animations/MetallicPaint`,
|
||||
@@ -44,4 +44,4 @@ import { ref, onMounted } from 'vue';
|
||||
</script>`,
|
||||
code,
|
||||
utility
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import code from "@content/Backgrounds/GridMotion/GridMotion.vue?raw";
|
||||
import type { CodeObject } from "../../../types/code";
|
||||
import code from '@content/Backgrounds/GridMotion/GridMotion.vue?raw';
|
||||
import type { CodeObject } from '../../../types/code';
|
||||
|
||||
export const gridMotion: CodeObject = {
|
||||
cli: `npx jsrepo add https://vue-bits.dev/ui/Backgrounds/GridMotion`,
|
||||
@@ -17,5 +17,5 @@ export const gridMotion: CodeObject = {
|
||||
const numberOfImages = 30;
|
||||
const images = Array.from({ length: numberOfImages }, () => imageUrl);
|
||||
</script>`,
|
||||
code,
|
||||
code
|
||||
};
|
||||
|
||||
@@ -3,20 +3,20 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue'
|
||||
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue';
|
||||
|
||||
interface ShaderParams {
|
||||
patternScale: number
|
||||
refraction: number
|
||||
edge: number
|
||||
patternBlur: number
|
||||
liquid: number
|
||||
speed: number
|
||||
patternScale: number;
|
||||
refraction: number;
|
||||
edge: number;
|
||||
patternBlur: number;
|
||||
liquid: number;
|
||||
speed: number;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
imageData: ImageData
|
||||
params?: ShaderParams
|
||||
imageData: ImageData;
|
||||
params?: ShaderParams;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
@@ -28,14 +28,14 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
liquid: 0.07,
|
||||
speed: 0.3
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
const canvasRef = ref<HTMLCanvasElement | null>(null)
|
||||
const gl = ref<WebGL2RenderingContext | null>(null)
|
||||
const uniforms = ref<Record<string, WebGLUniformLocation>>({})
|
||||
const totalAnimationTime = ref(0)
|
||||
const lastRenderTime = ref(0)
|
||||
const animationId = ref<number>()
|
||||
const canvasRef = ref<HTMLCanvasElement | null>(null);
|
||||
const gl = ref<WebGL2RenderingContext | null>(null);
|
||||
const uniforms = ref<Record<string, WebGLUniformLocation>>({});
|
||||
const totalAnimationTime = ref(0);
|
||||
const lastRenderTime = ref(0);
|
||||
const animationId = ref<number>();
|
||||
|
||||
const vertexShaderSource = `#version 300 es
|
||||
precision mediump float;
|
||||
@@ -46,7 +46,7 @@ out vec2 vUv;
|
||||
void main() {
|
||||
vUv = .5 * (a_position + 1.);
|
||||
gl_Position = vec4(a_position, 0.0, 1.0);
|
||||
}`
|
||||
}`;
|
||||
|
||||
const liquidFragSource = `#version 300 es
|
||||
precision mediump float;
|
||||
@@ -200,123 +200,123 @@ void main() {
|
||||
color *= opacity;
|
||||
fragColor = vec4(color, opacity);
|
||||
}
|
||||
`
|
||||
`;
|
||||
|
||||
function updateUniforms() {
|
||||
if (!gl.value || !uniforms.value) return
|
||||
gl.value.uniform1f(uniforms.value.u_edge, props.params.edge)
|
||||
gl.value.uniform1f(uniforms.value.u_patternBlur, props.params.patternBlur)
|
||||
gl.value.uniform1f(uniforms.value.u_time, 0)
|
||||
gl.value.uniform1f(uniforms.value.u_patternScale, props.params.patternScale)
|
||||
gl.value.uniform1f(uniforms.value.u_refraction, props.params.refraction)
|
||||
gl.value.uniform1f(uniforms.value.u_liquid, props.params.liquid)
|
||||
if (!gl.value || !uniforms.value) return;
|
||||
gl.value.uniform1f(uniforms.value.u_edge, props.params.edge);
|
||||
gl.value.uniform1f(uniforms.value.u_patternBlur, props.params.patternBlur);
|
||||
gl.value.uniform1f(uniforms.value.u_time, 0);
|
||||
gl.value.uniform1f(uniforms.value.u_patternScale, props.params.patternScale);
|
||||
gl.value.uniform1f(uniforms.value.u_refraction, props.params.refraction);
|
||||
gl.value.uniform1f(uniforms.value.u_liquid, props.params.liquid);
|
||||
}
|
||||
|
||||
function createShader(gl: WebGL2RenderingContext, sourceCode: string, type: number) {
|
||||
const shader = gl.createShader(type)
|
||||
const shader = gl.createShader(type);
|
||||
if (!shader) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
gl.shaderSource(shader, sourceCode)
|
||||
gl.compileShader(shader)
|
||||
gl.shaderSource(shader, sourceCode);
|
||||
gl.compileShader(shader);
|
||||
|
||||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
||||
console.error('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader))
|
||||
gl.deleteShader(shader)
|
||||
return null
|
||||
console.error('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
|
||||
gl.deleteShader(shader);
|
||||
return null;
|
||||
}
|
||||
|
||||
return shader
|
||||
return shader;
|
||||
}
|
||||
|
||||
function getUniforms(program: WebGLProgram, gl: WebGL2RenderingContext) {
|
||||
const uniformsObj: Record<string, WebGLUniformLocation> = {}
|
||||
const uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS)
|
||||
const uniformsObj: Record<string, WebGLUniformLocation> = {};
|
||||
const uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
|
||||
for (let i = 0; i < uniformCount; i++) {
|
||||
const uniformName = gl.getActiveUniform(program, i)?.name
|
||||
if (!uniformName) continue
|
||||
uniformsObj[uniformName] = gl.getUniformLocation(program, uniformName) as WebGLUniformLocation
|
||||
const uniformName = gl.getActiveUniform(program, i)?.name;
|
||||
if (!uniformName) continue;
|
||||
uniformsObj[uniformName] = gl.getUniformLocation(program, uniformName) as WebGLUniformLocation;
|
||||
}
|
||||
return uniformsObj
|
||||
return uniformsObj;
|
||||
}
|
||||
|
||||
function initShader() {
|
||||
const canvas = canvasRef.value
|
||||
const canvas = canvasRef.value;
|
||||
const glContext = canvas?.getContext('webgl2', {
|
||||
antialias: true,
|
||||
alpha: true
|
||||
})
|
||||
});
|
||||
if (!canvas || !glContext) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
const vertexShader = createShader(glContext, vertexShaderSource, glContext.VERTEX_SHADER)
|
||||
const fragmentShader = createShader(glContext, liquidFragSource, glContext.FRAGMENT_SHADER)
|
||||
const program = glContext.createProgram()
|
||||
const vertexShader = createShader(glContext, vertexShaderSource, glContext.VERTEX_SHADER);
|
||||
const fragmentShader = createShader(glContext, liquidFragSource, glContext.FRAGMENT_SHADER);
|
||||
const program = glContext.createProgram();
|
||||
if (!program || !vertexShader || !fragmentShader) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
glContext.attachShader(program, vertexShader)
|
||||
glContext.attachShader(program, fragmentShader)
|
||||
glContext.linkProgram(program)
|
||||
glContext.attachShader(program, vertexShader);
|
||||
glContext.attachShader(program, fragmentShader);
|
||||
glContext.linkProgram(program);
|
||||
|
||||
if (!glContext.getProgramParameter(program, glContext.LINK_STATUS)) {
|
||||
console.error('Unable to initialize the shader program: ' + glContext.getProgramInfoLog(program))
|
||||
return null
|
||||
console.error('Unable to initialize the shader program: ' + glContext.getProgramInfoLog(program));
|
||||
return null;
|
||||
}
|
||||
|
||||
const uniformsObj = getUniforms(program, glContext)
|
||||
uniforms.value = uniformsObj
|
||||
const uniformsObj = getUniforms(program, glContext);
|
||||
uniforms.value = uniformsObj;
|
||||
|
||||
const vertices = new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1])
|
||||
const vertexBuffer = glContext.createBuffer()
|
||||
glContext.bindBuffer(glContext.ARRAY_BUFFER, vertexBuffer)
|
||||
glContext.bufferData(glContext.ARRAY_BUFFER, vertices, glContext.STATIC_DRAW)
|
||||
const vertices = new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]);
|
||||
const vertexBuffer = glContext.createBuffer();
|
||||
glContext.bindBuffer(glContext.ARRAY_BUFFER, vertexBuffer);
|
||||
glContext.bufferData(glContext.ARRAY_BUFFER, vertices, glContext.STATIC_DRAW);
|
||||
|
||||
glContext.useProgram(program)
|
||||
glContext.useProgram(program);
|
||||
|
||||
const positionLocation = glContext.getAttribLocation(program, 'a_position')
|
||||
glContext.enableVertexAttribArray(positionLocation)
|
||||
const positionLocation = glContext.getAttribLocation(program, 'a_position');
|
||||
glContext.enableVertexAttribArray(positionLocation);
|
||||
|
||||
glContext.bindBuffer(glContext.ARRAY_BUFFER, vertexBuffer)
|
||||
glContext.vertexAttribPointer(positionLocation, 2, glContext.FLOAT, false, 0, 0)
|
||||
glContext.bindBuffer(glContext.ARRAY_BUFFER, vertexBuffer);
|
||||
glContext.vertexAttribPointer(positionLocation, 2, glContext.FLOAT, false, 0, 0);
|
||||
|
||||
gl.value = glContext
|
||||
gl.value = glContext;
|
||||
}
|
||||
|
||||
function resizeCanvas() {
|
||||
if (!canvasRef.value || !gl.value || !uniforms.value || !props.imageData) return
|
||||
const imgRatio = props.imageData.width / props.imageData.height
|
||||
gl.value.uniform1f(uniforms.value.u_img_ratio, imgRatio)
|
||||
if (!canvasRef.value || !gl.value || !uniforms.value || !props.imageData) return;
|
||||
const imgRatio = props.imageData.width / props.imageData.height;
|
||||
gl.value.uniform1f(uniforms.value.u_img_ratio, imgRatio);
|
||||
|
||||
const side = 1000
|
||||
canvasRef.value.width = side * devicePixelRatio
|
||||
canvasRef.value.height = side * devicePixelRatio
|
||||
gl.value.viewport(0, 0, canvasRef.value.height, canvasRef.value.height)
|
||||
gl.value.uniform1f(uniforms.value.u_ratio, 1)
|
||||
gl.value.uniform1f(uniforms.value.u_img_ratio, imgRatio)
|
||||
const side = 1000;
|
||||
canvasRef.value.width = side * devicePixelRatio;
|
||||
canvasRef.value.height = side * devicePixelRatio;
|
||||
gl.value.viewport(0, 0, canvasRef.value.height, canvasRef.value.height);
|
||||
gl.value.uniform1f(uniforms.value.u_ratio, 1);
|
||||
gl.value.uniform1f(uniforms.value.u_img_ratio, imgRatio);
|
||||
}
|
||||
|
||||
function setupTexture() {
|
||||
if (!gl.value || !uniforms.value) return
|
||||
if (!gl.value || !uniforms.value) return;
|
||||
|
||||
const existingTexture = gl.value.getParameter(gl.value.TEXTURE_BINDING_2D)
|
||||
const existingTexture = gl.value.getParameter(gl.value.TEXTURE_BINDING_2D);
|
||||
if (existingTexture) {
|
||||
gl.value.deleteTexture(existingTexture)
|
||||
gl.value.deleteTexture(existingTexture);
|
||||
}
|
||||
|
||||
const imageTexture = gl.value.createTexture()
|
||||
gl.value.activeTexture(gl.value.TEXTURE0)
|
||||
gl.value.bindTexture(gl.value.TEXTURE_2D, imageTexture)
|
||||
const imageTexture = gl.value.createTexture();
|
||||
gl.value.activeTexture(gl.value.TEXTURE0);
|
||||
gl.value.bindTexture(gl.value.TEXTURE_2D, imageTexture);
|
||||
|
||||
gl.value.texParameteri(gl.value.TEXTURE_2D, gl.value.TEXTURE_MIN_FILTER, gl.value.LINEAR)
|
||||
gl.value.texParameteri(gl.value.TEXTURE_2D, gl.value.TEXTURE_MAG_FILTER, gl.value.LINEAR)
|
||||
gl.value.texParameteri(gl.value.TEXTURE_2D, gl.value.TEXTURE_WRAP_S, gl.value.CLAMP_TO_EDGE)
|
||||
gl.value.texParameteri(gl.value.TEXTURE_2D, gl.value.TEXTURE_WRAP_T, gl.value.CLAMP_TO_EDGE)
|
||||
gl.value.texParameteri(gl.value.TEXTURE_2D, gl.value.TEXTURE_MIN_FILTER, gl.value.LINEAR);
|
||||
gl.value.texParameteri(gl.value.TEXTURE_2D, gl.value.TEXTURE_MAG_FILTER, gl.value.LINEAR);
|
||||
gl.value.texParameteri(gl.value.TEXTURE_2D, gl.value.TEXTURE_WRAP_S, gl.value.CLAMP_TO_EDGE);
|
||||
gl.value.texParameteri(gl.value.TEXTURE_2D, gl.value.TEXTURE_WRAP_T, gl.value.CLAMP_TO_EDGE);
|
||||
|
||||
gl.value.pixelStorei(gl.value.UNPACK_ALIGNMENT, 1)
|
||||
gl.value.pixelStorei(gl.value.UNPACK_ALIGNMENT, 1);
|
||||
|
||||
try {
|
||||
gl.value.texImage2D(
|
||||
@@ -329,58 +329,66 @@ function setupTexture() {
|
||||
gl.value.RGBA,
|
||||
gl.value.UNSIGNED_BYTE,
|
||||
props.imageData?.data
|
||||
)
|
||||
);
|
||||
|
||||
gl.value.uniform1i(uniforms.value.u_image_texture, 0)
|
||||
gl.value.uniform1i(uniforms.value.u_image_texture, 0);
|
||||
} catch (e) {
|
||||
console.error('Error uploading texture:', e)
|
||||
console.error('Error uploading texture:', e);
|
||||
}
|
||||
}
|
||||
|
||||
function render(currentTime: number) {
|
||||
if (!gl.value || !uniforms.value) return
|
||||
if (!gl.value || !uniforms.value) return;
|
||||
|
||||
const deltaTime = currentTime - lastRenderTime.value
|
||||
lastRenderTime.value = currentTime
|
||||
const deltaTime = currentTime - lastRenderTime.value;
|
||||
lastRenderTime.value = currentTime;
|
||||
|
||||
totalAnimationTime.value += deltaTime * props.params.speed
|
||||
gl.value.uniform1f(uniforms.value.u_time, totalAnimationTime.value)
|
||||
gl.value.drawArrays(gl.value.TRIANGLE_STRIP, 0, 4)
|
||||
animationId.value = requestAnimationFrame(render)
|
||||
totalAnimationTime.value += deltaTime * props.params.speed;
|
||||
gl.value.uniform1f(uniforms.value.u_time, totalAnimationTime.value);
|
||||
gl.value.drawArrays(gl.value.TRIANGLE_STRIP, 0, 4);
|
||||
animationId.value = requestAnimationFrame(render);
|
||||
}
|
||||
|
||||
function startAnimation() {
|
||||
if (animationId.value) {
|
||||
cancelAnimationFrame(animationId.value)
|
||||
cancelAnimationFrame(animationId.value);
|
||||
}
|
||||
lastRenderTime.value = performance.now()
|
||||
animationId.value = requestAnimationFrame(render)
|
||||
lastRenderTime.value = performance.now();
|
||||
animationId.value = requestAnimationFrame(render);
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await nextTick()
|
||||
initShader()
|
||||
updateUniforms()
|
||||
resizeCanvas()
|
||||
setupTexture()
|
||||
startAnimation()
|
||||
|
||||
window.addEventListener('resize', resizeCanvas)
|
||||
})
|
||||
await nextTick();
|
||||
initShader();
|
||||
updateUniforms();
|
||||
resizeCanvas();
|
||||
setupTexture();
|
||||
startAnimation();
|
||||
|
||||
window.addEventListener('resize', resizeCanvas);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (animationId.value) {
|
||||
cancelAnimationFrame(animationId.value)
|
||||
cancelAnimationFrame(animationId.value);
|
||||
}
|
||||
window.removeEventListener('resize', resizeCanvas)
|
||||
})
|
||||
window.removeEventListener('resize', resizeCanvas);
|
||||
});
|
||||
|
||||
watch(() => props.params, () => {
|
||||
updateUniforms()
|
||||
}, { deep: true })
|
||||
watch(
|
||||
() => props.params,
|
||||
() => {
|
||||
updateUniforms();
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
watch(() => props.imageData, () => {
|
||||
setupTexture()
|
||||
resizeCanvas()
|
||||
}, { deep: true })
|
||||
watch(
|
||||
() => props.imageData,
|
||||
() => {
|
||||
setupTexture();
|
||||
resizeCanvas();
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
@@ -1,97 +1,97 @@
|
||||
export function parseImage(file: File): Promise<{ imageData: ImageData; pngBlob: Blob }> {
|
||||
const canvas = document.createElement('canvas')
|
||||
const ctx = canvas.getContext('2d')
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!file || !ctx) {
|
||||
reject(new Error('Invalid file or context'))
|
||||
return
|
||||
reject(new Error('Invalid file or context'));
|
||||
return;
|
||||
}
|
||||
|
||||
const img = new Image()
|
||||
img.crossOrigin = 'anonymous'
|
||||
const img = new Image();
|
||||
img.crossOrigin = 'anonymous';
|
||||
img.onload = function () {
|
||||
if (file.type === 'image/svg+xml') {
|
||||
img.width = 1000
|
||||
img.height = 1000
|
||||
img.width = 1000;
|
||||
img.height = 1000;
|
||||
}
|
||||
|
||||
const MAX_SIZE = 1000
|
||||
const MIN_SIZE = 500
|
||||
let width = img.naturalWidth
|
||||
let height = img.naturalHeight
|
||||
const MAX_SIZE = 1000;
|
||||
const MIN_SIZE = 500;
|
||||
let width = img.naturalWidth;
|
||||
let height = img.naturalHeight;
|
||||
|
||||
if (width > MAX_SIZE || height > MAX_SIZE || width < MIN_SIZE || height < MIN_SIZE) {
|
||||
if (width > height) {
|
||||
if (width > MAX_SIZE) {
|
||||
height = Math.round((height * MAX_SIZE) / width)
|
||||
width = MAX_SIZE
|
||||
height = Math.round((height * MAX_SIZE) / width);
|
||||
width = MAX_SIZE;
|
||||
} else if (width < MIN_SIZE) {
|
||||
height = Math.round((height * MIN_SIZE) / width)
|
||||
width = MIN_SIZE
|
||||
height = Math.round((height * MIN_SIZE) / width);
|
||||
width = MIN_SIZE;
|
||||
}
|
||||
} else {
|
||||
if (height > MAX_SIZE) {
|
||||
width = Math.round((width * MAX_SIZE) / height)
|
||||
height = MAX_SIZE
|
||||
width = Math.round((width * MAX_SIZE) / height);
|
||||
height = MAX_SIZE;
|
||||
} else if (height < MIN_SIZE) {
|
||||
width = Math.round((width * MIN_SIZE) / height)
|
||||
height = MIN_SIZE
|
||||
width = Math.round((width * MIN_SIZE) / height);
|
||||
height = MIN_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
canvas.width = width
|
||||
canvas.height = height
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
const shapeCanvas = document.createElement('canvas')
|
||||
shapeCanvas.width = width
|
||||
shapeCanvas.height = height
|
||||
const shapeCtx = shapeCanvas.getContext('2d')!
|
||||
shapeCtx.drawImage(img, 0, 0, width, height)
|
||||
const shapeCanvas = document.createElement('canvas');
|
||||
shapeCanvas.width = width;
|
||||
shapeCanvas.height = height;
|
||||
const shapeCtx = shapeCanvas.getContext('2d')!;
|
||||
shapeCtx.drawImage(img, 0, 0, width, height);
|
||||
|
||||
const shapeImageData = shapeCtx.getImageData(0, 0, width, height)
|
||||
const data = shapeImageData.data
|
||||
const shapeMask = new Array(width * height).fill(false)
|
||||
const shapeImageData = shapeCtx.getImageData(0, 0, width, height);
|
||||
const data = shapeImageData.data;
|
||||
const shapeMask = new Array(width * height).fill(false);
|
||||
for (let y = 0; y < height; y++) {
|
||||
for (let x = 0; x < width; x++) {
|
||||
const idx4 = (y * width + x) * 4
|
||||
const r = data[idx4]
|
||||
const g = data[idx4 + 1]
|
||||
const b = data[idx4 + 2]
|
||||
const a = data[idx4 + 3]
|
||||
shapeMask[y * width + x] = !((r === 255 && g === 255 && b === 255 && a === 255) || a === 0)
|
||||
const idx4 = (y * width + x) * 4;
|
||||
const r = data[idx4];
|
||||
const g = data[idx4 + 1];
|
||||
const b = data[idx4 + 2];
|
||||
const a = data[idx4 + 3];
|
||||
shapeMask[y * width + x] = !((r === 255 && g === 255 && b === 255 && a === 255) || a === 0);
|
||||
}
|
||||
}
|
||||
|
||||
function inside(x: number, y: number) {
|
||||
if (x < 0 || x >= width || y < 0 || y >= height) return false
|
||||
return shapeMask[y * width + x]
|
||||
if (x < 0 || x >= width || y < 0 || y >= height) return false;
|
||||
return shapeMask[y * width + x];
|
||||
}
|
||||
|
||||
const boundaryMask = new Array(width * height).fill(false)
|
||||
const boundaryMask = new Array(width * height).fill(false);
|
||||
for (let y = 0; y < height; y++) {
|
||||
for (let x = 0; x < width; x++) {
|
||||
const idx = y * width + x
|
||||
if (!shapeMask[idx]) continue
|
||||
let isBoundary = false
|
||||
const idx = y * width + x;
|
||||
if (!shapeMask[idx]) continue;
|
||||
let isBoundary = false;
|
||||
for (let ny = y - 1; ny <= y + 1 && !isBoundary; ny++) {
|
||||
for (let nx = x - 1; nx <= x + 1 && !isBoundary; nx++) {
|
||||
if (!inside(nx, ny)) {
|
||||
isBoundary = true
|
||||
isBoundary = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isBoundary) {
|
||||
boundaryMask[idx] = true
|
||||
boundaryMask[idx] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const interiorMask = new Array(width * height).fill(false)
|
||||
const interiorMask = new Array(width * height).fill(false);
|
||||
for (let y = 1; y < height - 1; y++) {
|
||||
for (let x = 1; x < width - 1; x++) {
|
||||
const idx = y * width + x
|
||||
const idx = y * width + x;
|
||||
if (
|
||||
shapeMask[idx] &&
|
||||
shapeMask[idx - 1] &&
|
||||
@@ -99,82 +99,79 @@ export function parseImage(file: File): Promise<{ imageData: ImageData; pngBlob:
|
||||
shapeMask[idx - width] &&
|
||||
shapeMask[idx + width]
|
||||
) {
|
||||
interiorMask[idx] = true
|
||||
interiorMask[idx] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const u = new Float32Array(width * height).fill(0)
|
||||
const newU = new Float32Array(width * height).fill(0)
|
||||
const C = 0.01
|
||||
const ITERATIONS = 300
|
||||
const u = new Float32Array(width * height).fill(0);
|
||||
const newU = new Float32Array(width * height).fill(0);
|
||||
const C = 0.01;
|
||||
const ITERATIONS = 300;
|
||||
|
||||
function getU(x: number, y: number, arr: Float32Array) {
|
||||
if (x < 0 || x >= width || y < 0 || y >= height) return 0
|
||||
if (!shapeMask[y * width + x]) return 0
|
||||
return arr[y * width + x]
|
||||
if (x < 0 || x >= width || y < 0 || y >= height) return 0;
|
||||
if (!shapeMask[y * width + x]) return 0;
|
||||
return arr[y * width + x];
|
||||
}
|
||||
|
||||
for (let iter = 0; iter < ITERATIONS; iter++) {
|
||||
for (let y = 0; y < height; y++) {
|
||||
for (let x = 0; x < width; x++) {
|
||||
const idx = y * width + x
|
||||
const idx = y * width + x;
|
||||
if (!shapeMask[idx] || boundaryMask[idx]) {
|
||||
newU[idx] = 0
|
||||
continue
|
||||
newU[idx] = 0;
|
||||
continue;
|
||||
}
|
||||
const sumN = getU(x + 1, y, u) + getU(x - 1, y, u) + getU(x, y + 1, u) + getU(x, y - 1, u)
|
||||
newU[idx] = (C + sumN) / 4
|
||||
const sumN = getU(x + 1, y, u) + getU(x - 1, y, u) + getU(x, y + 1, u) + getU(x, y - 1, u);
|
||||
newU[idx] = (C + sumN) / 4;
|
||||
}
|
||||
}
|
||||
u.set(newU)
|
||||
u.set(newU);
|
||||
}
|
||||
|
||||
let maxVal = 0
|
||||
let maxVal = 0;
|
||||
for (let i = 0; i < width * height; i++) {
|
||||
if (u[i] > maxVal) maxVal = u[i]
|
||||
if (u[i] > maxVal) maxVal = u[i];
|
||||
}
|
||||
const alpha = 2.0
|
||||
const outImg = ctx.createImageData(width, height)
|
||||
const alpha = 2.0;
|
||||
const outImg = ctx.createImageData(width, height);
|
||||
|
||||
for (let y = 0; y < height; y++) {
|
||||
for (let x = 0; x < width; x++) {
|
||||
const idx = y * width + x
|
||||
const px = idx * 4
|
||||
const idx = y * width + x;
|
||||
const px = idx * 4;
|
||||
if (!shapeMask[idx]) {
|
||||
outImg.data[px] = 255
|
||||
outImg.data[px + 1] = 255
|
||||
outImg.data[px + 2] = 255
|
||||
outImg.data[px + 3] = 255
|
||||
outImg.data[px] = 255;
|
||||
outImg.data[px + 1] = 255;
|
||||
outImg.data[px + 2] = 255;
|
||||
outImg.data[px + 3] = 255;
|
||||
} else {
|
||||
const raw = u[idx] / maxVal
|
||||
const remapped = Math.pow(raw, alpha)
|
||||
const gray = 255 * (1 - remapped)
|
||||
outImg.data[px] = gray
|
||||
outImg.data[px + 1] = gray
|
||||
outImg.data[px + 2] = gray
|
||||
outImg.data[px + 3] = 255
|
||||
const raw = u[idx] / maxVal;
|
||||
const remapped = Math.pow(raw, alpha);
|
||||
const gray = 255 * (1 - remapped);
|
||||
outImg.data[px] = gray;
|
||||
outImg.data[px + 1] = gray;
|
||||
outImg.data[px + 2] = gray;
|
||||
outImg.data[px + 3] = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.putImageData(outImg, 0, 0)
|
||||
ctx.putImageData(outImg, 0, 0);
|
||||
|
||||
canvas.toBlob(
|
||||
blob => {
|
||||
if (!blob) {
|
||||
reject(new Error('Failed to create PNG blob'))
|
||||
return
|
||||
}
|
||||
resolve({
|
||||
imageData: outImg,
|
||||
pngBlob: blob
|
||||
})
|
||||
},
|
||||
'image/png'
|
||||
)
|
||||
}
|
||||
canvas.toBlob(blob => {
|
||||
if (!blob) {
|
||||
reject(new Error('Failed to create PNG blob'));
|
||||
return;
|
||||
}
|
||||
resolve({
|
||||
imageData: outImg,
|
||||
pngBlob: blob
|
||||
});
|
||||
}, 'image/png');
|
||||
};
|
||||
|
||||
img.onerror = () => reject(new Error('Failed to load image'))
|
||||
img.src = URL.createObjectURL(file)
|
||||
})
|
||||
img.onerror = () => reject(new Error('Failed to load image'));
|
||||
img.src = URL.createObjectURL(file);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -546,4 +546,4 @@
|
||||
.drawer-header {
|
||||
padding: 0 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,53 +89,55 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import TabbedLayout from '../../components/common/TabbedLayout.vue'
|
||||
import PropTable from '../../components/common/PropTable.vue'
|
||||
import CliInstallation from '../../components/code/CliInstallation.vue'
|
||||
import CodeExample from '../../components/code/CodeExample.vue'
|
||||
import Customize from '../../components/common/Customize.vue'
|
||||
import PreviewSlider from '../../components/common/PreviewSlider.vue'
|
||||
import MetallicPaint from '../../content/Animations/MetallicPaint/MetallicPaint.vue'
|
||||
import { metallicPaint } from '@/constants/code/Animations/metallicPaintCode'
|
||||
import { parseImage } from '../../content/Animations/MetallicPaint/parseImage'
|
||||
import { useForceRerender } from '@/composables/useForceRerender'
|
||||
import logo from '@/assets/logos/vue-bits-logo-small-dark.svg'
|
||||
import { ref, onMounted } from 'vue';
|
||||
import TabbedLayout from '../../components/common/TabbedLayout.vue';
|
||||
import PropTable from '../../components/common/PropTable.vue';
|
||||
import CliInstallation from '../../components/code/CliInstallation.vue';
|
||||
import CodeExample from '../../components/code/CodeExample.vue';
|
||||
import Customize from '../../components/common/Customize.vue';
|
||||
import PreviewSlider from '../../components/common/PreviewSlider.vue';
|
||||
import MetallicPaint from '../../content/Animations/MetallicPaint/MetallicPaint.vue';
|
||||
import { metallicPaint } from '@/constants/code/Animations/metallicPaintCode';
|
||||
import { parseImage } from '../../content/Animations/MetallicPaint/parseImage';
|
||||
import { useForceRerender } from '@/composables/useForceRerender';
|
||||
import logo from '@/assets/logos/vue-bits-logo-small-dark.svg';
|
||||
|
||||
const imageData = ref<ImageData | null>(null)
|
||||
const edge = ref(0)
|
||||
const patternScale = ref(2)
|
||||
const refraction = ref(0.015)
|
||||
const patternBlur = ref(0.005)
|
||||
const liquid = ref(0.07)
|
||||
const speed = ref(0.3)
|
||||
const imageData = ref<ImageData | null>(null);
|
||||
const edge = ref(0);
|
||||
const patternScale = ref(2);
|
||||
const refraction = ref(0.015);
|
||||
const patternBlur = ref(0.005);
|
||||
const liquid = ref(0.07);
|
||||
const speed = ref(0.3);
|
||||
|
||||
const { rerenderKey, forceRerender } = useForceRerender()
|
||||
const { rerenderKey, forceRerender } = useForceRerender();
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: 'imageData',
|
||||
type: 'ImageData',
|
||||
default: 'none (required)',
|
||||
description: 'The processed image data generated from the parseImage utility. This image data is used by the shader to create the liquid paper effect.'
|
||||
description:
|
||||
'The processed image data generated from the parseImage utility. This image data is used by the shader to create the liquid paper effect.'
|
||||
},
|
||||
{
|
||||
name: 'params',
|
||||
type: 'ShaderParams',
|
||||
default: '',
|
||||
description: 'An object to configure the shader effect. Properties include: patternScale, refraction, edge, patternBlur, liquid, speed'
|
||||
description:
|
||||
'An object to configure the shader effect. Properties include: patternScale, refraction, edge, patternBlur, liquid, speed'
|
||||
}
|
||||
]
|
||||
];
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const response = await fetch(logo)
|
||||
const blob = await response.blob()
|
||||
const file = new File([blob], 'default.png', { type: blob.type })
|
||||
const { imageData: processedImageData } = await parseImage(file)
|
||||
imageData.value = processedImageData
|
||||
const response = await fetch(logo);
|
||||
const blob = await response.blob();
|
||||
const file = new File([blob], 'default.png', { type: blob.type });
|
||||
const { imageData: processedImageData } = await parseImage(file);
|
||||
imageData.value = processedImageData;
|
||||
} catch (err) {
|
||||
console.error('Error loading default image:', err)
|
||||
console.error('Error loading default image:', err);
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -55,4 +55,4 @@ const images = Array.from({ length: numberOfImages }, () => imageUrl);
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user