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

animation.mddocs/

0

# Animation System

1

2

ZRender provides a comprehensive animation framework that supports property interpolation, easing functions, timeline control, and advanced features like path morphing. The animation system is frame-based and optimized for smooth 60fps performance.

3

4

## Core Animation Classes

5

6

### Animator

7

8

The primary interface for controlling animations on elements:

9

10

```typescript { .api }

11

interface Animator {

12

// Timeline control

13

when(time: number, props: any): Animator;

14

during(callback: (percent: number) => void): Animator;

15

delay(time: number): Animator;

16

17

// Lifecycle callbacks

18

done(callback: () => void): Animator;

19

aborted(callback: () => void): Animator;

20

21

// Playback control

22

start(easing?: string | Function): Animator;

23

stop(): void;

24

pause(): void;

25

resume(): void;

26

27

// Configuration

28

getLoop(): boolean;

29

setLoop(loop: boolean): Animator;

30

}

31

```

32

33

### Element Animation Methods

34

35

All graphics elements provide animation methods:

36

37

```typescript { .api }

38

interface Element {

39

animate(props?: string): Animator;

40

animateStyle(): Animator;

41

animateShape(): Animator;

42

stopAnimation(forwardToLast?: boolean): this;

43

44

// Animation state queries

45

isAnimationFinished(): boolean;

46

getAnimationDelay(): number;

47

}

48

```

49

50

## Built-in Easing Functions

51

52

ZRender includes a comprehensive set of easing functions:

53

54

### Basic Easing

55

56

```typescript { .api }

57

function linear(t: number): number;

58

function easeInQuad(t: number): number;

59

function easeOutQuad(t: number): number;

60

function easeInOutQuad(t: number): number;

61

function easeInCubic(t: number): number;

62

function easeOutCubic(t: number): number;

63

function easeInOutCubic(t: number): number;

64

function easeInQuart(t: number): number;

65

function easeOutQuart(t: number): number;

66

function easeInOutQuart(t: number): number;

67

function easeInQuint(t: number): number;

68

function easeOutQuint(t: number): number;

69

function easeInOutQuint(t: number): number;

70

```

71

72

### Advanced Easing

73

74

```typescript { .api }

75

function easeInSine(t: number): number;

76

function easeOutSine(t: number): number;

77

function easeInOutSine(t: number): number;

78

function easeInExpo(t: number): number;

79

function easeOutExpo(t: number): number;

80

function easeInOutExpo(t: number): number;

81

function easeInCirc(t: number): number;

82

function easeOutCirc(t: number): number;

83

function easeInOutCirc(t: number): number;

84

```

85

86

### Elastic and Bounce

87

88

```typescript { .api }

89

function easeInElastic(t: number): number;

90

function easeOutElastic(t: number): number;

91

function easeInOutElastic(t: number): number;

92

function easeInBack(t: number): number;

93

function easeOutBack(t: number): number;

94

function easeInOutBack(t: number): number;

95

function easeInBounce(t: number): number;

96

function easeOutBounce(t: number): number;

97

function easeInOutBounce(t: number): number;

98

```

99

100

### Convenience Aliases

101

102

```typescript { .api }

103

const easeIn: typeof easeInQuad;

104

const easeOut: typeof easeOutQuad;

105

const easeInOut: typeof easeInOutQuad;

106

const ease: typeof easeInOut;

107

```

108

109

## Animation Configuration

110

111

### Clip Interface

112

113

```typescript { .api }

114

interface Clip {

115

life: number; // Duration in milliseconds

116

delay: number; // Delay before starting

117

loop: boolean; // Whether to loop

118

gap: number; // Gap between loops

119

easing: string | Function; // Easing function

120

onframe: (percent: number) => void; // Frame callback

121

ondestroy: () => void; // Cleanup callback

122

onrestart: () => void; // Restart callback

123

}

124

```

125

126

### Animation Options

127

128

```typescript { .api }

129

interface AnimationOptions {

130

duration?: number; // Animation duration

131

easing?: string | Function; // Easing function

132

delay?: number; // Start delay

133

loop?: boolean; // Loop animation

134

gap?: number; // Gap between loops

135

during?: (percent: number) => void; // Progress callback

136

done?: () => void; // Completion callback

137

aborted?: () => void; // Abortion callback

138

}

139

```

140

141

## Path Morphing

142

143

Advanced morphing capabilities for transforming between different paths:

144

145

```typescript { .api }

146

namespace morph {

147

function morphPath(from: string, to: string, animationOpts?: AnimationOptions): string;

148

function combineMorphing(morphList: any[]): any;

149

function isCombineMorphing(obj: any): boolean;

150

}

151

```

152

153

## Usage Examples

154

155

### Basic Property Animation

156

157

