Compare commits

...
5 changed files with 72 additions and 137 deletions
+2 -2
View File
@@ -546,7 +546,7 @@ export class Engine {
canvasPosition: this.realTimeDrawEngine.captureCanvas.position(),
canvasSize: this.realTimeDrawEngine.captureCanvas.size(),
shapeType: shapeNode.shapeType,
size: shapeNode.size,
size: shapeNode.kNode.size(),
color: nodeColor.color,
mediaLayerRef: this.mediaLayer,
selectionManagerRef: this.selectionManager,
@@ -581,7 +581,7 @@ export class Engine {
canvasPosition: this.realTimeDrawEngine.captureCanvas.position(),
canvasSize: this.realTimeDrawEngine.captureCanvas.size(),
shapeType: type,
size: size,
size: { width: size, height: size },
color: color,
mediaLayerRef: this.mediaLayer,
selectionManagerRef: this.selectionManager,
@@ -7,9 +7,11 @@ import { RealTimeDrawEngine } from "../RenderingPrimitives/RealTimeDrawEngine";
import { MouseEventHandler } from "react";
import { KonvaEventObject } from "konva/lib/Node";
export type SupportedNode = Konva.Image | Konva.Group | Konva.Shape;
export abstract class BaseNode {
protected selectionManagerRef: SelectionManager;
public kNode: Konva.Image | Konva.Group;
public kNode: SupportedNode;
protected mediaLayerRef: Konva.Layer;
// Internal State members
@@ -31,7 +33,7 @@ export abstract class BaseNode {
}: {
selectionManagerRef: SelectionManager;
mediaLayerRef: Konva.Layer;
kNode: Konva.Image | Konva.Group | Konva.Shape;
kNode: SupportedNode;
}) {
// console.log("Node constructed");
// this.uuid = uuidv4();
@@ -62,7 +64,7 @@ export abstract class BaseNode {
}
public highlight() {
// console.log("Highlight", this.kNode._id);
if (this.kNode instanceof Konva.Image) {
if (this.kNode instanceof Konva.Image || this.kNode instanceof Konva.Shape) {
this.kNode.stroke(primaryOrange);
this.kNode.strokeWidth(highlightStrokeWidth);
return;
@@ -85,7 +87,7 @@ export abstract class BaseNode {
public unhighlight() {
// console.log("unHighlight", this.kNode._id);
if (this.kNode instanceof Konva.Image) {
if (this.kNode instanceof Konva.Image || this.kNode instanceof Konva.Shape) {
this.kNode.strokeWidth(0);
return;
}
@@ -1,7 +1,7 @@
import Konva from "konva";
import { v4 as uuidv4 } from "uuid";
import { SelectionManager } from "../NodesManagers";
import { BaseNode } from "./BaseNode";
import { BaseNode, SupportedNode } from "./BaseNode";
import { Size } from "../types";
import { LoadingVideosProvider } from "../EngineUtitlities/LoadingVideosProvider";
import { NodeProgressEventDetail } from "../types/events";
@@ -20,7 +20,7 @@ export enum UploadStatus {
}
export abstract class NetworkedNode extends BaseNode {
public kNode: Konva.Image;
public kNode: SupportedNode;
// members to deal with loading progress
public didFinishLoading: boolean = false;
@@ -49,7 +49,7 @@ export abstract class NetworkedNode extends BaseNode {
localFile,
loadingVideosProviderRef,
}: {
kNode: Konva.Image;
kNode: SupportedNode;
selectionManagerRef: SelectionManager;
mediaLayerRef: Konva.Layer;
loadingVideosProviderRef?: LoadingVideosProvider;
@@ -15,7 +15,7 @@ interface ShapeNodeConstructor {
canvasPosition: Position;
canvasSize: Size;
shapeType: ShapeType;
size: number;
size: Size;
color?: string; // Hex color string
transform?: TransformationData;
mediaLayerRef: Konva.Layer;
@@ -29,7 +29,7 @@ export class ShapeNode extends NetworkedNode {
}
public shapeType: ShapeType;
public size: number;
public size: Size;
public color: string;
constructor({
@@ -46,6 +46,7 @@ export class ShapeNode extends NetworkedNode {
// Create the actual shape inside the group
const transform = NodeUtilities.getInitialTransform({
existingTransform,
mediaFileSize: size,
canvasPosition,
canvasSize,
});
@@ -54,110 +55,48 @@ export class ShapeNode extends NetworkedNode {
switch (shapeType) {
case ShapeType.CIRCLE:
const circleShape = new Konva.Circle({
radius: 50,
shape = new Konva.Ellipse({
x: size.width,
y: size.height,
radiusX: size.width,
radiusY: size.height,
...transform,
fill: color,
strokeScaleEnabled: false,
draggable: true,
});
// Convert circle to image
const dataURL = circleShape.toDataURL();
const imageObj = new Image();
imageObj.src = dataURL;
shape = new Konva.Image({
image: imageObj,
x: circleShape.x(),
y: circleShape.y(),
width: circleShape.width(),
height: circleShape.height(),
...transform,
fill: "transparent",
strokeScaleEnabled: false,
draggable: true,
});
imageObj.onload = () => {
this.mediaLayerRef.draw();
loaded();
};
// Clean up the temporary circle
circleShape.destroy();
break;
case ShapeType.SQUARE:
const squareShape = new Konva.Rect({
width: 100,
height: 100,
shape = new Konva.Rect({
width: size.width,
height: size.height,
...transform,
fill: color,
strokeScaleEnabled: false,
draggable: true,
});
// Convert square to image
const squareDataURL = squareShape.toDataURL();
const squareImageObj = new Image();
squareImageObj.src = squareDataURL;
shape = new Konva.Image({
image: squareImageObj,
x: squareShape.x(),
y: squareShape.y(),
width: squareShape.width(),
height: squareShape.height(),
...transform,
fill: "transparent",
strokeScaleEnabled: false,
draggable: true,
});
squareImageObj.onload = () => {
this.mediaLayerRef.draw();
loaded();
};
// Clean up the temporary square
squareShape.destroy();
break;
case ShapeType.TRIANGLE:
const triangleShape = new Konva.RegularPolygon({
sides: 3,
radius: 50,
shape = new Konva.Shape({
x: 0,
y: 0,
width: size.width,
height: size.height,
...transform,
fill: color,
strokeScaleEnabled: false,
draggable: true,
sceneFunc: (context, shape) => {
context.beginPath();
context.moveTo(shape.width() / 2, 0);
context.lineTo(shape.width(), shape.height());
context.lineTo(0, shape.height());
context.closePath();
context.fillStrokeShape(shape);
}
});
// Convert triangle to image
const triangleDataURL = triangleShape.toDataURL();
const triangleImageObj = new Image();
triangleImageObj.src = triangleDataURL;
shape = new Konva.Image({
image: triangleImageObj,
x: triangleShape.x(),
y: triangleShape.y(),
width: triangleShape.width(),
height: triangleShape.height(),
...transform,
fill: "transparent",
strokeScaleEnabled: false,
draggable: true,
});
triangleImageObj.onload = () => {
this.mediaLayerRef.draw();
loaded();
};
// Clean up the temporary triangle
triangleShape.destroy();
break;
default:
@@ -91,6 +91,7 @@ export class RealTimeDrawEngine {
y: number;
},
) => void;
private offscreenRenderDiv: HTMLDivElement;
private onPreviewCopyCallback?: (previewCopy: Konva.Image) => void; // New Callback
@@ -188,6 +189,8 @@ export class RealTimeDrawEngine {
fill: "white",
});
this.offscreenRenderDiv = document.createElement("div");
this.backgroundRasterRect = new Konva.Image({
name: "backgroundRasterRect",
x: this.positionX,
@@ -201,7 +204,6 @@ export class RealTimeDrawEngine {
draggable: false,
});
this.mediaLayerRef.add(this.captureCanvas);
this.mediaLayerRef.add(this.backgroundRasterRect);
this.mediaLayerRef.add(this.previewCanvas);
@@ -805,6 +807,27 @@ export class RealTimeDrawEngine {
}
}
private cloneStageForRender(stage: Konva.Stage, layerOfInterest: Konva.Layer): Konva.Stage {
const stageClone = new Konva.Stage({
width: stage.width(),
height: stage.height(),
container: this.offscreenRenderDiv,
});
const renderLayer = new Konva.Layer();
// Clone all the nodes then reset them to render right
layerOfInterest.getChildren().forEach((node) => {
const dupNode = node.clone();
dupNode.strokeWidth(0);
renderLayer.add(dupNode);
});
stageClone.add(renderLayer);
return stageClone;
}
private async renderFrame(config: {
layerOfInterest: Konva.Layer; // layer where the element that you want to clip lives.
// XY and height of a captureCanvas ( region of interest )
@@ -825,54 +848,25 @@ export class RealTimeDrawEngine {
const y = config.y !== undefined ? config.y : Math.floor(box.y);
const pixelRatio = config.pixelRatio || 1;
const container = config.layerOfInterest as Container<Group | Shape>;
const offScreenSceneCanvas = new OffScreenSceneCanvas({
width:
config.width || Math.ceil(box.width) || (stage ? stage.width() : 0),
height:
config.height ||
Math.ceil(box.height) ||
(stage ? stage.height() : 0),
// Clone the required layer from the stage
// Set the right details (like removing highlight stroke)
// Then render the cloned stage to a bitmap
const stageClone = this.cloneStageForRender(stage, config.layerOfInterest);
const stageBlob = await stageClone.toBlob({
x: x,
y: y,
width: config.width || Math.ceil(box.width),
height: config.height || Math.ceil(box.height),
pixelRatio: pixelRatio,
});
const context = offScreenSceneCanvas.getContext();
const buffer = new OffScreenSceneCanvas({
width:
offScreenSceneCanvas.width / offScreenSceneCanvas.pixelRatio +
Math.abs(x),
height:
offScreenSceneCanvas.height / offScreenSceneCanvas.pixelRatio +
Math.abs(y),
pixelRatio: offScreenSceneCanvas.pixelRatio,
});
context.save();
if (x || y) {
context.translate(-1 * x, -1 * y);
}
container.drawScene(offScreenSceneCanvas, undefined, buffer);
// Not a type mistake ... DO NOT FIX
// @ts-ignore
const offscreenCanvas = offScreenSceneCanvas._canvas as OffscreenCanvas;
let result = undefined;
}) as Blob;
// if config.test is true, the result is downloaded to the local files
// config.test = true;
if (config.test) {
const blob = await offscreenCanvas.convertToBlob({
quality: config.quality ?? 1.0,
type: "image/jpeg",
});
await FileUtilities.blobToFileJpeg(blob, "1");
result = blob;
} else {
result = offscreenCanvas.transferToImageBitmap();
await FileUtilities.blobToFileJpeg(stageBlob, "1");
}
context.restore();
const result = await createImageBitmap(stageBlob);
return result;
} catch (error) {
throw error;