-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathDrawTextField.ts
108 lines (92 loc) · 3.62 KB
/
DrawTextField.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import { IDimensions, IPoint } from '../classes';
import { getContext2dOrThrow } from '../dom/getContext2dOrThrow';
import { resolveInput } from '../dom/resolveInput';
export enum AnchorPosition {
TOP_LEFT = 'TOP_LEFT',
TOP_RIGHT = 'TOP_RIGHT',
BOTTOM_LEFT = 'BOTTOM_LEFT',
BOTTOM_RIGHT = 'BOTTOM_RIGHT'
}
export interface IDrawTextFieldOptions {
anchorPosition?: AnchorPosition
backgroundColor?: string
fontColor?: string
fontSize?: number
fontStyle?: string
padding?: number
}
export class DrawTextFieldOptions implements IDrawTextFieldOptions {
public anchorPosition: AnchorPosition
public backgroundColor: string
public fontColor: string
public fontSize: number
public fontStyle: string
public padding: number
constructor(options: IDrawTextFieldOptions = {}) {
const { anchorPosition, backgroundColor, fontColor, fontSize, fontStyle, padding } = options
this.anchorPosition = anchorPosition || AnchorPosition.TOP_LEFT
this.backgroundColor = backgroundColor || 'rgba(0, 0, 0, 0.5)'
this.fontColor = fontColor || 'rgba(255, 255, 255, 1)'
this.fontSize = fontSize || 14
this.fontStyle = fontStyle || 'Georgia'
this.padding = padding || 4
}
}
export class DrawTextField {
public text: string[]
public anchor : IPoint
public options: DrawTextFieldOptions
constructor(
text: string | string[] | DrawTextField,
anchor: IPoint,
options: IDrawTextFieldOptions = {}
) {
this.text = typeof text === 'string'
? [text]
: (text instanceof DrawTextField ? text.text : text)
this.anchor = anchor
this.options = new DrawTextFieldOptions(options)
}
measureWidth(ctx: CanvasRenderingContext2D): number {
const { padding } = this.options
return this.text.map(l => ctx.measureText(l).width).reduce((w0, w1) => w0 < w1 ? w1 : w0, 0) + (2 * padding)
}
measureHeight(): number {
const { fontSize, padding } = this.options
return this.text.length * fontSize + (2 * padding)
}
getUpperLeft(ctx: CanvasRenderingContext2D, canvasDims?: IDimensions): IPoint {
const { anchorPosition } = this.options
const isShiftLeft = anchorPosition === AnchorPosition.BOTTOM_RIGHT || anchorPosition === AnchorPosition.TOP_RIGHT
const isShiftTop = anchorPosition === AnchorPosition.BOTTOM_LEFT || anchorPosition === AnchorPosition.BOTTOM_RIGHT
const textFieldWidth = this.measureWidth(ctx)
const textFieldHeight = this.measureHeight()
const x = (isShiftLeft ? this.anchor.x - textFieldWidth : this.anchor.x)
const y = isShiftTop ? this.anchor.y - textFieldHeight : this.anchor.y
// adjust anchor if text box exceeds canvas borders
if (canvasDims) {
const { width, height } = canvasDims
const newX = Math.max(Math.min(x, width - textFieldWidth), 0)
const newY = Math.max(Math.min(y, height - textFieldHeight), 0)
return { x: newX, y: newY }
}
return { x, y }
}
draw(canvasArg: string | HTMLCanvasElement | CanvasRenderingContext2D) {
const canvas = resolveInput(canvasArg)
const ctx = getContext2dOrThrow(canvas)
const { backgroundColor, fontColor, fontSize, fontStyle, padding } = this.options
ctx.font = `${fontSize}px ${fontStyle}`
const maxTextWidth = this.measureWidth(ctx)
const textHeight = this.measureHeight()
ctx.fillStyle = backgroundColor
const upperLeft = this.getUpperLeft(ctx, canvas)
ctx.fillRect(upperLeft.x, upperLeft.y, maxTextWidth, textHeight)
ctx.fillStyle = fontColor;
this.text.forEach((textLine, i) => {
const x = padding + upperLeft.x
const y = padding + upperLeft.y + ((i + 1) * fontSize)
ctx.fillText(textLine, x, y)
})
}
}