Skip to content

Commit e3a2f47

Browse files
authored
At/text tool improvements (#9)
* Checking for touch event only when defined in browser * Adding multi-line support input => content-editable div * Floating-text-entry componentized * Providing radix parameter in font-string parsing Using projection factor when dragging text box * Using hidden input for textbox
1 parent fde2cf3 commit e3a2f47

12 files changed

+198
-81
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"scripts": {
55
"ng": "ng",
66
"start": "ng serve",
7-
"build": "ng build",
7+
"build": "ng build && cp LICENSE dist/ && cp README.md dist/",
88
"test": "ng test",
99
"lint": "ng lint",
1010
"e2e": "ng e2e"

projects/aia-lib/src/lib/aia-lib.module.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import { NgModule } from '@angular/core';
22
import { AiaImageAnnotatorComponent } from './components/aia-image-annotator/aia-image-annotator.component';
3+
import { FloatingTextEntryComponent } from './components/aia-image-annotator/floating-text-entry/floating-text-entry.component';
4+
import { FormsModule } from '@angular/forms';
35

46
@NgModule({
5-
declarations: [AiaImageAnnotatorComponent],
7+
declarations: [AiaImageAnnotatorComponent, FloatingTextEntryComponent],
68
imports: [
9+
FormsModule
710
],
811
exports: [AiaImageAnnotatorComponent]
912
})

projects/aia-lib/src/lib/components/aia-image-annotator/aia-image-annotator.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
<canvas class="image-canvas" #imageCanvas [width]="imageWidth" [height]="imageHeight" [style.width.px]="displayWidth" [style.height.px]="displayHeight"></canvas>
44
<canvas #drawingCanvas (touchstart)="touchStart($event)" (touchmove)="touchMove($event)" (touchend)="touchEnd($event)" (mousedown)="mouseDown($event)" (mousemove)="mouseMove($event)" (mouseup)="mouseUp($event)" [width]="imageWidth" [height]="imageHeight" [style.width.px]="displayWidth" [style.height.px]="displayHeight"></canvas>
55
<canvas #mergeCanvas class="merge-canvas" [width]="imageWidth" [height]="imageHeight"></canvas>
6-
<input (keyup)="keyUp($event)" #textBox autocomplete="off" class="floating-text-entry">
6+
<aia-floating-text-entry (mouseup)="mouseUp($event)" (touchend)="touchEnd($event)"></aia-floating-text-entry>
77
</div>
88
</div>

projects/aia-lib/src/lib/components/aia-image-annotator/aia-image-annotator.component.scss

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,5 @@
1717
display: none;
1818
}
1919
}
20-
.floating-text-entry {
21-
position: absolute;
22-
padding: 0;
23-
margin: 0;
24-
background-color: rgba(0,0,0,0);
25-
border: 0;
26-
}
2720
}
2821
}

projects/aia-lib/src/lib/components/aia-image-annotator/aia-image-annotator.component.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Component, OnInit, ViewChild, ElementRef, Input, OnChanges } from '@ang
22
import { DrawState, PencilState, TextState, DrawCommand, StateName, ClearCommand } from './helpers/draw-state.interface';
33
import { EventTranslator } from './helpers/event-translator';
44
import { DEFAULTS } from './helpers/defaults';
5+
import { FloatingTextEntryComponent } from './floating-text-entry/floating-text-entry.component';
56

