mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-01 00:05:54 -04:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
becbfa06f5 | ||
|
|
018a77d113 | ||
|
|
d5df7dbca1 | ||
|
|
a605a2e51d | ||
|
|
3e2016a7af | ||
|
|
a77f9c3eec |
@@ -546,7 +546,7 @@ export class Engine {
|
|||||||
canvasPosition: this.realTimeDrawEngine.captureCanvas.position(),
|
canvasPosition: this.realTimeDrawEngine.captureCanvas.position(),
|
||||||
canvasSize: this.realTimeDrawEngine.captureCanvas.size(),
|
canvasSize: this.realTimeDrawEngine.captureCanvas.size(),
|
||||||
shapeType: shapeNode.shapeType,
|
shapeType: shapeNode.shapeType,
|
||||||
size: shapeNode.size,
|
size: shapeNode.kNode.size(),
|
||||||
color: nodeColor.color,
|
color: nodeColor.color,
|
||||||
mediaLayerRef: this.mediaLayer,
|
mediaLayerRef: this.mediaLayer,
|
||||||
selectionManagerRef: this.selectionManager,
|
selectionManagerRef: this.selectionManager,
|
||||||
@@ -581,7 +581,7 @@ export class Engine {
|
|||||||
canvasPosition: this.realTimeDrawEngine.captureCanvas.position(),
|
canvasPosition: this.realTimeDrawEngine.captureCanvas.position(),
|
||||||
canvasSize: this.realTimeDrawEngine.captureCanvas.size(),
|
canvasSize: this.realTimeDrawEngine.captureCanvas.size(),
|
||||||
shapeType: type,
|
shapeType: type,
|
||||||
size: size,
|
size: { width: size, height: size },
|
||||||
color: color,
|
color: color,
|
||||||
mediaLayerRef: this.mediaLayer,
|
mediaLayerRef: this.mediaLayer,
|
||||||
selectionManagerRef: this.selectionManager,
|
selectionManagerRef: this.selectionManager,
|
||||||
|
|||||||
@@ -7,9 +7,11 @@ import { RealTimeDrawEngine } from "../RenderingPrimitives/RealTimeDrawEngine";
|
|||||||
import { MouseEventHandler } from "react";
|
import { MouseEventHandler } from "react";
|
||||||
import { KonvaEventObject } from "konva/lib/Node";
|
import { KonvaEventObject } from "konva/lib/Node";
|
||||||
|
|
||||||
|
export type SupportedNode = Konva.Image | Konva.Group | Konva.Shape;
|
||||||
|
|
||||||
export abstract class BaseNode {
|
export abstract class BaseNode {
|
||||||
protected selectionManagerRef: SelectionManager;
|
protected selectionManagerRef: SelectionManager;
|
||||||
public kNode: Konva.Image | Konva.Group;
|
public kNode: SupportedNode;
|
||||||
protected mediaLayerRef: Konva.Layer;
|
protected mediaLayerRef: Konva.Layer;
|
||||||
|
|
||||||
// Internal State members
|
// Internal State members
|
||||||
@@ -31,7 +33,7 @@ export abstract class BaseNode {
|
|||||||
}: {
|
}: {
|
||||||
selectionManagerRef: SelectionManager;
|
selectionManagerRef: SelectionManager;
|
||||||
mediaLayerRef: Konva.Layer;
|
mediaLayerRef: Konva.Layer;
|
||||||
kNode: Konva.Image | Konva.Group | Konva.Shape;
|
kNode: SupportedNode;
|
||||||
}) {
|
}) {
|
||||||
// console.log("Node constructed");
|
// console.log("Node constructed");
|
||||||
// this.uuid = uuidv4();
|
// this.uuid = uuidv4();
|
||||||
@@ -62,7 +64,7 @@ export abstract class BaseNode {
|
|||||||
}
|
}
|
||||||
public highlight() {
|
public highlight() {
|
||||||
// console.log("Highlight", this.kNode._id);
|
// 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.stroke(primaryOrange);
|
||||||
this.kNode.strokeWidth(highlightStrokeWidth);
|
this.kNode.strokeWidth(highlightStrokeWidth);
|
||||||
return;
|
return;
|
||||||
@@ -85,7 +87,7 @@ export abstract class BaseNode {
|
|||||||
|
|
||||||
public unhighlight() {
|
public unhighlight() {
|
||||||
// console.log("unHighlight", this.kNode._id);
|
// 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);
|
this.kNode.strokeWidth(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import Konva from "konva";
|
import Konva from "konva";
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import { SelectionManager } from "../NodesManagers";
|
import { SelectionManager } from "../NodesManagers";
|
||||||
import { BaseNode } from "./BaseNode";
|
import { BaseNode, SupportedNode } from "./BaseNode";
|
||||||
import { Size } from "../types";
|
import { Size } from "../types";
|
||||||
import { LoadingVideosProvider } from "../EngineUtitlities/LoadingVideosProvider";
|
import { LoadingVideosProvider } from "../EngineUtitlities/LoadingVideosProvider";
|
||||||
import { NodeProgressEventDetail } from "../types/events";
|
import { NodeProgressEventDetail } from "../types/events";
|
||||||
@@ -20,7 +20,7 @@ export enum UploadStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export abstract class NetworkedNode extends BaseNode {
|
export abstract class NetworkedNode extends BaseNode {
|
||||||
public kNode: Konva.Image;
|
public kNode: SupportedNode;
|
||||||
|
|
||||||
// members to deal with loading progress
|
// members to deal with loading progress
|
||||||
public didFinishLoading: boolean = false;
|
public didFinishLoading: boolean = false;
|
||||||
@@ -49,7 +49,7 @@ export abstract class NetworkedNode extends BaseNode {
|
|||||||
localFile,
|
localFile,
|
||||||
loadingVideosProviderRef,
|
loadingVideosProviderRef,
|
||||||
}: {
|
}: {
|
||||||
kNode: Konva.Image;
|
kNode: SupportedNode;
|
||||||
selectionManagerRef: SelectionManager;
|
selectionManagerRef: SelectionManager;
|
||||||
mediaLayerRef: Konva.Layer;
|
mediaLayerRef: Konva.Layer;
|
||||||
loadingVideosProviderRef?: LoadingVideosProvider;
|
loadingVideosProviderRef?: LoadingVideosProvider;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ interface ShapeNodeConstructor {
|
|||||||
canvasPosition: Position;
|
canvasPosition: Position;
|
||||||
canvasSize: Size;
|
canvasSize: Size;
|
||||||
shapeType: ShapeType;
|
shapeType: ShapeType;
|
||||||
size: number;
|
size: Size;
|
||||||
color?: string; // Hex color string
|
color?: string; // Hex color string
|
||||||
transform?: TransformationData;
|
transform?: TransformationData;
|
||||||
mediaLayerRef: Konva.Layer;
|
mediaLayerRef: Konva.Layer;
|
||||||
@@ -29,7 +29,7 @@ export class ShapeNode extends NetworkedNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public shapeType: ShapeType;
|
public shapeType: ShapeType;
|
||||||
public size: number;
|
public size: Size;
|
||||||
public color: string;
|
public color: string;
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
@@ -46,6 +46,7 @@ export class ShapeNode extends NetworkedNode {
|
|||||||
// Create the actual shape inside the group
|
// Create the actual shape inside the group
|
||||||
const transform = NodeUtilities.getInitialTransform({
|
const transform = NodeUtilities.getInitialTransform({
|
||||||
existingTransform,
|
existingTransform,
|
||||||
|
mediaFileSize: size,
|
||||||
canvasPosition,
|
canvasPosition,
|
||||||
canvasSize,
|
canvasSize,
|
||||||
});
|
});
|
||||||
@@ -54,110 +55,48 @@ export class ShapeNode extends NetworkedNode {
|
|||||||
|
|
||||||
switch (shapeType) {
|
switch (shapeType) {
|
||||||
case ShapeType.CIRCLE:
|
case ShapeType.CIRCLE:
|
||||||
const circleShape = new Konva.Circle({
|
shape = new Konva.Ellipse({
|
||||||
radius: 50,
|
x: size.width,
|
||||||
|
y: size.height,
|
||||||
|
radiusX: size.width,
|
||||||
|
radiusY: size.height,
|
||||||
...transform,
|
...transform,
|
||||||
fill: color,
|
fill: color,
|
||||||
strokeScaleEnabled: false,
|
strokeScaleEnabled: false,
|
||||||
draggable: true,
|
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;
|
break;
|
||||||
|
|
||||||
case ShapeType.SQUARE:
|
case ShapeType.SQUARE:
|
||||||
const squareShape = new Konva.Rect({
|
shape = new Konva.Rect({
|
||||||
width: 100,
|
width: size.width,
|
||||||
height: 100,
|
height: size.height,
|
||||||
...transform,
|
...transform,
|
||||||
fill: color,
|
fill: color,
|
||||||
strokeScaleEnabled: false,
|
strokeScaleEnabled: false,
|
||||||
draggable: true,
|
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;
|
break;
|
||||||
|
|
||||||
case ShapeType.TRIANGLE:
|
case ShapeType.TRIANGLE:
|
||||||
const triangleShape = new Konva.RegularPolygon({
|
shape = new Konva.Shape({
|
||||||
sides: 3,
|
x: 0,
|
||||||
radius: 50,
|
y: 0,
|
||||||
|
width: size.width,
|
||||||
|
height: size.height,
|
||||||
...transform,
|
...transform,
|
||||||
fill: color,
|
fill: color,
|
||||||
strokeScaleEnabled: false,
|
strokeScaleEnabled: false,
|
||||||
draggable: true,
|
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;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ export class RealTimeDrawEngine {
|
|||||||
y: number;
|
y: number;
|
||||||
},
|
},
|
||||||
) => void;
|
) => void;
|
||||||
|
private offscreenRenderDiv: HTMLDivElement;
|
||||||
|
|
||||||
private onPreviewCopyCallback?: (previewCopy: Konva.Image) => void; // New Callback
|
private onPreviewCopyCallback?: (previewCopy: Konva.Image) => void; // New Callback
|
||||||
|
|
||||||
@@ -188,6 +189,8 @@ export class RealTimeDrawEngine {
|
|||||||
fill: "white",
|
fill: "white",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.offscreenRenderDiv = document.createElement("div");
|
||||||
|
|
||||||
this.backgroundRasterRect = new Konva.Image({
|
this.backgroundRasterRect = new Konva.Image({
|
||||||
name: "backgroundRasterRect",
|
name: "backgroundRasterRect",
|
||||||
x: this.positionX,
|
x: this.positionX,
|
||||||
@@ -201,7 +204,6 @@ export class RealTimeDrawEngine {
|
|||||||
draggable: false,
|
draggable: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
this.mediaLayerRef.add(this.captureCanvas);
|
this.mediaLayerRef.add(this.captureCanvas);
|
||||||
this.mediaLayerRef.add(this.backgroundRasterRect);
|
this.mediaLayerRef.add(this.backgroundRasterRect);
|
||||||
this.mediaLayerRef.add(this.previewCanvas);
|
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: {
|
private async renderFrame(config: {
|
||||||
layerOfInterest: Konva.Layer; // layer where the element that you want to clip lives.
|
layerOfInterest: Konva.Layer; // layer where the element that you want to clip lives.
|
||||||
// XY and height of a captureCanvas ( region of interest )
|
// 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 y = config.y !== undefined ? config.y : Math.floor(box.y);
|
||||||
const pixelRatio = config.pixelRatio || 1;
|
const pixelRatio = config.pixelRatio || 1;
|
||||||
|
|
||||||
const container = config.layerOfInterest as Container<Group | Shape>;
|
// Clone the required layer from the stage
|
||||||
|
// Set the right details (like removing highlight stroke)
|
||||||
const offScreenSceneCanvas = new OffScreenSceneCanvas({
|
// Then render the cloned stage to a bitmap
|
||||||
width:
|
const stageClone = this.cloneStageForRender(stage, config.layerOfInterest);
|
||||||
config.width || Math.ceil(box.width) || (stage ? stage.width() : 0),
|
const stageBlob = await stageClone.toBlob({
|
||||||
height:
|
x: x,
|
||||||
config.height ||
|
y: y,
|
||||||
Math.ceil(box.height) ||
|
width: config.width || Math.ceil(box.width),
|
||||||
(stage ? stage.height() : 0),
|
height: config.height || Math.ceil(box.height),
|
||||||
pixelRatio: pixelRatio,
|
pixelRatio: pixelRatio,
|
||||||
});
|
}) as Blob;
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
// if config.test is true, the result is downloaded to the local files
|
// if config.test is true, the result is downloaded to the local files
|
||||||
// config.test = true;
|
// config.test = true;
|
||||||
if (config.test) {
|
if (config.test) {
|
||||||
const blob = await offscreenCanvas.convertToBlob({
|
await FileUtilities.blobToFileJpeg(stageBlob, "1");
|
||||||
quality: config.quality ?? 1.0,
|
|
||||||
type: "image/jpeg",
|
|
||||||
});
|
|
||||||
await FileUtilities.blobToFileJpeg(blob, "1");
|
|
||||||
result = blob;
|
|
||||||
} else {
|
|
||||||
result = offscreenCanvas.transferToImageBitmap();
|
|
||||||
}
|
}
|
||||||
context.restore();
|
|
||||||
|
const result = await createImageBitmap(stageBlob);
|
||||||
return result;
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
Reference in New Issue
Block a user