Commit 0dde6233b130601d55d785f8164e0771e91ec77e
1 parent
06477895
Improve dashboard image dialog - ability to set crop rectangle for dashboard screenshot
Showing
3 changed files
with
75 additions
and
6 deletions
... | ... | @@ -28,10 +28,32 @@ |
28 | 28 | <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async"> |
29 | 29 | </mat-progress-bar> |
30 | 30 | <div mat-dialog-content style="position: relative;"> |
31 | - <fieldset [disabled]="(isLoading$ | async) || (takingScreenshot$ | async)" fxLayout="column" fxLayoutAlign="center center" fxLayoutGap="16px"> | |
32 | - <div class="tb-image-preview-container"> | |
33 | - <div *ngIf="!safeImageUrl; else elseBlock">{{ 'dashboard.no-image' | translate }}</div> | |
34 | - <ng-template #elseBlock><img class="tb-image-preview" [src]="safeImageUrl" /></ng-template> | |
31 | + <fieldset [disabled]="(isLoading$ | async) || (takingScreenshot$ | async)" fxLayout="column" fxLayoutAlign="center center" fxLayoutGap="8px"> | |
32 | + <div [formGroup]="dashboardRectFormGroup" fxLayout="column" fxLayoutGap="8px" fxLayoutAlign="center center"> | |
33 | + <mat-form-field class="rect-field"> | |
34 | + <mat-label>Top %</mat-label> | |
35 | + <input matInput formControlName="top" type="number" step="1" min="0" max="100"> | |
36 | + </mat-form-field> | |
37 | + <div fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="center center"> | |
38 | + <mat-form-field class="rect-field"> | |
39 | + <mat-label>Left %</mat-label> | |
40 | + <input matInput formControlName="left" type="number" step="1" min="0" max="100"> | |
41 | + </mat-form-field> | |
42 | + <div class="tb-image-preview-container"> | |
43 | + <div *ngIf="!safeImageUrl; else elseBlock">{{ 'dashboard.no-image' | translate }}</div> | |
44 | + <ng-template #elseBlock><img class="tb-image-preview" [src]="safeImageUrl" /></ng-template> | |
45 | + </div> | |
46 | + <mat-form-field class="rect-field"> | |
47 | + <mat-label>Right %</mat-label> | |
48 | + <input matInput formControlName="right" type="number" step="1" min="0" max="100"> | |
49 | + </mat-form-field> | |
50 | + </div> | |
51 | + <mat-form-field class="rect-field"> | |
52 | + <mat-label>Bottom %</mat-label> | |
53 | + <input matInput formControlName="bottom" type="number" step="1" min="0" max="100"> | |
54 | + </mat-form-field> | |
55 | + </div> | |
56 | + <div [formGroup]="dashboardRectFormGroup" fxFlex fxLayout="row" fxLayoutGap="8px"> | |
35 | 57 | </div> |
36 | 58 | <button mat-raised-button color="accent" |
37 | 59 | type="button" | ... | ... |
... | ... | @@ -18,7 +18,7 @@ import { Component, Inject } from '@angular/core'; |
18 | 18 | import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; |
19 | 19 | import { Store } from '@ngrx/store'; |
20 | 20 | import { AppState } from '@core/core.state'; |
21 | -import { FormBuilder, FormGroup } from '@angular/forms'; | |
21 | +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; | |
22 | 22 | import { Router } from '@angular/router'; |
23 | 23 | import { DialogComponent } from '@app/shared/components/dialog.component'; |
24 | 24 | import { DashboardId } from '@shared/models/id/dashboard-id'; |
... | ... | @@ -27,6 +27,7 @@ import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; |
27 | 27 | import html2canvas from 'html2canvas'; |
28 | 28 | import { map, share } from 'rxjs/operators'; |
29 | 29 | import { BehaviorSubject, from } from 'rxjs'; |
30 | +import { isNumber } from '@core/utils'; | |
30 | 31 | |
31 | 32 | export interface DashboardImageDialogData { |
32 | 33 | dashboardId: DashboardId; |
... | ... | @@ -55,6 +56,7 @@ export class DashboardImageDialogComponent extends DialogComponent<DashboardImag |
55 | 56 | safeImageUrl?: SafeUrl; |
56 | 57 | dashboardElement: HTMLElement; |
57 | 58 | |
59 | + dashboardRectFormGroup: FormGroup; | |
58 | 60 | dashboardImageFormGroup: FormGroup; |
59 | 61 | |
60 | 62 | constructor(protected store: Store<AppState>, |
... | ... | @@ -69,6 +71,14 @@ export class DashboardImageDialogComponent extends DialogComponent<DashboardImag |
69 | 71 | this.dashboardId = this.data.dashboardId; |
70 | 72 | this.updateImage(this.data.currentImage); |
71 | 73 | this.dashboardElement = this.data.dashboardElement; |
74 | + const clientRect = this.dashboardElement.getBoundingClientRect(); | |
75 | + | |
76 | + this.dashboardRectFormGroup = this.fb.group({ | |
77 | + left: [0, [Validators.min(0), Validators.max(100)]], | |
78 | + top: [0, [Validators.min(0), Validators.max(100)]], | |
79 | + right: [100, [Validators.min(0), Validators.max(100)]], | |
80 | + bottom: [100, [Validators.min(0), Validators.max(100)]] | |
81 | + }); | |
72 | 82 | |
73 | 83 | this.dashboardImageFormGroup = this.fb.group({ |
74 | 84 | dashboardImage: [this.data.currentImage] |
... | ... | @@ -81,13 +91,45 @@ export class DashboardImageDialogComponent extends DialogComponent<DashboardImag |
81 | 91 | ); |
82 | 92 | } |
83 | 93 | |
94 | + private convertUserPercent(percent: any, defaultValue: number): number { | |
95 | + let result: number; | |
96 | + if (isNumber(percent)) { | |
97 | + result = Math.max(0, Math.min(100, percent)) / 100; | |
98 | + } else { | |
99 | + result = defaultValue; | |
100 | + } | |
101 | + return result; | |
102 | + } | |
103 | + | |
84 | 104 | takeScreenShot() { |
85 | 105 | this.takingScreenshotSubject.next(true); |
106 | + const rect = this.dashboardElement.getBoundingClientRect(); | |
107 | + | |
108 | + const leftVal = this.convertUserPercent(this.dashboardRectFormGroup.get('left').value, 0); | |
109 | + const topVal = this.convertUserPercent(this.dashboardRectFormGroup.get('top').value, 0); | |
110 | + const rightVal = this.convertUserPercent(this.dashboardRectFormGroup.get('right').value, 100); | |
111 | + const bottomVal = this.convertUserPercent(this.dashboardRectFormGroup.get('bottom').value, 100); | |
112 | + | |
113 | + const left = leftVal * rect.width; | |
114 | + const top = topVal * rect.height; | |
115 | + const right = rightVal * rect.width; | |
116 | + const bottom = bottomVal * rect.height; | |
117 | + | |
118 | + const x = rect.left + left; | |
119 | + const y = rect.top + top; | |
120 | + let width = right - left; | |
121 | + let height = bottom - top; | |
122 | + width = Math.max(1, width); | |
123 | + height = Math.max(1, height); | |
86 | 124 | from(html2canvas(this.dashboardElement, { |
87 | 125 | logging: false, |
88 | 126 | useCORS: true, |
89 | 127 | foreignObjectRendering: false, |
90 | - scale: 512 / this.dashboardElement.clientWidth | |
128 | + scale: 512 / width, | |
129 | + x, | |
130 | + y, | |
131 | + width, | |
132 | + height | |
91 | 133 | })).pipe( |
92 | 134 | map(canvas => canvas.toDataURL())).subscribe( |
93 | 135 | (image) => { | ... | ... |