```typescript

158

import { Circle } from "zrender";

159

160

const circle = new Circle({

161

shape: { cx: 100, cy: 100, r: 30 },

162

style: { fill: '#74b9ff' },

163

position: [0, 0]

164

});

165

166

// Animate position

167

circle.animate('position')

168

.when(1000, [200, 150]) // Move to (200, 150) over 1 second

169

.when(2000, [100, 100]) // Return to (100, 100) over next second

170

.start('easeInOut');

171

172

// Animate shape properties

173

circle.animate('shape')

174

.when(1500, { r: 60 }) // Grow radius to 60

175

.start('easeOutElastic');

176

177

// Animate style properties

178

circle.animate('style')

179

.when(800, { fill: '#e17055', opacity: 0.7 })

180

.start('easeInOutQuad');

181

182

zr.add(circle);

183

```

184

185

### Complex Animation Sequences

186

187

```typescript

188

import { Rect } from "zrender";

189

190

const rect = new Rect({

191

shape: { x: 50, y: 50, width: 80, height: 60 },

192

style: { fill: '#00b894' }

193

});

194

195

// Chain multiple animations

196

rect.animate('position')

197

.when(1000, [200, 50])

198

.when(2000, [200, 200])

199

.when(3000, [50, 200])

200

.when(4000, [50, 50])

201

.start('easeInOutCubic');

202

203

// Parallel animation on different properties

204

rect.animate('shape')

205

.delay(500) // Start after 500ms delay

206

.when(1000, { width: 120, height: 40 })

207

.when(2000, { width: 40, height: 120 })

208

.when(3000, { width: 80, height: 60 })

209

.start('easeOutBounce');

210

211

// Color animation with callbacks

212

rect.animate('style')

213

.when(1000, { fill: '#fdcb6e' })

214

.when(2000, { fill: '#e84393' })

215

.when(3000, { fill: '#74b9ff' })

216

.when(4000, { fill: '#00b894' })

217

.during((percent) => {

218

// Custom logic during animation

219

if (percent > 0.5) {

220

rect.style.shadowBlur = (percent - 0.5) * 20;

221

}

222

})

223

.done(() => {

224

console.log('Animation completed!');

225

})

226

.start();

227

228

zr.add(rect);

229

```

230

231

### Easing Function Demonstrations

232

233

```typescript

234

import { Circle } from "zrender";

235

236

// Create circles to demonstrate different easing functions

237

const easingFunctions = [

238

'linear', 'easeInQuad', 'easeOutQuad', 'easeInOutQuad',

239

'easeInCubic', 'easeOutBounce', 'easeOutElastic', 'easeInBack'

240

];

241

242

easingFunctions.forEach((easing, index) => {

243

const circle = new Circle({

244

shape: { cx: 50, cy: 50 + index * 60, r: 20 },

245

style: { fill: '#74b9ff' }

246

});

247

248

// Animate position with different easing

249

circle.animate('position')

250

.when(2000, [400, 50 + index * 60])

251

.setLoop(true) // Loop the animation

252

.start(easing);

253

254

// Add label

255

const label = new Text({

256

style: {

257

text: easing,

258

fontSize: 12,

259

fill: '#2d3436'

260

},

261

position: [460, 55 + index * 60]

262

});

263

264

zr.add(circle);

265

zr.add(label);

266

});

267

```

268

269

### Interactive Animations

270

271

```typescript

272

import { Star } from "zrender";

273

274

const star = new Star({

275

shape: { cx: 200, cy: 200, n: 5, r0: 30, r: 60 },

276

style: { fill: '#fdcb6e', stroke: '#e17055', lineWidth: 2 }

277

});

278

279

// Hover animations

280

star.on('mouseover', () => {

281

star.stopAnimation(); // Stop any running animations

282

283

star.animate('shape')

284

.when(300, { r: 80, r0: 40 })

285

.start('easeOutElastic');

286

287

star.animate('style')

288

.when(200, { fill: '#fff5b4', shadowBlur: 15, shadowColor: '#fdcb6e' })

289

.start();

290

});

291

292

star.on('mouseout', () => {

293

star.stopAnimation();

294

295

star.animate('shape')

296

.when(300, { r: 60, r0: 30 })

297

.start('easeOutQuad');

298

299

star.animate('style')

300

.when(200, { fill: '#fdcb6e', shadowBlur: 0 })

301

.start();

302

});

303

304

// Click animation with rotation

305

star.on('click', () => {

306

star.animate('rotation')

307

.when(1000, Math.PI * 2) // Full rotation

308

.done(() => {

309

star.rotation = 0; // Reset rotation

310

})

311

.start('easeInOutCubic');

312

});

313

314

zr.add(star);

315

```

316

317

### Timeline Animations

318

319

