image-resizer-cropper
Version:
A simple image crop with resizing built for Angular 7, compatible with Angular Universal (SSR).
460 lines (454 loc) • 16.8 kB
JavaScript
import { Component, Input, ElementRef, Renderer2, Inject, PLATFORM_ID, Output, EventEmitter, NgModule } from '@angular/core';
import { isPlatformBrowser, CommonModule } from '@angular/common';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class ImageResizerCropperComponent {
/**
* @param {?} _el
* @param {?} _renderer
* @param {?} platformId
*/
constructor(_el, _renderer, platformId) {
this._el = _el;
this._renderer = _renderer;
this.platformId = platformId;
this.croppedImage = new EventEmitter(null);
this.error = new EventEmitter(null);
this.isDown = false;
this.offSet = [];
this.isBrowser = isPlatformBrowser(this.platformId);
if (this.isBrowser) {
this.canvasOne = this._renderer.createElement("canvas");
this.contextOne = this.canvasOne.getContext("2d");
this.canvasTwo = this._renderer.createElement("canvas");
this.contextTwo = this.canvasTwo.getContext("2d");
this.canvasThree = this._renderer.createElement("canvas");
this.contextThree = this.canvasThree.getContext("2d");
this.canvasFour = this._renderer.createElement("canvas");
this.contextFour = this.canvasFour.getContext("2d");
this.imgOne = this._renderer.createElement("img");
this.imgTwo = this._renderer.createElement("img");
}
}
/**
* @return {?}
*/
get crContainer() {
return this._el.nativeElement.querySelector(".cr-container");
}
/**
* @return {?}
*/
get crRange() {
return this._el.nativeElement.querySelector("#cr-range");
}
/**
* @return {?}
*/
get crImage() {
return this._el.nativeElement.querySelector("#cr-image");
}
/**
* @return {?}
*/
get crLens() {
return this._el.nativeElement.querySelector("#cr-lens");
}
/**
* @return {?}
*/
get btn() {
return this._el.nativeElement.querySelector("#crop");
}
/**
* @return {?}
*/
get testImg() {
return this._el.nativeElement.querySelector("#test-img");
}
/**
* @return {?}
*/
get finalCrop() {
return this._el.nativeElement.querySelector("#final-crop");
}
/**
* @return {?}
*/
get fileUpload() {
return this._el.nativeElement.querySelector("#file-upload");
}
/**
* @return {?}
*/
ngOnInit() {
if (this.sourceImage) {
if (this.sourceImage.target && this.sourceImage.target.files) {
this.parseFile(this.sourceImage);
}
}
}
/**
* @return {?}
*/
ngAfterViewInit() {
if (this.sourceImage) {
if (this.imgOne) {
this._renderer.setProperty(this.imgOne, "src", this.crImage.getAttribute("src"));
}
this.setUpListeners();
this.setUpConfigurations();
}
}
/**
* @param {?} changes
* @return {?}
*/
ngOnChanges(changes) {
if (changes["sourceImage"] &&
!changes["sourceImage"].firstChange &&
changes["sourceImage"].currentValue) {
this.clearImagePosition();
if (changes["sourceImage"].currentValue.target &&
changes["sourceImage"].currentValue.target.files) {
this.parseFile(this.sourceImage);
}
else {
if (this.imgOne) {
this._renderer.setProperty(this.imgOne, "src", this.crImage.getAttribute("src"));
this.setUpListeners();
this.setUpConfigurations();
}
}
}
}
/**
* @return {?}
*/
setUpListeners() {
if (this.isBrowser) {
this._renderer.listen(this.crRange, "input", event => {
this.linkImageToRangeInput(event.target.value);
});
this._renderer.listen(this.crImage, "mousedown", event => {
this.mouseDownOnImage(event);
});
this._renderer.listen(this.crImage, "mouseup", event => {
this.mouseUpOnImage();
if (this.autoCrop) {
this.resizeAndCropImage();
}
});
this._renderer.listen(this.crImage, "mousemove", event => {
event.preventDefault();
this.mouseMoveOnImage(event);
});
this._renderer.listen(this.crContainer, "mouseout", event => {
this.keepMouseMoveInsideContainer();
});
this._renderer.listen(this.btn, "click", event => {
this.resizeAndCropImage();
});
}
}
/**
* @return {?}
*/
setUpConfigurations() {
this.setBorderColor();
this.setBackgroundOpacity();
this.setLensHeightAndWidth();
}
/**
* @param {?} value
* @return {?}
*/
linkImageToRangeInput(value) {
/** @type {?} */
const val = value / 100;
this.crImage.style.transform = `scale(${val})`;
}
/**
* @param {?} event
* @return {?}
*/
mouseDownOnImage(event) {
this.isDown = true;
this.offSet = [
event.target.offsetLeft - event.clientX,
event.target.offsetTop - event.clientY
];
}
/**
* @return {?}
*/
mouseUpOnImage() {
this.isDown = false;
}
/**
* @param {?} event
* @return {?}
*/
mouseMoveOnImage(event) {
if (this.isDown) {
/** @type {?} */
const mousePosition = {
x: event.clientX,
y: event.clientY
};
this.crImage.style.left = mousePosition.x + this.offSet[0] + "px";
this.crImage.style.top = mousePosition.y + this.offSet[1] + "px";
}
}
/**
* @return {?}
*/
keepMouseMoveInsideContainer() {
this.isDown = false;
}
/**
* @param {?} e1
* @param {?} e2
* @return {?}
*/
checkForOverlap(e1, e2) {
if (e1.length && e1.length > 1) {
e1 = e1[0];
}
if (e2.length && e2.length > 1) {
e2 = e2[0];
}
/** @type {?} */
const rect1 = e1 instanceof Element ? e1.getBoundingClientRect() : false;
/** @type {?} */
const rect2 = e2 instanceof Element ? e2.getBoundingClientRect() : false;
/** @type {?} */
let overlap = null;
if (rect1 && rect2) {
overlap = !(rect1.right < rect2.left ||
rect1.left > rect2.right ||
rect1.bottom < rect2.top ||
rect1.top > rect2.bottom);
return [overlap, rect1, rect2];
}
else {
return [overlap, rect1, rect2];
}
}
/**
* @return {?}
*/
getResizeRatio() {
/** @type {?} */
const transform = this.crImage.style.transform;
/** @type {?} */
const matrix = transform
.substring(transform.indexOf("(") + 1, transform.indexOf(")"))
.split(",");
return matrix.length > 0 ? matrix[0] : null;
}
/**
* @return {?}
*/
drawImageResize() {
/** @type {?} */
const ratio = this.getResizeRatio();
this.canvasTwo.width = this.imgOne.width;
this.canvasTwo.height = this.imgOne.height;
this.contextTwo.drawImage(this.imgOne, 0, 0);
this.canvasOne.width = this.imgOne.width * ratio;
this.canvasOne.height = this.imgOne.height * ratio;
this.contextOne.drawImage(this.canvasTwo, 0, 0, this.canvasTwo.width, this.canvasTwo.height, 0, 0, this.canvasOne.width, this.canvasOne.height);
/** @type {?} */
const i = this.canvasOne.toDataURL("image/png", 100);
if (this.testImg) {
this._renderer.setProperty(this.testImg, "src", i);
}
return i;
}
/**
* @param {?} src
* @return {?}
*/
drawImageCrop(src) {
/** @type {?} */
const overlap = this.checkForOverlap(this.crLens, this.crImage);
if (this.imgTwo) {
this._renderer.setProperty(this.imgTwo, "src", src);
return new Promise((resolve, reject) => {
this._renderer.listen(this.imgTwo, "load", event => {
this.canvasThree.width = event.target.width;
this.canvasThree.height = event.target.height;
this.contextThree.drawImage(this.imgTwo, 0, 0);
this.canvasFour.width = this.lensWidth;
this.canvasFour.height = this.lensHeight;
if (overlap) {
/** @type {?} */
const xCrop = overlap[1]["x"] - overlap[2]["x"];
/** @type {?} */
const yCrop = overlap[1]["y"] - overlap[2]["y"];
this.contextFour.drawImage(this.canvasThree, xCrop, yCrop, this.lensWidth, this.lensHeight, 0, 0, this.lensWidth, this.lensHeight);
if (this.roundCrop) {
this.contextFour.globalCompositeOperation = "destination-in";
this.contextFour.beginPath();
this.contextFour.arc(this.lensWidth / 2, this.lensHeight / 2, this.lensHeight / 2, 0, Math.PI * 2);
this.contextFour.closePath();
this.contextFour.fill();
}
/** @type {?} */
const b = this.canvasFour.toDataURL("image/png", 100);
this.cropAvailable = true;
setTimeout(() => {
if (this.finalCrop) {
this._renderer.setProperty(this.finalCrop, "src", b);
}
});
resolve(b);
}
});
});
}
}
/**
* @return {?}
*/
resizeAndCropImage() {
/** @type {?} */
const imgSrc = this.drawImageResize();
this.drawImageCrop(imgSrc).then(result => {
this.croppedImage.emit(result);
console.log(result);
});
}
/**
* @return {?}
*/
setLensHeightAndWidth() {
if (this.lensHeight && this.lensHeight !== 150) {
this._renderer.setStyle(this.crLens, "height", this.lensHeight + "px");
}
if (this.lensWidth && this.lensWidth !== 150) {
this._renderer.setStyle(this.crLens, "width", this.lensWidth + "px");
}
if (this.roundCrop) {
this._renderer.addClass(this.crLens, "rounded");
}
}
/**
* @return {?}
*/
setBorderColor() {
if (this.borderColor) {
this._renderer.setStyle(this.crContainer, "border-color", this.borderColor);
this._renderer.setStyle(this.crLens, "border-color", this.borderColor);
/** @type {?} */
const spans = this.crLens.querySelectorAll("span");
spans.forEach(element => {
this._renderer.setStyle(element, "border-color", this.borderColor);
});
}
}
/**
* @return {?}
*/
setBackgroundOpacity() {
if (this.backgroundOpacity && this.crLens) {
this._renderer.setStyle(this.crLens, "box-shadow", "0 0 0 200000px rgba(0, 0, 0, " + this.backgroundOpacity + ")");
}
}
/**
* @param {?} event
* @return {?}
*/
parseFile(event) {
/** @type {?} */
const file = event.target.files[0];
if (file.type === "image/png" ||
file.type === "image/jpeg" ||
file.type === "image/gif" ||
file.type === "image/tiff") {
/** @type {?} */
const reader = new FileReader();
reader.readAsDataURL(file);
reader.addEventListener("load", e => {
/** @type {?} */
const val = reader.result.toString();
this._renderer.setProperty(this.crImage, "src", val);
this.loadedSourceImage = this.crImage.getAttribute("src");
this._renderer.setProperty(this.imgOne, "src", this.crImage.getAttribute("src"));
this.setUpListeners();
this.setUpConfigurations();
});
reader.onerror = function () {
console.log("there are some problems");
};
}
else {
this.loadedSourceImage = null;
if (this.crContainer) {
this._renderer.addClass(this.crContainer, "error");
}
this.errorMessage = "Invalid file type.";
this.error.emit(this.errorMessage);
}
}
/**
* @return {?}
*/
clearImagePosition() {
if (this.crImage) {
this.crImage.style.left = "auto";
this.crImage.style.top = "auto";
}
}
}
ImageResizerCropperComponent.decorators = [
{ type: Component, args: [{
selector: "irc-image-resizer-cropper",
template: "<ng-container *ngIf=\"sourceImage\">\n <div class=\"cr-container\">\n <div id=\"cr-lens\">\n <span></span>\n <span></span>\n <span></span>\n <span></span>\n </div>\n <img\n src=\"{{ loadedSourceImage }}\"\n style=\"transform: scale(1);\"\n draggable=\"false\"\n id=\"cr-image\"\n />\n <div *ngIf=\"errorMessage\" id=\"error-message\">{{ errorMessage }}</div>\n </div>\n <div class=\"cr-range-selector\">\n <input type=\"range\" id=\"cr-range\" min=\"10\" max=\"200\" />\n </div>\n <div class=\"text-center\">\n <button id=\"crop\" type=\"button\">Crop</button>\n </div>\n <div>\n <img src=\"#\" id=\"test-img\" style=\"display:none;\" />\n <img src=\"#\" *ngIf=\"previewCrop && cropAvailable\" id=\"final-crop\" />\n </div>\n</ng-container>\n",
styles: [".text-center{text-align:center}.cr-container{display:-webkit-box;display:flex;-webkit-box-align:center;align-items:center;-webkit-box-pack:center;justify-content:center;position:relative;min-height:300px;margin:0 auto;overflow:hidden;border:2px dashed #333}.cr-range-selector{margin:1rem 0;text-align:center}#cr-lens{height:150px;width:150px;border:2px solid #333;z-index:999999;box-shadow:0 0 0 200000px rgba(0,0,0,.2);position:relative}#cr-lens.rounded{border-radius:50%}#cr-lens span{position:absolute;width:15px;height:15px}#cr-lens span:nth-child(1){top:-17px;left:-17px;border-right:2px dashed #333;border-bottom:2px dashed #333}#cr-lens span:nth-child(2){top:-17px;right:-17px;border-left:2px dashed #333;border-bottom:2px dashed #333}#cr-lens span:nth-child(3){bottom:-17px;right:-17px;border-left:2px dashed #333;border-top:2px dashed #333}#cr-lens span:nth-child(4){bottom:-17px;left:-17px;border-right:2px dashed #333;border-top:2px dashed #333}#cr-image{display:block;position:absolute;-webkit-transform-origin:center center;transform-origin:center center;cursor:move}"]
}] }
];
/** @nocollapse */
ImageResizerCropperComponent.ctorParameters = () => [
{ type: ElementRef },
{ type: Renderer2 },
{ type: Object, decorators: [{ type: Inject, args: [PLATFORM_ID,] }] }
];
ImageResizerCropperComponent.propDecorators = {
sourceImage: [{ type: Input }],
lensHeight: [{ type: Input }],
lensWidth: [{ type: Input }],
autoCrop: [{ type: Input }],
previewCrop: [{ type: Input }],
roundCrop: [{ type: Input }],
borderColor: [{ type: Input }],
backgroundOpacity: [{ type: Input }],
croppedImage: [{ type: Output }],
error: [{ type: Output }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class ImageResizerCropperModule {
}
ImageResizerCropperModule.decorators = [
{ type: NgModule, args: [{
imports: [CommonModule],
declarations: [ImageResizerCropperComponent],
exports: [ImageResizerCropperComponent]
},] }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
export { ImageResizerCropperComponent, ImageResizerCropperModule };
//# sourceMappingURL=image-resizer-cropper.js.map