0
# Text and Image Rendering
1
2
ZRender provides comprehensive text rendering capabilities with rich typography support and efficient bitmap image display. The text system supports multi-line text, rich formatting, alignment options, and advanced features like text truncation and custom styling.
3
4
## Text Rendering
5
6
### Text Class
7
8
The primary class for rendering text content:
9
10
```typescript { .api }
11
class Text extends Displayable {
12
constructor(opts: TextProps);
13
style: TextStyleProps;
14
}
15
16
interface TextProps extends DisplayableProps {
17
style?: TextStyleProps;
18
}
19
20
interface TextState {
21
style?: Partial<TextStyleProps>;
22
}
23
```
24
25
### Text Styling Properties
26
27
```typescript { .api }
28
interface TextStyleProps {
29
// Content and basic typography
30
text?: string;
31
fontSize?: number;
32
fontFamily?: string;
33
fontStyle?: 'normal' | 'italic' | 'oblique';
34
fontWeight?: string | number;
35
36
// Text positioning and alignment
37
textAlign?: 'left' | 'center' | 'right';
38
textVerticalAlign?: 'top' | 'middle' | 'bottom';
39
textBaseline?: 'top' | 'middle' | 'bottom' | 'alphabetic' | 'ideographic' | 'hanging';
40
41
// Visual appearance
42
fill?: string | LinearGradient | RadialGradient | Pattern;
43
stroke?: string;
44
lineWidth?: number;
45
opacity?: number;
46
47
// Text shadows
48
textShadowBlur?: number;
49
textShadowColor?: string;
50
textShadowOffsetX?: number;
51
textShadowOffsetY?: number;
52
53
// Layout and spacing
54
width?: number;
55
height?: number;
56
textPadding?: number | number[];
57
textLineHeight?: number;
58
59
// Rich text formatting
60
rich?: Record<string, TextStyleProps>;
61
62
// Text truncation
63
truncate?: {
64
outerWidth?: number;
65
outerHeight?: number;
66
ellipsis?: string;
67
placeholder?: string;
68
};
69
70
// Advanced styling
71
textBorderColor?: string;
72
textBorderWidth?: number;
73
textBackgroundColor?: string;
74
backgroundColor?: string;
75
borderColor?: string;
76
borderWidth?: number;
77
borderRadius?: number;
78
padding?: number | number[];
79
80
// Rendering effects
81
blend?: string;
82
}
83
```
84
85
### TSpan Class
86
87
Text span element for rich text components:
88
89
```typescript { .api }
90
class TSpan extends Displayable {
91
constructor(opts: TSpanProps);
92
style: TSpanStyleProps;
93
}
94
95
interface TSpanProps extends DisplayableProps {
96
style?: TSpanStyleProps;
97
}
98
99
interface TSpanStyleProps extends TextStyleProps {
100
x?: number;
101
y?: number;
102
text?: string;
103
}
104
105
interface TSpanState {
106
style?: Partial<TSpanStyleProps>;
107
}
108
```
109
110
## Image Rendering
111
112
### Image Class
113
114
Display bitmap images with complete control over positioning and effects:
115
116
```typescript { .api }
117
class Image extends Displayable {
118
constructor(opts: ImageProps);
119
style: ImageStyleProps;
120
}
121
122
interface ImageProps extends DisplayableProps {
123
style?: ImageStyleProps;
124
}
125
126
interface ImageState {
127
style?: Partial<ImageStyleProps>;
128
}
129
```
130
131
### Image Styling Properties
132
133
```typescript { .api }
134
interface ImageStyleProps {
135
// Image source
136
image?: string | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement;
137
138
// Positioning and sizing
139
x?: number;
140
y?: number;
141
width?: number;
142
height?: number;
143
144
// Source cropping (sprite/atlas support)
145
sx?: number; // Source x position
146
sy?: number; // Source y position
147
sWidth?: number; // Source width
148
sHeight?: number; // Source height
149
150
// Visual effects
151
opacity?: number;
152
shadowBlur?: number;
153
shadowColor?: string;
154
shadowOffsetX?: number;
155
shadowOffsetY?: number;
156
157
// Advanced rendering
158
blend?: string;
159
globalCompositeOperation?: string;
160
}
161
```
162
163
## Usage Examples
164
165
### Basic Text Rendering
166
167
```typescript
168
import { Text } from "zrender";
169
170
// Simple text
171
const simpleText = new Text({
172
style: {
173
text: 'Hello ZRender!',
174
fontSize: 24,
175
fontFamily: 'Arial, sans-serif',
176
fill: '#2d3436',
177
textAlign: 'center'
178
},
179
position: [200, 100]
180
});
181
182
// Styled text with shadow
183
const styledText = new Text({
184
style: {
185
text: 'Styled Text',
186
fontSize: 32,
187
fontWeight: 'bold',
188
fill: '#74b9ff',
189
stroke: '#0984e3',
190
lineWidth: 2,
191
textShadowBlur: 5,
192
textShadowColor: 'rgba(0, 0, 0, 0.3)',
193
textShadowOffsetX: 2,
194
textShadowOffsetY: 2
195
},
196
position: [200, 200]
197
});
198
199
zr.add(simpleText);
200
zr.add(styledText);
201
```
202
203
### Multi-line Text and Layout
204
205
```typescript
206
import { Text } from "zrender";
207
208
// Multi-line text with layout control
209
const multilineText = new Text({
210
style: {
211
text: 'This is a long text that will wrap to multiple lines when the width is constrained.',
212
fontSize: 16,
213
fontFamily: 'Arial, sans-serif',
214
fill: '#2d3436',
215
width: 250, // Constrain width for wrapping
216
textLineHeight: 20, // Line height
217
textAlign: 'left',
218
textVerticalAlign: 'top',
219
padding: [10, 15], // Internal padding
220
backgroundColor: '#f8f9fa',
221
borderColor: '#dee2e6',
222
borderWidth: 1,
223
borderRadius: 5
224
},
225
position: [50, 50]
226
});
227
228
// Text with specific alignment
229
const alignedText = new Text({
230
style: {
231
text: 'Center Aligned\nMultiple Lines\nOf Text',
232
fontSize: 18,
233
fill: '#6c757d',
234
textAlign: 'center',
235
textVerticalAlign: 'middle',
236
width: 200,
237
height: 100,
238
backgroundColor: '#e9ecef',
239
borderRadius: 8
240
},
241
position: [350, 50]
242
});
243
244
zr.add(multilineText);
245
zr.add(alignedText);
246
```
247
248
### Rich Text Formatting
249
250
```typescript
251
import { Text } from "zrender";
252
253
// Rich text with multiple styles
254
const richText = new Text({
255
style: {
256
text: '{title|ZRender Graphics}\n{subtitle|Powerful 2D Rendering}\n{body|Create beautiful graphics with ease}\n{link|Learn More →}',
257
rich: {
258
title: {
259
fontSize: 28,
260
fontWeight: 'bold',
261
fill: '#2d3436',
262
textAlign: 'center'
263
},
264
subtitle: {
265
fontSize: 16,
266
fill: '#74b9ff',
267
fontStyle: 'italic',
268
textAlign: 'center',
269
textPadding: [5, 0, 10, 0]
270
},
271
body: {
272
fontSize: 14,
273
fill: '#636e72',
274
textAlign: 'center',
275
textPadding: [0, 0, 15, 0]
276
},
277
link: {
278
fontSize: 14,
279
fill: '#00b894',
280
fontWeight: 'bold',
281
textAlign: 'center',
282
textBorderColor: '#00b894',
283
textBorderWidth: 1,
284
textPadding: [5, 10],
285
textBackgroundColor: 'rgba(0, 184, 148, 0.1)',
286
borderRadius: 3
287
}
288
},
289
width: 300,
290
textAlign: 'center'
291
},
292
position: [50, 250]
293
});
294
295
zr.add(richText);
296
```
297
298
### Text Truncation and Overflow
299
300
```typescript
301
import { Text } from "zrender";
302
303
// Text with ellipsis truncation
304
const truncatedText = new Text({
305
style: {
306
text: 'This is a very long text that will be truncated with ellipsis when it exceeds the specified width',
307
fontSize: 14,
308
fill: '#2d3436',
309
width: 200,
310
truncate: {
311
outerWidth: 200,
312
ellipsis: '...',
313
placeholder: 'No content'
314
}
315
},
316
position: [400, 250]
317
});
318
319
// Text with custom truncation
320
const customTruncatedText = new Text({
321
style: {
322
text: 'Another long text example',
323
fontSize: 16,
324
fill: '#e17055',
325
width: 150,
326
truncate: {
327
outerWidth: 150,
328
ellipsis: ' [more]',
329
placeholder: '[empty]'
330
}
331
},
332
position: [400, 300]
333
});
334
335
zr.add(truncatedText);
336
zr.add(customTruncatedText);
337
```
338
339
### Gradient and Pattern Text
340
341
```typescript
342
import { Text, LinearGradient, Pattern } from "zrender";
343
344
// Gradient fill text
345
const gradientText = new Text({
346
style: {
347
text: 'GRADIENT',
348
fontSize: 48,
349
fontWeight: 'bold',
350
fill: new LinearGradient(0, 0, 1, 0, [
351
{ offset: 0, color: '#ff7675' },
352
{ offset: 0.5, color: '#fd79a8' },
353
{ offset: 1, color: '#fdcb6e' }
354
]),
355
stroke: '#2d3436',
356
lineWidth: 2
357
},
358
position: [50, 400]
359
});
360
361
// Pattern fill text (if you have a pattern image)
362
const createTextPattern = () => {
363
const canvas = document.createElement('canvas');
364
canvas.width = 20;
365
canvas.height = 20;
366
const ctx = canvas.getContext('2d')!;
367
368
// Create diagonal stripes pattern
369
ctx.strokeStyle = '#74b9ff';
370
ctx.lineWidth = 2;
371
ctx.beginPath();
372
ctx.moveTo(0, 0);
373
ctx.lineTo(20, 20);
374
ctx.moveTo(0, 20);
375
ctx.lineTo(20, 0);
376
ctx.stroke();
377
378
return new Pattern(canvas, 'repeat');
379
};
380
381
const patternText = new Text({
382
style: {
383
text: 'PATTERN',
384
fontSize: 48,
385
fontWeight: 'bold',
386
fill: createTextPattern(),
387
stroke: '#0984e3',
388
lineWidth: 1
389
},
390
position: [350, 400]
391
});
392
393
zr.add(gradientText);
394
zr.add(patternText);
395
```
396
397
### Basic Image Display
398
399
```typescript
400
import { Image } from "zrender";
401
402
// Display image from URL
403
const imageFromUrl = new Image({
404
style: {
405
image: 'https://example.com/image.jpg',
406
x: 50,
407
y: 50,
408
width: 200,
409
height: 150
410
}
411
});
412
413
// Display image from HTMLImageElement
414
const createImageElement = (src: string) => {
415
const img = new Image();
416
img.src = src;
417
return img;
418
};
419
420
const imageFromElement = new Image({
421
style: {
422
image: createImageElement('path/to/image.png'),
423
x: 300,
424
y: 50,
425
width: 150,
426
height: 150,
427
opacity: 0.8
428
}
429
});
430
431
zr.add(imageFromUrl);
432
zr.add(imageFromElement);
433
```
434
435
### Image Effects and Transformations
436
437
```typescript
438
import { Image } from "zrender";
439
440
// Image with shadow effect
441
const shadowImage = new Image({
442
style: {
443
image: 'path/to/image.jpg',
444
x: 50,
445
y: 250,
446
width: 120,
447
height: 120,
448
shadowBlur: 15,
449
shadowColor: 'rgba(0, 0, 0, 0.4)',
450
shadowOffsetX: 5,
451
shadowOffsetY: 5
452
}
453
});
454
455
// Sprite/Atlas usage (cropping from larger image)
456
const spriteImage = new Image({
457
style: {
458
image: 'path/to/spritesheet.png',
459
x: 200,
460
y: 250,
461
width: 64,
462
height: 64,
463
// Crop from sprite sheet
464
sx: 128, // Source x in spritesheet
465
sy: 64, // Source y in spritesheet
466
sWidth: 64, // Source width
467
sHeight: 64 // Source height
468
}
469
});
470
471
// Animated image scaling
472
const scalingImage = new Image({
473
style: {
474
image: 'path/to/image.jpg',
475
x: 350,
476
y: 250,
477
width: 80,
478
height: 80
479
}
480
});
481
482
// Animate image scaling on hover
483
scalingImage.on('mouseover', () => {
484
scalingImage.animate('style')
485
.when(300, { width: 120, height: 120, x: 330, y: 230 })
486
.start('easeOutQuad');
487
});
488
489
scalingImage.on('mouseout', () => {
490
scalingImage.animate('style')
491
.when(300, { width: 80, height: 80, x: 350, y: 250 })
492
.start('easeOutQuad');
493
});
494
495
zr.add(shadowImage);
496
zr.add(spriteImage);
497
zr.add(scalingImage);
498
```
499
500
### Canvas and Video as Image Sources
501
502
```typescript
503
import { Image } from "zrender";
504
505
// Use canvas as image source
506
const createCanvasImage = () => {
507
const canvas = document.createElement('canvas');
508
canvas.width = 100;
509
canvas.height = 100;
510
const ctx = canvas.getContext('2d')!;
511
512
// Draw something on canvas
513
const gradient = ctx.createLinearGradient(0, 0, 100, 100);
514
gradient.addColorStop(0, '#ff7675');
515
gradient.addColorStop(1, '#74b9ff');
516
517
ctx.fillStyle = gradient;
518
ctx.fillRect(0, 0, 100, 100);
519
520
ctx.fillStyle = '#ffffff';
521
ctx.font = '16px Arial';
522
ctx.textAlign = 'center';
523
ctx.fillText('Canvas', 50, 55);
524
525
return canvas;
526
};
527
528
const canvasImage = new Image({
529
style: {
530
image: createCanvasImage(),
531
x: 50,
532
y: 400,
533
width: 100,
534
height: 100
535
}
536
});
537
538
// Use video as image source (for video frames)
539
const videoImage = new Image({
540
style: {
541
image: document.getElementById('videoElement') as HTMLVideoElement,
542
x: 200,
543
y: 400,
544
width: 160,
545
height: 90
546
}
547
});
548
549
zr.add(canvasImage);
550
// zr.add(videoImage); // Only if video element exists
551
```
552
553
### Interactive Text and Images
554
555
```typescript
556
import { Text, Image, Group } from "zrender";
557
558
// Create interactive card with text and image
559
const createInteractiveCard = (title: string, description: string, imageSrc: string, x: number, y: number) => {
560
const card = new Group({
561
position: [x, y]
562
});
563
564
// Background
565
const background = new Rect({
566
shape: { x: 0, y: 0, width: 250, height: 150, r: 8 },
567
style: {
568
fill: '#ffffff',
569
stroke: '#dee2e6',
570
lineWidth: 1,
571
shadowBlur: 10,
572
shadowColor: 'rgba(0, 0, 0, 0.1)'
573
}
574
});
575
576
// Image
577
const image = new Image({
578
style: {
579
image: imageSrc,
580
x: 15,
581
y: 15,
582
width: 60,
583
height: 60
584
}
585
});
586
587
// Title text
588
const titleText = new Text({
589
style: {
590
text: title,
591
fontSize: 18,
592
fontWeight: 'bold',
593
fill: '#2d3436',
594
textAlign: 'left'
595
},
596
position: [90, 25]
597
});
598
599
// Description text
600
const descText = new Text({
601
style: {
602
text: description,
603
fontSize: 14,
604
fill: '#636e72',
605
width: 145,
606
textAlign: 'left'
607
},
608
position: [90, 50]
609
});
610
611
// Build card
612
card.add(background);
613
card.add(image);
614
card.add(titleText);
615
card.add(descText);
616
617
// Add hover effects
618
card.on('mouseover', () => {
619
background.animate('style')
620
.when(200, { shadowBlur: 20, fill: '#f8f9fa' })
621
.start();
622
});
623
624
card.on('mouseout', () => {
625
background.animate('style')
626
.when(200, { shadowBlur: 10, fill: '#ffffff' })
627
.start();
628
});
629
630
return card;
631
};
632
633
// Create interactive cards
634
const card1 = createInteractiveCard(
635
'ZRender Graphics',
636
'Powerful 2D rendering library for creating interactive visualizations.',
637
'path/to/zrender-icon.png',
638
50, 500
639
);
640
641
const card2 = createInteractiveCard(
642
'Canvas Rendering',
643
'High-performance canvas-based rendering with full feature support.',
644
'path/to/canvas-icon.png',
645
350, 500
646
);
647
648
zr.add(card1);
649
zr.add(card2);
650
```
651
652
### Text Measurement and Layout
653
654
```typescript
655
import { Text } from "zrender";
656
657
// Dynamic text sizing based on content
658
const createAdaptiveText = (content: string, maxWidth: number) => {
659
let fontSize = 20;
660
let textElement: Text;
661
662
// Function to estimate text width (approximate)
663
const estimateTextWidth = (text: string, size: number) => {
664
return text.length * size * 0.6; // Rough approximation
665
};
666
667
// Adjust font size to fit width
668
while (estimateTextWidth(content, fontSize) > maxWidth && fontSize > 8) {
669
fontSize -= 1;
670
}
671
672
textElement = new Text({
673
style: {
674
text: content,
675
fontSize: fontSize,
676
fill: '#2d3436',
677
width: maxWidth,
678
textAlign: 'center'
679
}
680
});
681
682
return textElement;
683
};
684
685
// Create texts that adapt to container width
686
const adaptiveText1 = createAdaptiveText('Short text', 200);
687
const adaptiveText2 = createAdaptiveText('This is a much longer text that should be sized down', 200);
688
689
adaptiveText1.position = [50, 650];
690
adaptiveText2.position = [300, 650];
691
692
zr.add(adaptiveText1);
693
zr.add(adaptiveText2);
694
```