```typescript

320

import { Group, Circle, Rect, Text } from "zrender";

321

322

// Create a group of elements for coordinated animation

323

const animationGroup = new Group();

324

325

const elements = [];

326

for (let i = 0; i < 5; i++) {

327

const circle = new Circle({

328

shape: { cx: 100 + i * 80, cy: 200, r: 25 },

329

style: { fill: `hsl(${i * 60}, 70%, 60%)` }

330

});

331

elements.push(circle);

332

animationGroup.add(circle);

333

}

334

335

// Staggered animation sequence

336

elements.forEach((element, index) => {

337

const delay = index * 200; // 200ms stagger

338

339

// Bounce animation

340

element.animate('position')

341

.delay(delay)

342

.when(600, [0, -100]) // Move up (relative to group)

343

.when(1200, [0, 0]) // Return to original position

344

.start('easeOutBounce');

345

346

// Scale animation

347

element.animate('scale')

348

.delay(delay + 300)

349

.when(400, [1.5, 1.5])

350

.when(800, [1, 1])

351

.start('easeInOutQuad');

352

});

353

354

// Group-level animation

355

animationGroup.animate('position')

356

.delay(1500)

357

.when(1000, [0, 100])

358

.start('easeInOutCubic');

359

360

zr.add(animationGroup);

361

```

362

363

### Custom Easing Functions

364

365

```typescript

366

import { Rect } from "zrender";

367

368

// Define custom easing function

369

const customEasing = (t: number): number => {

370

// Elastic overshoot effect

371

return t === 0 ? 0 : t === 1 ? 1 :

372

-Math.pow(2, 10 * (t - 1)) * Math.sin((t - 1.1) * 5 * Math.PI);

373

};

374

375

// Apply custom easing

376

const rect = new Rect({

377

shape: { x: 50, y: 150, width: 60, height: 40 },

378

style: { fill: '#e84393' }

379

});

380

381

rect.animate('position')

382

.when(2000, [400, 150])

383

.start(customEasing);

384

385

// Bezier curve easing (cubic-bezier equivalent)

386

const bezierEasing = (t: number): number => {

387

// Equivalent to cubic-bezier(0.25, 0.46, 0.45, 0.94)

388

const p0 = 0, p1 = 0.25, p2 = 0.45, p3 = 1;

389

const u = 1 - t;

390

return 3 * u * u * t * p1 + 3 * u * t * t * p2 + t * t * t * p3;

391

};

392

393

rect.animate('shape')

394

.when(2000, { width: 120, height: 80 })

395

.start(bezierEasing);

396

397

zr.add(rect);

398

```

399

400

### Animation Control and Management

401

402

```typescript

403

import { Circle } from "zrender";

404

405

const controlledCircle = new Circle({

406

shape: { cx: 200, cy: 200, r: 40 },

407

style: { fill: '#74b9ff' }

408

});

409

410

// Create controllable animation

411

let currentAnimation: Animator;

412

413

const startAnimation = () => {

414

currentAnimation = controlledCircle.animate('position')

415

.when(3000, [400, 200])

416

.when(6000, [200, 200])

417

.setLoop(true)

418

.start('easeInOutQuad');

419

};

420

421

const pauseAnimation = () => {

422

if (currentAnimation) {

423

currentAnimation.pause();

424

}

425

};

426

427

const resumeAnimation = () => {

428

if (currentAnimation) {

429

currentAnimation.resume();

430

}

431

};

432

433

const stopAnimation = () => {

434

if (currentAnimation) {

435

currentAnimation.stop();

436

}

437

};

438

439

// Control via keyboard events

440

document.addEventListener('keydown', (e) => {

441

switch(e.key) {

442

case 's': startAnimation(); break;

443

case 'p': pauseAnimation(); break;

444

case 'r': resumeAnimation(); break;

445

case 'x': stopAnimation(); break;

446

}

447

});

448

449

// Click to toggle animation

450

controlledCircle.on('click', () => {

451

if (controlledCircle.isAnimationFinished()) {

452

startAnimation();

453

} else {

454

stopAnimation();

455

}

456

});

457

458

zr.add(controlledCircle);

459

```

460

461

### Performance Optimization

462

463

```typescript

464

import { Group, Circle } from "zrender";

465

466

// Animate many elements efficiently

467

const createOptimizedAnimation = () => {

468

const group = new Group();

469

const elements: Circle[] = [];

470

471

// Create many elements

472

for (let i = 0; i < 100; i++) {

473

const circle = new Circle({

474

shape: {

475

cx: Math.random() * 800,

476

cy: Math.random() * 600,

477

r: 5 + Math.random() * 10

478

},

479

style: {

480

fill: `hsl(${Math.random() * 360}, 70%, 60%)`,

481

opacity: 0.8

482

}

483

});

484

elements.push(circle);

485

group.add(circle);

486

}

487

488

// Use single group animation instead of individual animations

489

// This is more performant for coordinated movements

490

group.animate('rotation')

491

.when(10000, Math.PI * 2)

492

.setLoop(true)

493

.start('linear');

494

495

// Stagger individual element animations efficiently

496

elements.forEach((element, index) => {

497

if (index % 10 === 0) { // Animate every 10th element

498

element.animate('scale')

499

.delay(Math.random() * 2000)

500

.when(1000, [1.5, 1.5])

501

.when(2000, [1, 1])

502

.setLoop(true)

503

.start('easeInOutSine');

504

}

505

});

506

507

return group;

508

};

509

510

const optimizedGroup = createOptimizedAnimation();

511

zr.add(optimizedGroup);

512

```