feat: enhance designer canvas with multi-view support and model viewer integration
Some checks failed
Deploy Production / deploy (push) Failing after 1m11s

- Added canvasId prop to DesignerCanvas for identifying multiple canvases.
- Implemented active view selection (front, top, left, right) in designer page.
- Updated DesignerCanvas to maintain aspect ratio and dimensions based on view.
- Integrated @google/model-viewer for 3D model rendering on the index page.
- Refactored useSlipmatDesigner to manage multiple canvases and their states.
- Added LAMESA.glb model file for 3D representation.
- Updated package.json and package-lock.json to include @google/model-viewer dependency.
This commit is contained in:
Frank John Begornia
2026-01-12 23:00:32 +08:00
parent 3ba0b250ed
commit 27dabed2d2
7 changed files with 457 additions and 150 deletions

View File

@@ -4,12 +4,14 @@ import { onBeforeUnmount, onMounted, ref, watch } from "vue";
import type { Canvas as FabricCanvas } from "fabric";
const props = defineProps<{
canvasId?: string;
size: number;
registerCanvas: (payload: {
canvas: FabricCanvas;
fabric: typeof import("fabric");
canvasId?: string;
}) => void;
unregisterCanvas: () => void;
unregisterCanvas: (canvasId?: string) => void;
backgroundColor: string;
}>();
@@ -30,28 +32,28 @@ const syncCanvasDomStyles = () => {
if (!element) {
return;
}
element.classList.add("rounded-full");
element.style.borderRadius = "9999px";
element.classList.add("rounded-lg");
element.style.borderRadius = "8px";
element.style.backgroundColor = "transparent";
});
};
const updateCssDimensions = (dimension?: number) => {
const updateCssDimensions = (containerWidth?: number) => {
if (!fabricCanvas) {
return;
}
const targetSize =
dimension ??
const targetWidth =
containerWidth ??
containerElement.value?.clientWidth ??
containerElement.value?.clientHeight ??
null;
if (!targetSize) {
if (!targetWidth) {
return;
}
const targetHeight = Math.round(targetWidth * 0.67); // 3:2 aspect ratio
fabricCanvas.setDimensions(
{
width: targetSize,
height: targetSize,
width: targetWidth,
height: targetHeight,
},
{ cssOnly: true }
);
@@ -70,8 +72,8 @@ const observeContainer = () => {
if (!entry) {
return;
}
const dimension = Math.min(entry.contentRect.width, entry.contentRect.height);
updateCssDimensions(dimension);
const containerWidth = entry.contentRect.width;
updateCssDimensions(containerWidth);
});
resizeObserver.observe(containerElement.value);
updateCssDimensions();
@@ -91,9 +93,14 @@ const setupCanvas = async () => {
preserveObjectStacking: true,
});
fabricCanvas.setDimensions({ width: props.size, height: props.size });
const canvasHeight = Math.round(props.size * 0.67); // 3:2 aspect ratio
fabricCanvas.setDimensions({ width: props.size, height: canvasHeight });
props.registerCanvas({ canvas: fabricCanvas, fabric: fabricModule });
props.registerCanvas({
canvas: fabricCanvas,
fabric: fabricModule,
canvasId: props.canvasId
});
observeContainer();
syncCanvasDomStyles();
isReady.value = true;
@@ -104,7 +111,7 @@ onMounted(() => {
});
onBeforeUnmount(() => {
props.unregisterCanvas();
props.unregisterCanvas(props.canvasId);
resizeObserver?.disconnect();
resizeObserver = null;
fabricCanvas = null;
@@ -118,7 +125,8 @@ watch(
if (!isReady.value || next === prev || !fabricCanvas) {
return;
}
fabricCanvas.setDimensions({ width: next, height: next });
const canvasHeight = Math.round(next * 0.67); // 3:2 aspect ratio
fabricCanvas.setDimensions({ width: next, height: canvasHeight });
updateCssDimensions();
fabricCanvas.renderAll();
}
@@ -127,20 +135,20 @@ watch(
<template>
<div
class="relative mx-auto flex max-w-[min(720px,100%)] items-center justify-center overflow-hidden rounded-3xl border border-slate-700/60 bg-slate-900/80 p-6 shadow-xl shadow-slate-950/40"
class="relative mx-auto flex max-w-[min(900px,100%)] items-center justify-center overflow-hidden rounded-3xl border border-slate-700/60 bg-slate-900/80 p-6 shadow-xl shadow-slate-950/40"
>
<div class="relative aspect-square w-full">
<div class="relative aspect-[3/2] w-full">
<div class="absolute inset-4 sm:inset-5 md:inset-6 lg:inset-8">
<div ref="containerElement" class="relative h-full w-full">
<canvas
ref="canvasElement"
class="absolute inset-0 h-full w-full rounded-full"
class="absolute inset-0 h-full w-full rounded-lg"
:width="size"
:height="size"
/>
<div
v-if="!isReady"
class="absolute inset-0 grid place-items-center rounded-full bg-slate-900/70"
class="absolute inset-0 grid place-items-center rounded-lg bg-slate-900/70"
>
<span class="text-sm font-medium text-slate-400">Loading canvas</span>
</div>