95 lines
3.0 KiB
React
95 lines
3.0 KiB
React
|
|
import { Image as KonvaImage, Transformer } from "react-konva";
|
||
|
|
import { useRef, useEffect, useState } from "react";
|
||
|
|
import useEditorStore from "../useEditorStore";
|
||
|
|
import QRCode from "qrcode";
|
||
|
|
|
||
|
|
export default function QRCodeElement({ element }) {
|
||
|
|
const updateElement = useEditorStore((state) => state.updateElement);
|
||
|
|
const setSelectedId = useEditorStore((state) => state.setSelectedId);
|
||
|
|
const selectedId = useEditorStore((state) => state.selectedId);
|
||
|
|
|
||
|
|
const shapeRef = useRef();
|
||
|
|
const trRef = useRef();
|
||
|
|
const [qrImage, setQrImage] = useState(null);
|
||
|
|
|
||
|
|
const isSelected = selectedId === element.id;
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const generateQR = async () => {
|
||
|
|
try {
|
||
|
|
const url = await QRCode.toDataURL(element.data || "https://example.com", {
|
||
|
|
width: element.size,
|
||
|
|
margin: 1,
|
||
|
|
});
|
||
|
|
const img = new window.Image();
|
||
|
|
img.src = url;
|
||
|
|
img.onload = () => {
|
||
|
|
setQrImage(img);
|
||
|
|
};
|
||
|
|
} catch (err) {
|
||
|
|
console.error("QR Code generation error:", err);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
generateQR();
|
||
|
|
}, [element.data, element.size]);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (isSelected && trRef.current && shapeRef.current) {
|
||
|
|
trRef.current.nodes([shapeRef.current]);
|
||
|
|
trRef.current.getLayer().batchDraw();
|
||
|
|
}
|
||
|
|
}, [isSelected]);
|
||
|
|
|
||
|
|
if (!qrImage) return null;
|
||
|
|
|
||
|
|
return (
|
||
|
|
<>
|
||
|
|
<KonvaImage
|
||
|
|
ref={shapeRef}
|
||
|
|
image={qrImage}
|
||
|
|
x={element.x}
|
||
|
|
y={element.y}
|
||
|
|
width={element.size}
|
||
|
|
height={element.size}
|
||
|
|
rotation={element.rotation}
|
||
|
|
draggable
|
||
|
|
onClick={() => setSelectedId(element.id)}
|
||
|
|
onDragEnd={(e) => {
|
||
|
|
const snap = (v) => Math.round(v / 10) * 10;
|
||
|
|
updateElement(element.id, {
|
||
|
|
x: snap(e.target.x()),
|
||
|
|
y: snap(e.target.y()),
|
||
|
|
});
|
||
|
|
}}
|
||
|
|
onTransformEnd={() => {
|
||
|
|
const node = shapeRef.current;
|
||
|
|
const scaleX = node.scaleX();
|
||
|
|
|
||
|
|
node.scaleX(1);
|
||
|
|
node.scaleY(1);
|
||
|
|
|
||
|
|
updateElement(element.id, {
|
||
|
|
x: node.x(),
|
||
|
|
y: node.y(),
|
||
|
|
size: Math.max(50, node.width() * scaleX),
|
||
|
|
rotation: node.rotation(),
|
||
|
|
});
|
||
|
|
}}
|
||
|
|
/>
|
||
|
|
|
||
|
|
{isSelected && (
|
||
|
|
<Transformer
|
||
|
|
ref={trRef}
|
||
|
|
rotateEnabled={true}
|
||
|
|
boundBoxFunc={(oldBox, newBox) => {
|
||
|
|
if (newBox.width < 50 || newBox.height < 50) {
|
||
|
|
return oldBox;
|
||
|
|
}
|
||
|
|
return newBox;
|
||
|
|
}}
|
||
|
|
/>
|
||
|
|
)}
|
||
|
|
</>
|
||
|
|
);
|
||
|
|
}
|