@paperbits/core
Version:
Paperbits core components.
147 lines (121 loc) • 5.04 kB
text/typescript
import * as ko from "knockout";
import * as Cropper from "cropperjs";
import template from "./cropper.html";
import { Component, Param } from "@paperbits/common/ko/decorators";
import { EventManager } from "@paperbits/common/events";
import { IMediaService } from "@paperbits/common/media";
import { ViewManager } from "@paperbits/common/ui";
import { MediaItem } from "../media/ko/mediaItem";
export class CropperBindingHandler {
constructor(eventManager: EventManager) {
ko.bindingHandlers["cropper"] = {
init: (imageElement: HTMLImageElement, valueAccessor) => {
const observable = valueAccessor();
const cropperInstance = new (<any>Cropper)(imageElement, {
// aspectRatio: 1,
viewMode: 1,
responsive: true,
ready: () => {
this["croppable"] = true;
}
});
observable(cropperInstance);
const onResize = () => {
cropperInstance["resize"]();
};
eventManager.addEventListener("onEditorResize", onResize);
ko.utils.domNodeDisposal.addDisposeCallback(imageElement, () => {
eventManager.removeEventListener("onEditorResize", onResize);
});
}
};
}
}
export class PictureCropper {
public cropperInstance: ko.Observable<any>;
public readonly mediaItem: MediaItem;
constructor(
private readonly mediaService: IMediaService,
private readonly viewManager: ViewManager
) {
this.setMoveMode = this.setMoveMode.bind(this);
this.setCropMode = this.setCropMode.bind(this);
this.zoomIn = this.zoomIn.bind(this);
this.zoomOut = this.zoomOut.bind(this);
this.rotateLeft = this.rotateLeft.bind(this);
this.rotateRight = this.rotateRight.bind(this);
this.flipHorizontally = this.flipHorizontally.bind(this);
this.flipVertically = this.flipVertically.bind(this);
this.crop = this.crop.bind(this);
this.clear = this.clear.bind(this);
this.cropperInstance = ko.observable(null);
}
public getRoundedCanvas(sourceCanvas: HTMLCanvasElement): HTMLCanvasElement {
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
const width = sourceCanvas.width;
const height = sourceCanvas.height;
canvas.width = width;
canvas.height = height;
context.imageSmoothingEnabled = true;
context.drawImage(sourceCanvas, 0, 0, width, height);
context.globalCompositeOperation = "destination-in";
context.beginPath();
context.arc(width / 2, height / 2, Math.min(width, height) / 2, 0, 2 * Math.PI, true);
context.fill();
return canvas;
}
public setMoveMode(): void {
this.cropperInstance().setDragMode("move");
}
public setCropMode(): void {
this.cropperInstance().setDragMode("crop");
}
public zoomIn(): void {
this.cropperInstance().zoom(0.1);
}
public zoomOut(): void {
this.cropperInstance().zoom(-0.1);
}
public rotateLeft(): void {
this.cropperInstance().rotate(-45);
}
public rotateRight(): void {
this.cropperInstance().rotate(45);
}
public flipHorizontally(): void {
this.cropperInstance().scaleX(-this.cropperInstance().imageData.scaleX);
}
public flipVertically(): void {
this.cropperInstance().scaleY(-this.cropperInstance().imageData.scaleY);
}
public crop(): void {
const cropper = this.cropperInstance();
const canvas = <HTMLCanvasElement>cropper.getCroppedCanvas();
canvas.toBlob(async (blob) => {
const reader = new FileReader();
reader.addEventListener("loadend", async () => {
const arrayBuffer = <ArrayBuffer>reader.result;
await this.updateMediaContent(new Uint8Array(arrayBuffer));
});
reader.readAsArrayBuffer(blob);
});
}
private async updateMediaContent(content: Uint8Array): Promise<void> {
const uploadPromise = this.mediaService.updateMediaContent(this.mediaItem.toMedia(), content);
uploadPromise.then(updatedItem => {
this.mediaItem.downloadUrl(updatedItem.downloadUrl);
const cropper = this.cropperInstance();
cropper.replace(updatedItem.downloadUrl);
});
await this.viewManager.notifyProgress(uploadPromise, "Media library", `Updating ${this.mediaItem.fileName()}...`);
}
public clear(): void {
this.cropperInstance().clear();
}
}