2026-01-07 10:27:48 +08:00
|
|
|
<template>
|
|
|
|
|
<div class="pingpu" ref="el"><canvas ref="canvasRef"></canvas></div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
2026-01-07 13:02:08 +08:00
|
|
|
import { fabric } from "fabric-with-all";
|
|
|
|
|
import { ref, watch, onMounted } from "vue";
|
2026-01-07 10:27:48 +08:00
|
|
|
const props = defineProps({
|
2026-01-09 14:40:18 +08:00
|
|
|
list: { type: Array, default: () => [] },
|
2026-01-07 10:27:48 +08:00
|
|
|
});
|
2026-01-07 13:02:08 +08:00
|
|
|
const el = ref(null);
|
|
|
|
|
const canvasRef = ref(null);
|
|
|
|
|
const observer = ref(null);
|
2026-01-09 14:40:18 +08:00
|
|
|
var canvas = null;
|
2026-01-07 13:02:08 +08:00
|
|
|
onMounted(async () => {
|
|
|
|
|
initCanvas();
|
2026-01-09 14:40:18 +08:00
|
|
|
await setCanvasData();
|
|
|
|
|
|
2026-01-07 13:02:08 +08:00
|
|
|
let throttleTimeout = null;
|
|
|
|
|
let lastRunTime = 0;
|
|
|
|
|
let trailingTimeout = null;
|
|
|
|
|
observer.value = new ResizeObserver((entries) => {
|
|
|
|
|
const now = Date.now();
|
|
|
|
|
const throttleDelay = 100;
|
|
|
|
|
if (!throttleTimeout) {
|
|
|
|
|
updateCanvasSize();
|
|
|
|
|
lastRunTime = now;
|
|
|
|
|
throttleTimeout = setTimeout(() => {
|
|
|
|
|
throttleTimeout = null;
|
|
|
|
|
}, throttleDelay);
|
|
|
|
|
} else {
|
|
|
|
|
clearTimeout(trailingTimeout);
|
|
|
|
|
trailingTimeout = setTimeout(() => {
|
|
|
|
|
updateCanvasSize();
|
|
|
|
|
lastRunTime = Date.now();
|
|
|
|
|
}, throttleDelay);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
observer.value.observe(el.value);
|
|
|
|
|
});
|
|
|
|
|
onBeforeUnmount(() => {
|
|
|
|
|
observer.value.disconnect();
|
|
|
|
|
});
|
|
|
|
|
const initCanvas = () => {
|
2026-01-09 14:40:18 +08:00
|
|
|
canvas = new fabric.Canvas(canvasRef.value, {
|
2026-01-07 13:02:08 +08:00
|
|
|
selection: false,
|
|
|
|
|
});
|
2026-01-09 14:40:18 +08:00
|
|
|
canvas.setWidth(el.value.offsetWidth);
|
|
|
|
|
canvas.setHeight(el.value.offsetHeight);
|
2026-01-07 13:02:08 +08:00
|
|
|
};
|
2026-01-09 14:40:18 +08:00
|
|
|
const updateCanvasSize = async () => {
|
|
|
|
|
canvas.setWidth(el.value.offsetWidth);
|
|
|
|
|
canvas.setHeight(el.value.offsetHeight);
|
|
|
|
|
await setCanvasData();
|
2026-01-07 13:02:08 +08:00
|
|
|
};
|
2026-01-09 14:40:18 +08:00
|
|
|
const urlToCanvas = (url) => {
|
2026-01-07 13:02:08 +08:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
fabric.Image.fromURL(
|
2026-01-09 14:40:18 +08:00
|
|
|
url,
|
2026-01-07 13:02:08 +08:00
|
|
|
(object) => {
|
|
|
|
|
const imgElement = object.getElement();
|
|
|
|
|
// 创建透明 Canvas
|
|
|
|
|
const tcanvas = document.createElement("canvas");
|
|
|
|
|
tcanvas.width = imgElement.width;
|
|
|
|
|
tcanvas.height = imgElement.height;
|
|
|
|
|
const ctx = tcanvas.getContext("2d");
|
|
|
|
|
ctx.clearRect(0, 0, tcanvas.width, tcanvas.height);
|
|
|
|
|
ctx.drawImage(imgElement, 0, 0);
|
|
|
|
|
resolve(tcanvas);
|
|
|
|
|
},
|
|
|
|
|
{ crossOrigin: "anonymous" }
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
};
|
2026-01-09 14:40:18 +08:00
|
|
|
const setCanvasData = async () => {
|
|
|
|
|
canvas.clear();
|
|
|
|
|
const cwidth = canvas.width;
|
|
|
|
|
const cheight = canvas.height;
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < props.list.length; i++) {
|
|
|
|
|
let item = props.list[i];
|
|
|
|
|
let image = await urlToCanvas(item.path);
|
|
|
|
|
let offsetX = item.location[0];
|
|
|
|
|
let offsetY = item.location[1];
|
|
|
|
|
let scaleX = ((cwidth / image.width) * item.scale[0]) / 5;
|
|
|
|
|
let scaleY = ((cheight / image.height) * item.scale[1]) / 5;
|
|
|
|
|
let scale = cwidth > cheight ? scaleX : scaleY;
|
|
|
|
|
let angle = item.angle;
|
|
|
|
|
let gapX = item.object.gapX;
|
|
|
|
|
let gapY = item.object.gapY;
|
|
|
|
|
let patternTransform = fabric.util.composeMatrix({
|
|
|
|
|
scaleX: scale,
|
|
|
|
|
scaleY: scale,
|
|
|
|
|
angle: angle,
|
|
|
|
|
});
|
|
|
|
|
// 创建透明 Canvas
|
|
|
|
|
let tcanvas = document.createElement("canvas");
|
|
|
|
|
tcanvas.width = image.width + gapX;
|
|
|
|
|
tcanvas.height = image.height + gapY;
|
|
|
|
|
let ctx = tcanvas.getContext("2d");
|
|
|
|
|
ctx.clearRect(0, 0, tcanvas.width, tcanvas.height);
|
|
|
|
|
ctx.drawImage(image, 0, 0);
|
|
|
|
|
let pattern = new fabric.Pattern({
|
|
|
|
|
source: tcanvas,
|
|
|
|
|
repeat: "repeat",
|
|
|
|
|
patternTransform,
|
|
|
|
|
offsetX, // 水平偏移
|
|
|
|
|
offsetY, // 垂直偏移
|
|
|
|
|
});
|
|
|
|
|
let rect = new fabric.Rect({
|
|
|
|
|
id: item.id,
|
|
|
|
|
width: cwidth,
|
|
|
|
|
height: cheight,
|
|
|
|
|
fill: pattern,
|
|
|
|
|
|
|
|
|
|
...item.object,
|
|
|
|
|
});
|
|
|
|
|
canvas.add(rect);
|
|
|
|
|
}
|
|
|
|
|
canvas.renderAll();
|
2026-01-07 13:02:08 +08:00
|
|
|
};
|
2026-01-09 14:40:18 +08:00
|
|
|
const updataList = (list) => {
|
|
|
|
|
|
|
|
|
|
console.log(list);
|
|
|
|
|
};
|
|
|
|
|
defineExpose({
|
|
|
|
|
updataList,
|
|
|
|
|
});
|
2026-01-07 10:27:48 +08:00
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang='less' scoped>
|
|
|
|
|
.pingpu {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
}
|
|
|
|
|
</style>
|