A lightweight 2D graphics library providing canvas and SVG rendering for Apache ECharts
ZRender provides a comprehensive styling system for controlling the visual appearance of graphics elements. This includes solid colors, gradients, patterns, shadows, and advanced rendering effects.
All visual elements support these fundamental styling properties:
interface PathStyleProps {
// Fill properties
fill?: string | LinearGradient | RadialGradient | Pattern;
fillOpacity?: number;
// Stroke properties
stroke?: string;
strokeOpacity?: number;
lineWidth?: number;
lineDash?: number[];
lineDashOffset?: number;
lineCap?: 'butt' | 'round' | 'square';
lineJoin?: 'bevel' | 'round' | 'miter';
miterLimit?: number;
// Overall opacity
opacity?: number;
// Shadow effects
shadowBlur?: number;
shadowColor?: string;
shadowOffsetX?: number;
shadowOffsetY?: number;
// Advanced rendering
blend?: string;
globalCompositeOperation?: string;
}Creates linear gradients with customizable direction and color stops:
class LinearGradient {
constructor(x1: number, y1: number, x2: number, y2: number, colorStops: ColorStop[], globalCoord?: boolean);
x1: number; // Start point X (0-1 in local coordinates)
y1: number; // Start point Y (0-1 in local coordinates)
x2: number; // End point X (0-1 in local coordinates)
y2: number; // End point Y (0-1 in local coordinates)
colorStops: ColorStop[];
global: boolean; // Use global coordinates instead of local
}
interface LinearGradientObject {
type: 'linear';
x1: number;
y1: number;
x2: number;
y2: number;
colorStops: ColorStop[];
global?: boolean;
}
interface ColorStop {
offset: number; // Position along gradient (0-1)
color: string; // Color at this stop
}Creates radial gradients emanating from a center point:
class RadialGradient {
constructor(x: number, y: number, r: number, colorStops: ColorStop[], globalCoord?: boolean);
x: number; // Center X (0-1 in local coordinates)
y: number; // Center Y (0-1 in local coordinates)
r: number; // Radius (0-1 in local coordinates)
colorStops: ColorStop[];
global: boolean; // Use global coordinates instead of local
}
interface RadialGradientObject {
type: 'radial';
x: number;
y: number;
r: number;
colorStops: ColorStop[];
global?: boolean;
}Creates pattern fills using images or other elements:
class Pattern {
constructor(image: PatternImageSource, repeat: PatternRepeat);
image: PatternImageSource;
repeat: PatternRepeat;
}
type PatternImageSource =
| HTMLImageElement
| HTMLCanvasElement
| HTMLVideoElement
| string; // URL or data URI
type PatternRepeat = 'repeat' | 'repeat-x' | 'repeat-y' | 'no-repeat';
interface PatternObjectBase {
type: 'pattern';
repeat: PatternRepeat;
}
interface ImagePatternObject extends PatternObjectBase {
image: HTMLImageElement | HTMLCanvasElement | string;
x?: number;
y?: number;
rotation?: number;
scaleX?: number;
scaleY?: number;
}
interface SVGPatternObject extends PatternObjectBase {
svgElement: SVGElement;
svgWidth: number;
svgHeight: number;
x?: number;
y?: number;
rotation?: number;
scaleX?: number;
scaleY?: number;
}
type PatternObject = ImagePatternObject | SVGPatternObject;Text elements have additional styling properties:
interface TextStyleProps {
// Text content and basic properties
text?: string;
fontSize?: number;
fontFamily?: string;
fontStyle?: 'normal' | 'italic' | 'oblique';
fontWeight?: string | number;
// Text positioning and alignment
textAlign?: 'left' | 'center' | 'right';
textVerticalAlign?: 'top' | 'middle' | 'bottom';
textBaseline?: 'top' | 'middle' | 'bottom' | 'alphabetic' | 'ideographic' | 'hanging';
// Text appearance
fill?: string | LinearGradient | RadialGradient | Pattern;
stroke?: string;
lineWidth?: number;
opacity?: number;
// Text shadows
textShadowBlur?: number;
textShadowColor?: string;
textShadowOffsetX?: number;
textShadowOffsetY?: number;
// Text layout
width?: number;
height?: number;
textPadding?: number | number[];
textLineHeight?: number;
// Rich text formatting
rich?: Record<string, TextStyleProps>;
// Text truncation
truncate?: {
outerWidth?: number;
outerHeight?: number;
ellipsis?: string;
placeholder?: string;
};
// Rendering effects
blend?: string;
}import { Circle, Rect } from "zrender";
// Simple solid color fill
const circle = new Circle({
shape: { cx: 100, cy: 100, r: 50 },
style: {
fill: '#ff6b6b', // Solid red fill
stroke: '#d63031', // Dark red border
lineWidth: 3,
opacity: 0.8
}
});
// Advanced stroke styling
const rect = new Rect({
shape: { x: 200, y: 50, width: 120, height: 80 },
style: {
fill: 'none',
stroke: '#74b9ff',
lineWidth: 4,
lineDash: [10, 5], // Dashed line
lineCap: 'round', // Rounded line ends
lineJoin: 'round' // Rounded corners
}
});
zr.add(circle);
zr.add(rect);import { Rect, LinearGradient } from "zrender";
// Horizontal gradient
const horizontalGradient = new LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#ff7675' },
{ offset: 0.5, color: '#fd79a8' },
{ offset: 1, color: '#fdcb6e' }
]);
// Vertical gradient
const verticalGradient = new LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#74b9ff' },
{ offset: 1, color: '#0984e3' }
]);
// Diagonal gradient
const diagonalGradient = new LinearGradient(0, 0, 1, 1, [
{ offset: 0, color: '#00b894' },
{ offset: 1, color: '#00cec9' }
]);
const rect1 = new Rect({
shape: { x: 50, y: 50, width: 150, height: 80 },
style: { fill: horizontalGradient }
});
const rect2 = new Rect({
shape: { x: 250, y: 50, width: 80, height: 150 },
style: { fill: verticalGradient }
});
const rect3 = new Rect({
shape: { x: 50, y: 180, width: 120, height: 120 },
style: { fill: diagonalGradient }
});
zr.add(rect1);
zr.add(rect2);
zr.add(rect3);import { Circle, RadialGradient } from "zrender";
// Center-focused radial gradient
const radialGradient = new RadialGradient(0.5, 0.5, 0.5, [
{ offset: 0, color: '#ffffff' },
{ offset: 0.7, color: '#74b9ff' },
{ offset: 1, color: '#0984e3' }
]);
// Off-center radial gradient for lighting effect
const lightingGradient = new RadialGradient(0.3, 0.3, 0.8, [
{ offset: 0, color: '#fff5b4' },
{ offset: 0.4, color: '#ffda79' },
{ offset: 1, color: '#e17055' }
]);
const circle1 = new Circle({
shape: { cx: 150, cy: 150, r: 80 },
style: { fill: radialGradient }
});
const circle2 = new Circle({
shape: { cx: 350, cy: 150, r: 80 },
style: {
fill: lightingGradient,
stroke: '#d63031',
lineWidth: 2
}
});
zr.add(circle1);
zr.add(circle2);import { Rect, Pattern } from "zrender";
// Create pattern from image
const createImagePattern = (imageUrl: string) => {
const img = new Image();
img.src = imageUrl;
return new Pattern(img, 'repeat');
};
// Create pattern from canvas
const createCanvasPattern = () => {
const canvas = document.createElement('canvas');
canvas.width = 20;
canvas.height = 20;
const ctx = canvas.getContext('2d')!;
// Draw a simple pattern
ctx.fillStyle = '#e17055';
ctx.fillRect(0, 0, 10, 10);
ctx.fillStyle = '#fdcb6e';
ctx.fillRect(10, 10, 10, 10);
return new Pattern(canvas, 'repeat');
};
const patternRect = new Rect({
shape: { x: 100, y: 100, width: 200, height: 150 },
style: {
fill: createCanvasPattern(),
stroke: '#2d3436',
lineWidth: 2
}
});
zr.add(patternRect);import { Circle, Text } from "zrender";
// Drop shadow effect
const shadowCircle = new Circle({
shape: { cx: 150, cy: 150, r: 60 },
style: {
fill: '#74b9ff',
shadowBlur: 20,
shadowColor: 'rgba(116, 185, 255, 0.6)',
shadowOffsetX: 10,
shadowOffsetY: 10
}
});
// Glow effect
const glowCircle = new Circle({
shape: { cx: 350, cy: 150, r: 50 },
style: {
fill: '#00b894',
shadowBlur: 30,
shadowColor: '#00b894'
// No offset for glow effect
}
});
// Text with shadow
const shadowText = new Text({
style: {
text: 'Shadow Text',
fontSize: 24,
fill: '#2d3436',
textShadowBlur: 5,
textShadowColor: 'rgba(0, 0, 0, 0.5)',
textShadowOffsetX: 2,
textShadowOffsetY: 2
},
position: [200, 300]
});
zr.add(shadowCircle);
zr.add(glowCircle);
zr.add(shadowText);import { Text } from "zrender";
// Rich text with multiple styles
const richText = new Text({
style: {
text: '{title|ZRender} {subtitle|Graphics Library}\n{body|Create beautiful 2D graphics}',
rich: {
title: {
fontSize: 28,
fontWeight: 'bold',
fill: '#2d3436'
},
subtitle: {
fontSize: 16,
fill: '#636e72',
fontStyle: 'italic'
},
body: {
fontSize: 14,
fill: '#74b9ff',
textPadding: [10, 0, 0, 0]
}
},
width: 300,
textAlign: 'center'
},
position: [150, 100]
});
// Gradient text fill
const gradientText = new Text({
style: {
text: 'Gradient Text',
fontSize: 32,
fontWeight: 'bold',
fill: new LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#ff7675' },
{ offset: 0.5, color: '#fd79a8' },
{ offset: 1, color: '#e84393' }
]),
stroke: '#2d3436',
lineWidth: 1
},
position: [150, 250]
});
zr.add(richText);
zr.add(gradientText);import { Circle, LinearGradient } from "zrender";
// Create animated gradient
const animatedCircle = new Circle({
shape: { cx: 200, cy: 200, r: 60 },
style: {
fill: new LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#74b9ff' },
{ offset: 1, color: '#0984e3' }
]),
shadowBlur: 0
}
});
// Animate color transitions
animatedCircle.animate('style')
.when(1000, {
shadowBlur: 20,
shadowColor: '#74b9ff'
})
.when(2000, {
shadowBlur: 0
})
.start('easeInOut');
// Animate gradient color stops
const animateGradient = () => {
const newGradient = new LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#e17055' },
{ offset: 1, color: '#fdcb6e' }
]);
animatedCircle.animate('style')
.when(1500, { fill: newGradient })
.start();
};
// Trigger gradient animation on click
animatedCircle.on('click', animateGradient);
zr.add(animatedCircle);import { Rect } from "zrender";
// Interactive styling with state management
const interactiveRect = new Rect({
shape: { x: 100, y: 100, width: 120, height: 80, r: 8 },
style: {
fill: '#74b9ff',
stroke: '#0984e3',
lineWidth: 2,
opacity: 1
}
});
// Store original style
const originalStyle = { ...interactiveRect.style };
// Hover states
interactiveRect.on('mouseover', () => {
interactiveRect.animate('style')
.when(200, {
fill: '#a29bfe',
shadowBlur: 10,
shadowColor: '#74b9ff',
opacity: 0.9
})
.start('easeOut');
});
interactiveRect.on('mouseout', () => {
interactiveRect.animate('style')
.when(200, originalStyle)
.start('easeOut');
});
// Active/pressed state
interactiveRect.on('mousedown', () => {
interactiveRect.animate('style')
.when(100, {
fill: '#6c5ce7',
shadowBlur: 5
})
.start();
});
interactiveRect.on('mouseup', () => {
interactiveRect.animate('style')
.when(100, {
fill: '#a29bfe',
shadowBlur: 10
})
.start();
});
zr.add(interactiveRect);Install with Tessl CLI
npx tessl i tessl/npm-zrender