@inweb/markup
Version:
JavaScript 2D markups
188 lines (150 loc) • 6.05 kB
text/typescript
import Konva from "konva";
import { IMarkupImage, IMarkupImageParams } from "../IMarkupImage";
export class KonvaImage implements IMarkupImage {
private _ref: Konva.Image;
private _canvasImage: HTMLImageElement;
private _ratio = 1;
private readonly EPSILON = 10e-6;
private readonly BASE64_HEADER_START = "data:image/";
private readonly BASE64_NOT_FOUND =
"data:image/jpeg;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAADsAAAA7AF5KHG9AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAmhJREFUWIXtlr9rVEEQxz+H8RQUJIdeIopYm0vkCg0GBBtbG1NF7Kxt7dR/IGIw/uhTaBNLERURg2kCEUyCYCPi70b0InjGS57FzOZN3r19d+9HJIVfWO52dma/s7Mz8xa2KAaBCWAR+AkECWOmSOIdwC1gtQOpHc+NfQ8wClQ8+1d0vcdH/lQ3bSIRGAZ2pTjAqNovANXIWlXlAXA2zvi2Ln4AjqYgtagYEutENSLvjRoOImFv5iB32Ae8UrLXwFBk3h9ndF0VJnKSO9gTu3yKu5Z1LKnS8YIcABgw5Ks692JZFXcXRJ46Aq6kikCnHNi/mQ50WwVtfaIoBzL3gRk2drSscJ2wrc4VvUoe2wn/41/iBfoVLRnBGnDSY3AAKacy8AmYR+o7K1zCl6wgrgpOAc/MuhvfgMuk+1JGHQgSBcAloKXy78AjYBppJk5/noTulseBMZ23iD/piHFkEdgTQzKk+5wHjmHC3cmBg0BD5xcSTrFXyQPgIWFtDwMvab+2N8DpbhyY1v/3E8gdDgNfVX9SCVZ0/gW4B0wB71S2BpxLcuCM/jaQSHSDEeAX4VMuAG4gTzyHbcAVXXO6GxxwIX+vvxe7JHcYQ07nHqklj96UIW/YhSWzMKcep8VVtf8B1Dw6h4DfhB+sdbgn2R+gnoEc5NR3dZ+3QJ9H74HqXLPCGlJyTfI9y3YCs0owq3OLOpKkLeBI1HhSDT/mdKIPiUCARMTlQx34TMLjtww8IczmO8AJ/N/2JNSQXAiQ671JePePge0+wzJSQq4FFzlaenIvucUAkiQLhC/mLGNZ9xgn5s63BP4CCk0QDtm4BhoAAAAASUVORK5CYII=";
constructor(params: IMarkupImageParams, ref = null) {
if (ref) {
if (!ref.src || !ref.src.startsWith(this.BASE64_HEADER_START)) ref.src = this.BASE64_NOT_FOUND;
if (ref.height() <= this.EPSILON) ref.height(32);
if (ref.width() <= this.EPSILON) ref.width(32);
this._ref = ref;
this._canvasImage = ref.image();
this._ratio =
this._ref.height() <= this.EPSILON || this._ref.width() <= this.EPSILON
? 1
: this._ref.height() / this._ref.width();
return;
}
if (!params) params = {};
if (!params.position) params.position = { x: 0, y: 0 };
if (!params.src || !params.src.startsWith(this.BASE64_HEADER_START)) params.src = this.BASE64_NOT_FOUND;
this._canvasImage = new Image();
this._canvasImage.onload = () => {
this._ref.image(this._canvasImage);
if (this._ref.height() <= this.EPSILON) this._ref.height(this._canvasImage.height);
if (this._ref.width() <= this.EPSILON) this._ref.width(this._canvasImage.width);
this._ratio =
this._ref.height() <= this.EPSILON || this._ref.width() <= this.EPSILON
? 1
: this._ref.height() / this._ref.width();
// need to rescale only if input width and height are 0 - we do not loading Viewpoint with existing params
if (
(params.width <= this.EPSILON || params.height <= this.EPSILON) &&
(params.maxWidth >= this.EPSILON || params.maxWidth >= this.EPSILON)
) {
const heightOutOfCanvas = params.maxHeight - this._canvasImage.height;
const widthOutOfCanvas = params.maxWidth - this._canvasImage.width;
if (heightOutOfCanvas <= this.EPSILON || widthOutOfCanvas <= this.EPSILON) {
if (widthOutOfCanvas <= this.EPSILON && widthOutOfCanvas < heightOutOfCanvas / this._ratio) {
this._ref.height(params.maxWidth * this._ratio);
this._ref.width(params.maxWidth);
} else {
this._ref.width(params.maxHeight / this._ratio);
this._ref.height(params.maxHeight);
}
}
}
};
this._canvasImage.onerror = () => {
this._canvasImage.onerror = function () {};
this._canvasImage.src = this.BASE64_NOT_FOUND;
};
this._canvasImage.src = params.src;
this._ref = new Konva.Image({
x: params.position.x,
y: params.position.y,
image: this._canvasImage,
width: params.width ?? 0,
height: params.height ?? 0,
draggable: true,
});
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;
if (e.evt.ctrlKey || e.evt.shiftKey) {
if (scaleByX) {
this._ref.width(newWidth);
this._ref.height(newWidth * this._ratio);
} else {
this._ref.width(newHeight / this._ratio);
this._ref.height(newHeight);
}
} else {
if (scaleByX) {
this._ref.width(newWidth);
}
if (scaleByY) {
this._ref.height(newHeight);
}
}
this._ref.scale({ x: 1, y: 1 });
});
this._ref.id(this._ref._id.toString());
}
getSrc(): string {
return this._canvasImage.src;
}
setSrc(src: any) {
this._canvasImage.src = src;
}
getWidth(): number {
return this._ref.width();
}
setWidth(w: number): void {
this._ref.width(w);
this._ref.height(w * this._ratio);
}
getHeight(): number {
return this._ref.height();
}
setHeight(h: number): void {
this._ref.height(h);
this._ref.width(h / this._ratio);
}
ref() {
return this._ref;
}
id(): string {
return this._ref.id();
}
enableMouseEditing(value: boolean): void {
this._ref.draggable(value);
}
type(): string {
return "Image";
}
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(): { x: number; y: number } {
return this._ref.getPosition();
}
setPosition(x: number, y: number): void {
this._ref.setPosition({ x, y });
}
}