@inweb/markup
Version:
JavaScript 2D markups
204 lines (160 loc) • 5.15 kB
text/typescript
import Konva from "konva";
import { IMarkupCloud, IMarkupCloudParams } from "../IMarkupCloud";
export class KonvaCloud implements IMarkupCloud {
private _ref: Konva.Shape;
constructor(params: IMarkupCloudParams, ref = null) {
if (ref) {
this._ref = ref;
return;
}
if (!params) params = {};
if (!params.position) params.position = { x: 0, y: 0 };
const arcRadius = 16;
this._ref = new Konva.Shape({
x: params.position.x,
y: params.position.y,
width: params.width ?? 200,
height: params.height ?? 200,
stroke: params.color ?? "#ff0000",
strokeWidth: params.lineWidth ?? 4,
draggable: true,
strokeScaleEnabled: false,
globalCompositeOperation: "source-over",
sceneFunc: (context, shape) => {
function calculateMidpoint(position, width, height) {
const midX = position.x + width / 2;
const midY = position.y + height / 2;
return { x: midX, y: midY };
}
const points = [
{ x: 0, y: 0 },
{ x: 0 + this._ref.width(), y: 0 },
{ x: 0 + this._ref.width(), y: 0 + this._ref.height() },
{ x: 0, y: 0 + this._ref.height() },
{ x: 0, y: 0 },
];
const midPoint = calculateMidpoint({ x: 0, y: 0 }, this._ref.width(), this._ref.height());
const baseArcLength = 30;
context.beginPath();
for (let iPoint = 0; iPoint < points.length - 1; iPoint++) {
let approxArcLength = baseArcLength;
const dx = points[iPoint + 1].x - points[iPoint].x;
const dy = points[iPoint + 1].y - points[iPoint].y;
const length = Math.sqrt(dx * dx + dy * dy);
const arcCount = Math.floor(length / approxArcLength);
const lengthMod = length % approxArcLength;
approxArcLength = baseArcLength + arcCount / lengthMod;
let pX = points[iPoint].x + dx / arcCount / 2;
let pY = points[iPoint].y + dy / arcCount / 2;
const pEndX = points[iPoint + 1].x;
const pEndY = points[iPoint + 1].y;
const endAngle = Math.atan((pEndY - pY) / (pEndX - pX));
const startAngle = endAngle + Math.PI;
const counterClockwise = pX > midPoint.x && pY > midPoint.y;
for (let iArc = 0; iArc < arcCount; iArc++) {
if (counterClockwise) {
context.arc(pX, pY, arcRadius, endAngle, startAngle);
} else {
context.arc(pX, pY, arcRadius, startAngle, endAngle);
}
pX += dx / arcCount;
pY += dy / arcCount;
}
}
context.closePath();
// (!) Konva specific method, it is very important
// it will apply are required styles
context.fillStrokeShape(shape);
},
});
this._ref.className = "Cloud";
this._ref.on("transform", (e) => {
const attrs = e.target.attrs;
if (attrs.rotation !== this._ref.rotation()) this._ref.rotation(attrs.rotation);
const scaleByX = Math.abs(attrs.scaleX - 1) > 10e-6;
const scaleByY = Math.abs(attrs.scaleY - 1) > 10e-6;
let newWidth = this._ref.width();
if (scaleByX) newWidth *= attrs.scaleX;
let newHeight = this._ref.height();
if (scaleByY) newHeight *= attrs.scaleY;
const minWidth = 100;
const minHeight = 100;
if (newWidth < minWidth) newWidth = minWidth;
if (newHeight < minHeight) newHeight = minHeight;
if (scaleByX) {
this._ref.width(newWidth);
}
if (scaleByY) {
this._ref.height(newHeight);
}
this._ref.scale({ x: 1, y: 1 });
});
this._ref.getSelfRect = () => {
return {
x: 0 - arcRadius,
y: 0 - arcRadius,
width: this._ref.width() + 2 * arcRadius,
height: this._ref.height() + 2 * arcRadius,
};
};
this._ref.id(this._ref._id.toString());
}
ref() {
return this._ref;
}
id(): string {
return this._ref.id();
}
enableMouseEditing(value: boolean): void {
this._ref.draggable(value);
}
type(): string {
return "Cloud";
}
getColor(): string {
return this._ref.stroke() as string;
}
setColor(hex: string): void {
this._ref.stroke(hex);
}
getRotation(): number {
return this._ref.rotation();
}
setRotation(degrees: number): void {
this._ref.rotation(degrees);
}
getZIndex(): number {
return this._ref.zIndex();
}
setZIndex(zIndex: number): void {
this._ref.zIndex(zIndex);
}
delete() {
this._ref.destroy();
this._ref = null;
}
getPosition() {
return this._ref.position();
}
setPosition(x: number, y: number): void {
this._ref.position({ x, y });
}
getWidth(): number {
return this._ref.width();
}
setWidth(w: number): void {
this._ref.width(w);
}
getHeigth(): number {
return this._ref.height();
}
setHeight(h: number): void {
this._ref.height(h);
}
getLineWidth(): number {
return this._ref.strokeWidth();
}
setLineWidth(size: number): void {
this._ref.strokeWidth(size);
}
}