[英]Need help converting code for fabric canvas from Vanilla JS to ReactJS
所以我在玩 fabricjs canvas 庫,我發現這個小提琴是用 vanillajs 寫的,它可以讓你在 canvas 上繪制多邊形。 我想在我的 react 項目中實現這個確切的東西,所以我嘗試將整個代碼轉換為 react ( https://codesandbox.io/s/jolly-kowalevski-tjt58 )。 該代碼有些工作,但有一些新的錯誤不在原始小提琴中,我在修復它們時遇到了麻煩。
例如:嘗試通過單擊繪制按鈕創建一個多邊形,當你第一次這樣做時,多邊形繪制沒有任何錯誤,但是當你第二次再次單擊繪制按鈕時,canvas 開始表現得怪異而怪異創建多邊形。
所以基本上我需要幫助來轉換香草代碼以應對 0 個錯誤。
額外的信息:
小提琴中使用的面料版本:4.0.0
沙盒中的結構版本:4.0.0
香草JS代碼:
const getPathBtn = document.getElementById("get-path");
const drawPolygonBtn = document.getElementById("draw-polygon");
const showPolygonBtn = document.getElementById("show-polygon");
const editPolygonBtn = document.getElementById("edit-polygon");
const canvas = new fabric.Canvas("canvas", {
selection: false
});
let line, isDown;
let prevCords;
let vertices = [];
let polygon;
const resetCanvas = () => {
canvas.off();
canvas.clear();
};
const resetVariables = () => {
line = undefined;
isDown = undefined;
prevCords = undefined;
polygon = undefined;
vertices = [];
};
const addVertice = (newPoint) => {
if (vertices.length > 0) {
const lastPoint = vertices[vertices.length - 1];
if (lastPoint.x !== newPoint.x && lastPoint.y !== newPoint.y) {
vertices.push(newPoint);
}
} else {
vertices.push(newPoint);
}
};
const drawPolygon = () => {
resetVariables();
resetCanvas();
canvas.on("mouse:down", function(o) {
isDown = true;
const pointer = canvas.getPointer(o.e);
let points = [pointer.x, pointer.y, pointer.x, pointer.y];
if (prevCords && prevCords.x2 && prevCords.y2) {
const prevX = prevCords.x2;
const prevY = prevCords.y2;
points = [prevX, prevY, prevX, prevY];
}
const newPoint = {
x: points[0],
y: points[1]
};
addVertice(newPoint);
line = new fabric.Line(points, {
strokeWidth: 2,
fill: "black",
stroke: "black",
originX: "center",
originY: "center",
});
canvas.add(line);
});
canvas.on("mouse:move", function(o) {
if (!isDown) return;
const pointer = canvas.getPointer(o.e);
const coords = {
x2: pointer.x,
y2: pointer.y
};
line.set(coords);
prevCords = coords;
canvas.renderAll();
});
canvas.on("mouse:up", function(o) {
isDown = false;
const pointer = canvas.getPointer(o.e);
const newPoint = {
x: pointer.x,
y: pointer.y
};
addVertice(newPoint);
});
canvas.on("object:moving", function(option) {
const object = option.target;
canvas.forEachObject(function(obj) {
if (obj.name == "Polygon") {
if (obj.PolygonNumber == object.polygonNo) {
const points = window["polygon" + object.polygonNo].get(
"points"
);
points[object.circleNo - 1].x = object.left;
points[object.circleNo - 1].y = object.top;
window["polygon" + object.polygonNo].set({
points: points,
});
}
}
});
canvas.renderAll();
});
};
const showPolygon = () => {
resetCanvas();
if (!polygon) {
polygon = new fabric.Polygon(vertices, {
fill: "transparent",
strokeWidth: 2,
stroke: "black",
objectCaching: false,
transparentCorners: false,
cornerColor: "blue",
});
}
polygon.edit = false;
polygon.hasBorders = true;
polygon.cornerColor = "blue";
polygon.cornerStyle = "rect";
polygon.controls = fabric.Object.prototype.controls;
canvas.add(polygon);
};
// polygon stuff
// define a function that can locate the controls.
// this function will be used both for drawing and for interaction.
function polygonPositionHandler(dim, finalMatrix, fabricObject) {
let x = fabricObject.points[this.pointIndex].x - fabricObject.pathOffset.x,
y = fabricObject.points[this.pointIndex].y - fabricObject.pathOffset.y;
return fabric.util.transformPoint({
x: x,
y: y
},
fabric.util.multiplyTransformMatrices(
fabricObject.canvas.viewportTransform,
fabricObject.calcTransformMatrix()
)
);
}
// define a function that will define what the control does
// this function will be called on every mouse move after a control has been
// clicked and is being dragged.
// The function receive as argument the mouse event, the current trasnform object
// and the current position in canvas coordinate
// transform.target is a reference to the current object being transformed,
function actionHandler(eventData, transform, x, y) {
let polygon = transform.target,
currentControl = polygon.controls[polygon.__corner],
mouseLocalPosition = polygon.toLocalPoint(
new fabric.Point(x, y),
"center",
"center"
),
polygonBaseSize = polygon._getNonTransformedDimensions(),
size = polygon._getTransformedDimensions(0, 0),
finalPointPosition = {
x: (mouseLocalPosition.x * polygonBaseSize.x) / size.x +
polygon.pathOffset.x,
y: (mouseLocalPosition.y * polygonBaseSize.y) / size.y +
polygon.pathOffset.y,
};
polygon.points[currentControl.pointIndex] = finalPointPosition;
return true;
}
// define a function that can keep the polygon in the same position when we change its
// width/height/top/left.
function anchorWrapper(anchorIndex, fn) {
return function(eventData, transform, x, y) {
let fabricObject = transform.target,
absolutePoint = fabric.util.transformPoint({
x: fabricObject.points[anchorIndex].x -
fabricObject.pathOffset.x,
y: fabricObject.points[anchorIndex].y -
fabricObject.pathOffset.y,
},
fabricObject.calcTransformMatrix()
),
actionPerformed = fn(eventData, transform, x, y),
newDim = fabricObject._setPositionDimensions({}),
polygonBaseSize = fabricObject._getNonTransformedDimensions(),
newX =
(fabricObject.points[anchorIndex].x -
fabricObject.pathOffset.x) /
polygonBaseSize.x,
newY =
(fabricObject.points[anchorIndex].y -
fabricObject.pathOffset.y) /
polygonBaseSize.y;
fabricObject.setPositionByOrigin(absolutePoint, newX + 0.5, newY + 0.5);
return actionPerformed;
};
}
function editPolygon() {
canvas.setActiveObject(polygon);
polygon.edit = true;
polygon.hasBorders = false;
let lastControl = polygon.points.length - 1;
polygon.cornerStyle = "circle";
polygon.cornerColor = "rgba(0,0,255,0.5)";
polygon.controls = polygon.points.reduce(function(acc, point, index) {
acc["p" + index] = new fabric.Control({
positionHandler: polygonPositionHandler,
actionHandler: anchorWrapper(
index > 0 ? index - 1 : lastControl,
actionHandler
),
actionName: "modifyPolygon",
pointIndex: index,
});
return acc;
}, {});
canvas.requestRenderAll();
}
// Button events
drawPolygonBtn.onclick = () => {
drawPolygon();
};
showPolygonBtn.onclick = () => {
showPolygon();
};
editPolygonBtn.onclick = () => {
editPolygon();
};
getPathBtn.onclick = () => {
console.log("vertices", polygon.points);
};
在第二次繪制(第二次再次單擊繪制按鈕)時,線始終連接到同一點。 所以prevCords有問題。 通過將 console.log 添加到 "mouse:mouse" 的處理程序 function 確認上述聲明:
fabricCanvas.on("mouse:move", function (o) {
console.log("mousemove fired", prevCords); // always the same value
if (isDown.current || !line.current) return;
const pointer = fabricCanvas.getPointer(o.e);
const coords = {
x2: pointer.x,
y2: pointer.y
};
line.current.set(coords);
setPrevCords(coords); // the line should connect to this new point
fabricCanvas.renderAll();
});
這是因為closure
,mouse:move 的 function 處理程序將始終記住 prevCords 創建時的值(即當您單擊 Draw 按鈕時),而不是由setPrevCords
更新的值
要解決上述問題,只需使用useRef
存儲 prevCords(或使用引用)第 6 行:
const [fabricCanvas, setFabricCanvas] = useState();
const prevCordsRef = useRef();
const line = useRef();
第 35 行:
const resetVariables = () => {
line.current = undefined;
isDown.current = undefined;
prevCordsRef.current = undefined;
polygon.current = undefined;
vertices.current = [];
};
第 65 行:
if (prevCordsRef.current && prevCordsRef.current.x2 && prevCordsRef.current.y2) {
const prevX = prevCordsRef.current.x2;
const prevY = prevCordsRef.current.y2;
points = [prevX, prevY, prevX, prevY];
}
第 96 行:
prevCordsRef.current = coords;
最后一個建議是更改第 89 行(以便該功能與演示相匹配):
if (!isDown.current) return;
總結:
useState
用於必須在另一個 function 處理程序中具有最新值的變量。 改用useRef
useState
是一種浪費,因為 React 會在每個 setState 上重新渲染
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.