or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

animation.mdcore-zrender.mdevents.mdgraphics-primitives.mdindex.mdshapes.mdstyling.mdtext-images.mdutilities.md

text-images.mddocs/

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

```