67
@Component({
78
selector: 'aia-image-annotator',
@@ -37,7 +38,7 @@ export class AiaImageAnnotatorComponent implements OnInit, OnChanges {
3738
@ViewChild('imageCanvas') private imageCanvasRef: ElementRef;
3839
@ViewChild('drawingCanvas') private drawingCanvasRef: ElementRef;
3940
@ViewChild('mergeCanvas') private mergeCanvasRef: ElementRef;
40-
@ViewChild('textBox') textBoxRef: ElementRef;
41+
@ViewChild(FloatingTextEntryComponent) textEntry: FloatingTextEntryComponent;
4142

4243
public imageWidth = 0;
4344
public imageHeight = 0;
@@ -219,20 +220,20 @@ export class AiaImageAnnotatorComponent implements OnInit, OnChanges {
219220
* Sets the color and the font
220221
*/
221222
private setColorAndFont(color: string, fontSize: string, fontFamily: string) {
222-
if (!this.drawingCtx || !this.textBoxRef) {
223+
if (!this.drawingCtx) {
223224
return;
224225
}
225226

226227
this.drawingCtx.strokeStyle = color || DEFAULTS.color;
227228
this.drawingCtx.fillStyle = color || DEFAULTS.color;
228-
this.textBoxRef.nativeElement.style.color = color || DEFAULTS.color;
229+
this.textEntry.setColor(color || DEFAULTS.color);
229230

230231
const fontString = `${fontSize || DEFAULTS.fontSize} ${fontFamily || DEFAULTS.fontFamily}`;
231232
this.drawingCtx.font = fontString;
232233

233234
const fontParts = fontSize.match(/(.*)(px|pt)/);
234-
const adjustedFontSize = Math.floor(parseInt(fontParts[1]) / this.projectionFactor) + fontParts[2];
235-
this.textBoxRef.nativeElement.style.font = `${adjustedFontSize || DEFAULTS.fontSize} ${fontFamily || DEFAULTS.fontFamily}`;
235+
const adjustedFontSize = Math.floor(parseInt(fontParts[1], 10) / this.projectionFactor) + fontParts[2];
236+
this.textEntry.setFont(`${adjustedFontSize || DEFAULTS.fontSize} ${fontFamily || DEFAULTS.fontFamily}`);
236237
}
237238

238239

@@ -295,7 +296,4 @@ export class AiaImageAnnotatorComponent implements OnInit, OnChanges {
295296
this._state.contactEnd(this, contactEvent);
296297
}
297298

298-
public keyUp(ev: KeyboardEvent) {
299-
this._state.keyUp(this, ev);
300-
}
301299
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<div #textBox class="floating-text-entry">
2+
<textarea #input [(ngModel)]="currentText"></textarea>
3+
<p>{{currentText}}&nbsp;</p>
4+
</div>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
.floating-text-entry {
2+
display: none;
3+
position: absolute;
4+
padding: 0;
5+
margin: 0;
6+
background-color: rgba(0,0,0,0);
7+
border: 1px solid rgb(90, 165, 190);
8+
text-align: left;
9+
white-space: pre;
10+
11+
p {
12+
padding: 0;
13+
margin: 0;
14+
}
15+
16+
textarea {
17+
position: absolute;
18+
bottom: 0;
19+
right: 0;
20+
height: 0;
21+
width: 0;
22+
border: 0;
23+
padding: 0;
24+
background: rgba(0,0,0,0);
25+
resize: none;
26+
}
27+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { FloatingTextEntryComponent } from './floating-text-entry.component';
4+
5+
describe('FloatingTextEntryComponent', () => {
6+
let component: FloatingTextEntryComponent;
7+
let fixture: ComponentFixture<FloatingTextEntryComponent>;
8+
9+
beforeEach(async(() => {
10+
TestBed.configureTestingModule({
11+
declarations: [ FloatingTextEntryComponent ]
12+
})
13+
.compileComponents();
14+
}));
15+
16+
beforeEach(() => {
17+
fixture = TestBed.createComponent(FloatingTextEntryComponent);
18+
component = fixture.componentInstance;
19+
fixture.detectChanges();
20+
});
21+
22+
it('should create', () => {
23+
expect(component).toBeTruthy();
24+
});
25+
});
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { Component, ViewChild, ElementRef, OnInit } from '@angular/core';
2+
3+
@Component({
4+
selector: 'aia-floating-text-entry',
5+
templateUrl: './floating-text-entry.component.html',
6+
styleUrls: ['./floating-text-entry.component.scss']
7+
})
8+
export class FloatingTextEntryComponent {
9+
10+
public currentText = '';
11+
12+
@ViewChild('textBox') textBoxRef: ElementRef;
13+
@ViewChild('input') inputRef: ElementRef;
14+
15+
public getText(): string {
16+
return this.currentText;
17+
}
18+
19+
public hide(): void {
20+
this.textBoxRef.nativeElement.style.display = 'none';
21+
}
22+
23+
public show(): void {
24+
this.textBoxRef.nativeElement.style.display = 'block';
25+
}
26+
27+
public setFont(fontStr: string) {
28+
this.textBoxRef.nativeElement.style.font = fontStr;
29+
}
30+
31+
public setColor(color: string) {
32+
this.textBoxRef.nativeElement.style.color = color;
33+
}
34+
35+
public setPosition(x: number, y: number) {
36+
this.textBoxRef.nativeElement.style.top = y + 'px';
37+
this.textBoxRef.nativeElement.style.left = x + 'px';
38+
}
39+
40+
public isEmpty(): boolean {
41+
return this.currentText.trim() === '';
42+
}
43+
44+
public focus() {
45+
setTimeout(_ => {
46+
this.inputRef.nativeElement.focus();
47+
}, 0);
48+
}
49+
50+
public clear() {
51+
this.currentText = '';
52+
}
53+
54+
public onBlur(): Promise<any> {
55+
return new Promise<any>(resolve => {
56+
this.inputRef.nativeElement.addEventListener('blur', resolve);
57+
});
58+
}
59+
60+
public getLineHeight(): number {
61+
return this.getLineHeightHelper(this.textBoxRef.nativeElement);
62+
}
63+
64+
private getLineHeightHelper(el: HTMLDivElement): number {
65+
let temp = document.createElement(el.nodeName);
66+
temp.setAttribute('style', `margin:0px;padding:0px;font-family:${el.style.fontFamily};font-size:${el.style.fontSize}`);
67+
temp.innerHTML = 'test';
68+
temp = el.parentNode.appendChild(temp);
69+
const ret = temp.clientHeight;
70+
temp.parentNode.removeChild(temp);
71+
return ret;
72+
}
73+
74+
}

0 commit comments

Comments
 (0)