+12
-2
@@ -1,3 +1,11 @@
|
||||
<!--
|
||||
Copyright since 2025 Mifos Initiative
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<!--
|
||||
Copyright since 2025 Mifos Initiative
|
||||
-->
|
||||
@@ -20,11 +28,13 @@
|
||||
|
||||
<div class="crop-workspace" [class.hidden]="!imageLoaded">
|
||||
<div class="crop-source">
|
||||
<canvas #sourceCanvas
|
||||
<canvas
|
||||
#sourceCanvas
|
||||
(mousedown)="onMouseDown($event)"
|
||||
(mousemove)="onMouseMove($event)"
|
||||
(mouseup)="onMouseUp()"
|
||||
(mouseleave)="onMouseUp()">
|
||||
(mouseleave)="onMouseUp()"
|
||||
>
|
||||
</canvas>
|
||||
<div class="crop-hint">Drag to move. Drag corner to resize.</div>
|
||||
</div>
|
||||
|
||||
+9
-1
@@ -1,3 +1,11 @@
|
||||
/**
|
||||
* Copyright since 2025 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copyright since 2025 Mifos Initiative
|
||||
*/
|
||||
@@ -158,7 +166,7 @@ $navy: #0f172a;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
@media (width <= 600px) {
|
||||
.crop-workspace {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
+55
-21
@@ -1,8 +1,12 @@
|
||||
/**
|
||||
* Copyright since 2025 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { Component, Inject, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
|
||||
import { Component, inject, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef, MatDialogContent, MatDialogActions } from '@angular/material/dialog';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
@@ -59,10 +63,8 @@ export class ImageCropDialogComponent implements AfterViewInit {
|
||||
|
||||
zoom = 1;
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<ImageCropDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: ImageCropData
|
||||
) {}
|
||||
dialogRef = inject(MatDialogRef<ImageCropDialogComponent>);
|
||||
data = inject<ImageCropData>(MAT_DIALOG_DATA);
|
||||
|
||||
ngAfterViewInit() {
|
||||
const reader = new FileReader();
|
||||
@@ -127,9 +129,14 @@ export class ImageCropDialogComponent implements AfterViewInit {
|
||||
ctx.clearRect(this.cropX, this.cropY, this.cropW, this.cropH);
|
||||
ctx.drawImage(
|
||||
this.img,
|
||||
this.cropX * this.scaleX, this.cropY * this.scaleY,
|
||||
this.cropW * this.scaleX, this.cropH * this.scaleY,
|
||||
this.cropX, this.cropY, this.cropW, this.cropH
|
||||
this.cropX * this.scaleX,
|
||||
this.cropY * this.scaleY,
|
||||
this.cropW * this.scaleX,
|
||||
this.cropH * this.scaleY,
|
||||
this.cropX,
|
||||
this.cropY,
|
||||
this.cropW,
|
||||
this.cropH
|
||||
);
|
||||
|
||||
// Crop border
|
||||
@@ -141,14 +148,31 @@ export class ImageCropDialogComponent implements AfterViewInit {
|
||||
const hs = 8;
|
||||
ctx.fillStyle = '#1353a0';
|
||||
const corners = [
|
||||
[this.cropX, this.cropY],
|
||||
[this.cropX + this.cropW, this.cropY],
|
||||
[this.cropX, this.cropY + this.cropH],
|
||||
[this.cropX + this.cropW, this.cropY + this.cropH]
|
||||
[
|
||||
this.cropX,
|
||||
this.cropY
|
||||
],
|
||||
[
|
||||
this.cropX + this.cropW,
|
||||
this.cropY
|
||||
],
|
||||
[
|
||||
this.cropX,
|
||||
this.cropY + this.cropH
|
||||
],
|
||||
[
|
||||
this.cropX + this.cropW,
|
||||
this.cropY + this.cropH
|
||||
]
|
||||
];
|
||||
corners.forEach(([x, y]) => {
|
||||
ctx.fillRect(x - hs / 2, y - hs / 2, hs, hs);
|
||||
});
|
||||
corners.forEach(
|
||||
([
|
||||
x,
|
||||
y
|
||||
]) => {
|
||||
ctx.fillRect(x - hs / 2, y - hs / 2, hs, hs);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private updatePreview() {
|
||||
@@ -163,9 +187,14 @@ export class ImageCropDialogComponent implements AfterViewInit {
|
||||
pCtx.clearRect(0, 0, pw, ph);
|
||||
pCtx.drawImage(
|
||||
this.img,
|
||||
this.cropX * this.scaleX, this.cropY * this.scaleY,
|
||||
this.cropW * this.scaleX, this.cropH * this.scaleY,
|
||||
0, 0, pw, ph
|
||||
this.cropX * this.scaleX,
|
||||
this.cropY * this.scaleY,
|
||||
this.cropW * this.scaleX,
|
||||
this.cropH * this.scaleY,
|
||||
0,
|
||||
0,
|
||||
pw,
|
||||
ph
|
||||
);
|
||||
}
|
||||
|
||||
@@ -229,9 +258,14 @@ export class ImageCropDialogComponent implements AfterViewInit {
|
||||
const ctx = offscreen.getContext('2d')!;
|
||||
ctx.drawImage(
|
||||
this.img,
|
||||
this.cropX * this.scaleX, this.cropY * this.scaleY,
|
||||
this.cropW * this.scaleX, this.cropH * this.scaleY,
|
||||
0, 0, offscreen.width, offscreen.height
|
||||
this.cropX * this.scaleX,
|
||||
this.cropY * this.scaleY,
|
||||
this.cropW * this.scaleX,
|
||||
this.cropH * this.scaleY,
|
||||
0,
|
||||
0,
|
||||
offscreen.width,
|
||||
offscreen.height
|
||||
);
|
||||
|
||||
offscreen.toBlob((blob) => {
|
||||
|
||||
Reference in New Issue
Block a user