如上述的两种圆柱形、立方体类型的图形,需要我们自己定义绘制的形状。
这时就需要通过 series: { type: 'custom' } 进行设置。如果你仅仅是想在图表中添加一些修饰图案、特殊文案、或者水印可以使用 graphic 来进行设置,具体细节请点击这里。
series-custom
自定义系列可以自定义系列中的图形元素渲染。从而能扩展出不同的图表。同时,echarts 会统一管理图形的创建删除、动画、与其他组件(如 dataZoom、visualMap)的联动,使开发者不必纠结这些细节。
开发者自定义渲染逻辑(renderItem 函数)
系列需要开发者自己提供图形渲染的逻辑。这个渲染逻辑一般命名为 renderItem例如:(图2的代码)
// 表示立方体左侧面和右侧面水平方向的宽度。
const RECT_WIDTH = 12;
// 表示立方体顶部、底部矩形的大小计算因子,
// 通过 RECT_WIDTH * FACTOR * 2 我们可以计算出立方体顶部矩形的高度。
const FACTOR = 0.5;
//...
series: {
type: 'custom',
name: '省级中心示范村建设数量',
animation: true,
renderItem: function (params: any, api: any): any {
// api.value(0) - 表示返回 dataItem 在 data 中数组中的索引下标;api.value(1) - 表示返回 dataItem[1]
// api.coord() - 返回对应 dataItem 的 x、y 坐标值(pixel)
// 注意: echarts 坐标系默认是从 div 元素的左上角作为原点。
// 朝左边 x 值越大,朝下边 y 值越大。
const [x, y] = api.coord([api.value(0), api.value(1)]);
// api.size() - 返回对应 dataItem 的 width、height。
const [width, height] = api.size([api.value(0), api.value(1)]);
// 返回 grid 的大小和位置,grid 包含在坐标系中。
const { y: gridY, height: gridH } = params.coordSys;
return {
type: 'group',
// 当没有数据的时候什么都不渲染
children: api.value(1) > 0 ? [
{
type: 'LeftShape',
shape: {
// 图形向左偏移半个立方体的宽度,保持和刻度线居中
x: x - RECT_WIDTH,
y: y,
// 立方体左侧面的宽度
width: RECT_WIDTH,
// 立方体的高度,减去RECT_WIDTH * FACTOR 是为了不让立方体绘制到 X 轴下方
// 应该和 X 轴对齐。
height: h - RECT_WIDTH * FACTOR,
},
style: {
fill: colorLeft,
},
},
{
type: 'RightShape',
shape: {
x: x,
y: y,
// 立方体左侧面的宽度
width: RECT_WIDTH,
// 立方体的高度
height: h - RECT_WIDTH * FACTOR,
},
style: {
fill: colorRight,
},
},
{
type: 'TopShape',
shape: {
x: x - RECT_WIDTH,
y: y,
// 立方体顶部矩形的宽度
width: RECT_WIDTH * 2,
// 立方体的高度
height: h - RECT_WIDTH * FACTOR,
},
style: {
fill: '#38E3FF',
},
},
/* 绘制 bar 的背景 */
{
type: 'LeftShape',
shape: {
x: x - RECT_WIDTH,
y: gridY,
// 立方体的宽度
width: RECT_WIDTH,
// 背景的高度应该始终和 grid 高度一样,
// 减去 RECT_WIDTH * FACTOR 是为了不让背景绘制到 X 轴下方
height: gridH - RECT_WIDTH * FACTOR,
},
style: {
fill: colorLeft,
opacity: 0.2,
},
},
{
type: 'RightShape',
shape: {
x: x,
y: gridY,
width: RECT_WIDTH,
height: gridH - RECT_WIDTH * FACTOR,
},
style: {
fill: colorRight,
opacity: 0.2,
},
},
{
type: 'TopShape',
shape: {
x: x - RECT_WIDTH,
y: gridY,
width: RECT_WIDTH * 2,
height: gridH - RECT_WIDTH * FACTOR,
},
style: {
fill: '#38E3FF',
opacity: 0.2,
},
},
]} : [],
},
},
对于 data 中的每个数据项(为方便描述,这里称为 dataItem)调用此 renderItem 函数。
renderItem(params, api)
params:包含了当前数据信息和坐标系的信息。api:是一些开发者可调用的方法集合。renderItem: 函数须返回根据此dataItem绘制出的图形元素的定义信息。
一般来说,renderItem 函数的主要逻辑,是将 dataItem 里的值映射到坐标系上的图形元素。这一般需要用到 renderItem.arguments.api 中的两个函数:
api.value(...),意思是取出dataItem中的数值。例如api.value(0)表示取出当前dataItem中第一个维度的数值。api.coord(...),意思是进行坐标转换计算。例如var point = api.coord([api.value(0), api.value(1)])表示dataItem中的数值转换成坐标系上的点。
有时候还需要用到 api.size(...) 函数,表示得到坐标系上该 dataItem 的对应的图像尺寸。
返回值中样式的设置可以使用 api.style(...) 函数,他能得到 series.itemStyle 中定义的样式信息,以及视觉映射的样式信息。
也可以用这种方式覆盖这些样式信息:api.style({ fill: 'green', stroke: 'yellow' })。
renderItem.arguments 介绍
params.dataIndex
返回该 dataItem 在数据中对应的索引下标
params.seriesName
返回系列名称
params.dataInsideLength
返回数据的长度。
params.encode
返回 series.encode
params.coordSys
返回 grid 的坐标和尺寸: {x: number, y: number, width: numberm, height: number }
api.value()
返回对应的 X、Y 轴的值。 如果 X 轴是类目轴,则返回的是该数据项在数据集合中的索引下标。 如果 Y 轴是数值轴,则返回的是该数据项的数据值。
api.coord()
返回数据项在坐标系中的坐标
// x、y 分别表示 XY 的坐标,单位为 pixel。
// 注意,这里的参数必须传值,否则得不到想要的结果
const [x, y] = api.coord([api.value(0), api.value(1)]);
api.size()
返回数据项对应图形的尺寸
// w、h 分别表示图像的宽高,单位为 pixel。
// 注意,这里的参数必须传值,否则得不到想要的结果
const [w, h] = api.coord([api.value(0), api.value(1)]);
renderItem.return 介绍
图形元素。每个图形元素是一个 object。详细信息参见:graphic。
如果什么都不渲染,可以不返回任何东西。
下面我们按照绘制图像的类型来进行介绍,例如图一和图二都是组合类型(group)。
组合类型
type
如果是一个组合类型,则值应该为 group、并通过 children 属性来绘制单个图形。
x、y
图像的 x、y 坐标(像素)位置。
rotation
图像的旋转。number 类型
scaleX、scaleY
图像在 x、y 方向上的缩放。
originX、originY
图像旋转和缩放原点的 x、y 像素位置。
transition
可以通过'all'指定所有属性都开启过渡动画,也可以指定单个或一组属性。可以设置为 string、Array 类型。
Transform 相关的属性:'x'、 'y'、'scaleX'、'scaleY'、'rotation'、'originX'、'originY'。例如:
{
type: 'rect',
x: 100,
y: 200,
transition: ['x', 'y']
}
还可以是这三个属性 'shape'、'style'、'extra'。表示这三个属性中所有的子属性都开启过渡动画。例如:
{
type: 'rect',
shape: { // ... },
// 表示 shape 中所有属性都开启过渡动画。
transition: 'shape',
}
在自定义系列中,当 transition 没有指定时,'x' 和 'y' 会默认开启过渡动画。如果想禁用这种默认,可设定为空数组:transition: []
enterFrom
配置图形的入场属性用于入场动画。例如:
{
type: 'circle',
x: 100,
// enterFrom 其实是指定入场动画初始化时的样式。可以设置 x、y、shape、style 等
enterFrom: {
// 淡入
style: { opacity: 0 },
// 从左飞入
x: 0
}
}
leaveTo
配置图形的退场属性用于退场动画。例如:
{
type: 'circle',
x: 100,
// leaveTo 其实是指定离场动画结束时的样式。可以设置 x、y、shape、style 等
leaveTo: {
// 淡出
style: { opacity: 0 },
// 向右飞出
x: 200
}
}
enterAnimation
入场动画配置。
enterAnimation:{
// 动画时长,单位 ms
duration: 100,
// 可以查看 https://echarts.apache.org/examples/zh/editor.html?c=line-easing
easing: 'linear',
// 动画延迟时长,单位 ms
delay: 100,
}
updateAnimation、leaveAnimation
更新属性的动画配置。
z2
用于决定图形元素的覆盖关系。number 类型
width
用于描述此 group 的宽。width 属性只有组合类型中有,单类型中没有(可以在 shape 中设置 width)
这个宽只用于给子节点定位。
即便当宽度为零的时候,子节点也可以使用 left: 'center' 相对于父节点水平居中。
height
用于描述此 group 的高。height 属性只有组合类型中有,单类型中没有(可以在 shape 中设置 height)
这个高只用于给子节点定位。
即便当高度为零的时候,子节点也可以使用 top: 'middle' 相对于父节点垂直居中。
children
子节点列表,其中项都是一个图形元素定义。
单个类型
type
用 setOption 首次设定图形元素时必须指定。 可取值:image、text、rect、group、line、polygen、circle、arc 等。
我们也可以通过 graphic.registerShape('名称', 自定义的shape类)来注册一个自定义的图形类型。后面在介绍。
组合类型中所有的属性在这里都有,单类型中没有 children、width、height 属性。介绍几个不一样的属性:
shape
{
// 图形元素的左上角在父节点坐标系(以父节点左上角为原点)中的横坐标值。
x: 100,
// 图形元素的左上角在父节点坐标系(以父节点左上角为原点)中的纵坐标值。
y: 100,
// 图形元素的宽度。
width: 100,
// 图形元素的高度。
height: 100,
// 圆角
r: [8],
// 可以是一个属性名,或者一组属性名。 被指定的属性,在其指发生变化时,会开启过渡动画。
// 只可以指定本 shape 下的属性。
transition: ['x', 'y'],
}
注意:不建议设置 shape 中的 x、y 属性。应该采用外部 x、y 属性。这样在设置
enterFrom动画时更方便设置。
style
{
fill: 填充色
stroke:描边色
lineWidth:线的宽度
linecap: 笔触的类型,round、butt、square
lineJoin: 设置线条转折点的样式。默认 miter
shadowBlur:阴影模糊半径
shadowOffsetX: 阴影 X 方向偏移。
shadowOffsetY: 阴影 Y 方向偏移。
shadowColor: 阴影颜色。
opacity: 透明度。
}
注意:如果我们希望 shape、style 中设置的属性在入场和离场时有动画效果,那么就需要在
enterFrom、或leaveTo动画中设置 shape、style 的属性。
{
type: 'rect',
x: 100,
y: 100,
shape: {
width: 30,
height: 100,
},
// 开启 shape 中所有的属性动画,如果忽略该属性,动画也会照常执行。
transition: ['shape'],
// 设置图像入场时的位置和尺寸,
enterFrom: {
x: 0,
y: 0,
shape: {
width: 30,
height: 0
},
style: {
opacity: 0,
}
}
}
自定义图形类型
如果自定义图形类型,我们需要先通过 graphic.extendShape(options) 方法定义一个类。然后再使用 graphic.registerShape('xxx', xxx)把先前自定的图形类注册到 Echarts 中,这样方可使用。
注册一个面(图2中立方体的左侧面)
const LeftRectShape = graphic.extendShape({
// shape 是一个固定值。
shape: {
x: 0,
y: 0,
width: 0,
height: 0,
},
// ctx 其实就是 canvas.getContext('2d')。可以使用 ctx 绘制 arc、rect、bezierCurveTo 等形状
// 没有 transform 等这样的 api。
// shape 是绘制的图形的尺寸和位置信息。
buildPath: function (ctx, shape) {
// 注意:shape 其实 dataTime 调用 renderItem() 方法是返回的图形定义。
const { width: w, height: h, x, y } = shape;
// 绘制一个平面
ctx.moveTo(x, y); // 左上角
ctx.lineTo(x, y + h); // 左下角
ctx.lineTo(x + w, y + h + w * FACTOR); // 右下角
ctx.lineTo(x + w, y + w * FACTOR); // 右上角
ctx.closePath();
},
});
graphic.registerShape('LeftRectShape', LeftRectShape);
注册一个椭圆(图1中圆柱体的顶部面)
// 绘制一个椭圆
const EllipseShape = graphic.extendShape({
shape: {
x: 0,
y: 0,
width: 0,
height: 0,
},
buildPath: function (ctx, shape) {
/**
* 绘制椭圆形,这是一个固定的公式:
* x、y 表示椭圆的圆心坐标
* w、y 表示椭圆的X、Y方向的直径。
* kappa 理解为椭圆的绘制因子,固定值。
*/
const { x, y, width: w, height: h } = shape;
const kappa = 0.5522848;
const ox = (w / 2) * kappa;
const oy = (h / 2) * kappa;
const xe = x + w;
const ye = y + h;
const xm = x + w / 2;
const ym = y + h / 2;
ctx.moveTo(x, ym);
ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
ctx.closePath();
},
});
// 注册
graphic.registerShape('EllipseShape', EllipseShape);
案例一(图2)
// 我们将立方体的顶部矩形定义为一个矩形,矩形的宽度和高度比值设定为 0.5 (可自己设定合适的值)。
const FACTOR = 0.5;
useEffect(() => {
if (!echartsInstanceRef.current) {
echartsInstanceRef.current = echartsInit(dvRef.current);
// 立方体左侧面的图形类
const LeftRectShape = graphic.extendShape({
// shape 是一个固定值。
shape: {
x: 0,
y: 0,
width: 0,
height: 0,
},
// ctx 其实就是 canvas.getContext('2d')。可以使用 ctx 绘制 arc、rect、bezierCurveTo 等形状
// 没有 transform 等这样的 api。
// shape 是绘制的图形的尺寸和位置信息。
buildPath: function (ctx, shape) {
// 注意:shape 其实 dataTime 调用 renderItem() 方法是返回的图形定义。
const { width: w, height: h, x, y } = shape;
// 绘制一个平面
ctx.moveTo(x, y); // 左上角
ctx.lineTo(x, y + h); // 左下角
ctx.lineTo(x + w, y + h + w * FACTOR); // 右下角
ctx.lineTo(x + w, y + w * FACTOR); // 右上角
ctx.closePath();
},
});
// 立方体右侧面的图形类
const RightRectShape = graphic.extendShape({
shape: {
x: 0,
y: 0,
width: 0,
height: 0,
},
buildPath: function (ctx, shape) {
const { width: w, height: h, x, y } = shape;
ctx.moveTo(x, y + w * FACTOR);
ctx.lineTo(x, y + w * FACTOR + h);
ctx.lineTo(x + w, y + h);
ctx.lineTo(x + w, y);
ctx.closePath();
},
});
const TopRectShape = graphic.extendShape({
shape: {
x: 0,
y: 0,
width: 0,
height: 0,
},
buildPath: function (ctx, shape) {
const { width: w, x, y } = shape;
ctx.moveTo(x, y);
ctx.lineTo(x + w / 2, y + (w / 2) * FACTOR);
ctx.lineTo(x + w, y);
ctx.lineTo(x + w / 2, y - (w / 2) * FACTOR);
ctx.closePath();
},
});
graphic.registerShape('LeftRectShape', LeftRectShape);
graphic.registerShape('RightRectShape', RightRectShape);
graphic.registerShape('TopRectShape', TopRectShape);
}
// 左侧填充色
const colorLeft = new graphic.LinearGradient(0, 0, 0, 1, [
{ color: '#2C8CAE', offset: 0 },
{ color: '#1AE1DE', offset: 0 },
]);
// 右侧填充色
const colorRight = new graphic.LinearGradient(0, 0, 0, 1, [
{ color: '#3398BC', offset: 0 },
{ color: '#60FFFD', offset: 0 },
]);
const options: EChartsOption = {
tooltip: {
show: true,
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
legend: {
show: true,
orient: 'horizontal',
icon: 'circle',
textStyle: {
color: '#fff',
},
},
grid: {
left: 20,
right: 0,
bottom: 0,
top: 70,
containLabel: true,
},
xAxis: [
{
show: true,
type: 'category',
boundaryGap: true,
axisLabel: {
color: '#fff',
fontSize: 10,
rotate: 30,
margin: 10,
},
axisTick: { show: false },
splitLine: { show: false },
axisLine: {
show: true,
lineStyle: {
width: 1,
color: '#37B6F2',
opacity: 0.32,
},
},
},
],
yAxis: [
{
show: true,
alignTicks: true,
type: 'value',
name: '单位(个)',
nameTextStyle: {
color: '#ddd',
fontSize: 10,
align: 'center',
},
nameGap: 20,
nameLocation: 'end',
axisLabel: {
inside: false,
color: '#fff',
fontSize: 10,
},
axisLine: {
show: false,
lineStyle: {
width: 1,
color: '#37B6F2',
opacity: 0.32,
},
},
splitLine: { show: false },
},
{
show: true,
type: 'value',
max: 100,
name: '单位(%)',
nameTextStyle: {
color: '#ddd',
fontSize: 10,
align: 'center',
},
nameGap: 20,
nameLocation: 'end',
axisLabel: {
inside: false,
color: '#fff',
fontSize: 10,
},
axisLine: { show: false },
splitLine: { show: false },
},
],
series: [
{
type: 'custom',
color: '#0BF6FF',
name: props.source[0][1],
// name: '省级中心村建设数量(单位:个)',
yAxisIndex: 0,
xAxisIndex: 0,
animation: true,
renderItem: function (params: any, api: any): any {
const [x, y] = api.coord([api.value(0), api.value(1)]);
const [w, h] = api.size([api.value(0), api.value(1)]);
const { y: gridY, height: gridH } = params.coordSys;
// w 表示整个图像的宽度,理论上我们将立方体分为 LeftRectShape 和 RightRectShape,
// 所以每个 rect 的宽度就是 w/2,
const rectWidth = w / 2 * 0.5;
return {
type: 'group',
children: api.value(1) > 0 ? [
{
type: 'LeftRectShape',
x: x - rectWidth,
y: y,
shape: {
// 立方体左侧面的宽度
width: rectWidth,
// 立方体的高度
height: h - rectWidth * FACTOR,
},
enterFrom: {
// 淡入
style: { opacity: 0 },
// y: 0,
shape: {
height: 0,
},
y: gridH + gridY,
},
style: {
fill: colorLeft,
},
},
{
type: 'RightRectShape',
x: x,
y: y,
shape: {
// 立方体左侧面的宽度
width: rectWidth,
// 立方体的高度
height: h - rectWidth * FACTOR,
},
enterFrom: {
// 淡入
style: { opacity: 0 },
// y: 0,
shape: {
height: 0,
},
y: gridH + gridY,
},
style: {
fill: colorRight,
},
},
{
type: 'TopRectShape',
x: x - rectWidth,
y: y,
shape: {
// 立方体顶部矩形的宽度
width: rectWidth * 2,
// 立方体的高度
height: h - rectWidth * FACTOR,
},
enterFrom: {
// 淡入
style: { opacity: 0 },
// y: 0,
shape: {
height: 0,
},
y: gridH + gridY,
},
style: {
fill: '#38E3FF',
},
},
/* 绘制 bar 的背景 */
{
type: 'LeftRectShape',
shape: {
x: x - rectWidth,
y: gridY,
// 立方体的宽度
width: rectWidth,
// 立方体的高度
height: gridH - rectWidth * FACTOR,
},
enterFrom: {
// 淡入
style: { opacity: 0 },
},
style: {
fill: colorLeft,
opacity: 0.2,
},
},
{
type: 'RightRectShape',
shape: {
x: x,
y: gridY,
// 立方体的宽度
width: rectWidth,
// 立方体的高度
height: gridH - rectWidth * FACTOR,
},
enterFrom: {
// 淡入
style: { opacity: 0, },
},
style: {
fill: colorRight,
opacity: 0.2,
},
},
{
type: 'TopRectShape',
shape: {
x: x - rectWidth,
y: gridY,
// 立方体顶部矩形的宽度
width: rectWidth * 2,
// 立方体的高度
height: gridH - rectWidth * FACTOR,
},
enterFrom: {
// 淡入
style: { opacity: 0 },
},
style: {
fill: '#38E3FF',
opacity: 0.2,
},
},
] : [],
};
},
tooltip: {
show: true,
valueFormatter: (value: any) => `${value}个`,
},
},
{
type: 'line',
name: props.source[0][2],
color: '#229AFF',
// name: '覆盖率(单位:%)',
yAxisIndex: 1,
xAxisIndex: 0,
encode: {
x: '城市名称',
y: '覆盖率',
},
smooth: true,
lineStyle: {
color: '#229AFF',
},
tooltip: {
valueFormatter: (value: any) => `${value.toFixed(2)}%`,
},
},
],
dataset: {
source: props.source,
},
};
echartsInstanceRef.current.setOption(options);
}, [props.source]);
案例二(图3)
// 绘制一个椭圆
const EllipseShape = graphic.extendShape({
shape: {
x: 0,
y: 0,
width: 0,
height: 0,
},
buildPath: function (ctx, shape) {
/**
* 绘制椭圆形,这是一个固定的公式:
* x、y 表示椭圆的圆心坐标
* w、y 表示椭圆的 X、Y 方向的直径。
* kappa 理解为椭圆的绘制因子,固定值。
*/
const { x, y, width: w, height: h } = shape;
const kappa = 0.5522848;
const ox = (w / 2) * kappa;
const oy = (h / 2) * kappa;
const xe = x + w;
const ye = y + h;
const xm = x + w / 2;
const ym = y + h / 2;
ctx.moveTo(x, ym);
ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
ctx.closePath();
},
});
graphic.registerShape('EllipseShape', EllipseShape);
series: {
type: 'custom',
name: props.source[0][1],
animation: true,
renderItem: function (params: any, api: any): any {
// 注意,x 的坐标默认和 X 轴的刻度线时对齐的。
const [x, y] = api.coord([api.value(0), api.value(1)]);
const [w, h] = api.size([api.value(0), api.value(1)]);
const { y: gridY, height: gridH } = params.coordSys;
// w 表示图形的宽度,这个宽度是 echarts 自己算出来的;
// 我们应该根据 w 来计算,否则当数量量变化时无法实现图像的自适应。
// 0.4 表示每个图形之间就都有 0.6w 的间隙了,更美观。
const ellipseRadiusX = w / 2 * 0.4;
// 椭圆的 Y 轴半径是 X 轴半径的一般
const ellipseRadiusY = ellipseRadiusX * 0.5;
return {
type: 'group',
children: api.value(1) > 0 [
{
// 绘制矩形
type: 'rect',
x: x - ellipseRadiusX,
y: y,
shape: {
// 圆柱体的宽度
width: ellipseRadiusX * 2,
// 圆柱体的高度
height: h - ellipseRadiusY * 2,
},
style: {
fill: colorSteps,
},
enterFrom: {
style: { opacity: 0 },
// 高度为 0
shape: { height: 0 },
// 入场动画,图像的 y 在X 轴线位置
y: gridY + gridH,
}
},
{
// 圆柱形顶部的椭圆绘制
type: 'EllipseShape',
x: x - ellipseRadiusX,
y: y - ellipseRadiusY,
shape: {
width: ellipseRadiusX * 2,
height: ellipseRadiusY * 2,
},
style: {
fill: '#21F3FF',
},
// 顶部椭圆的动画是从下往上的,和圆柱体的高度递增动画保持一致。
enterFrom: {
style: { opacity: 0 },
y: gridY + gridH,
}
},
{
// 圆柱形底部的椭圆绘制
type: 'EllipseShape',
x: x - ellipseRadiusX,
// 与 X 轴线保持一定的距离(图像向上偏移 10 pixels)。
// 注意,椭圆圆心坐标为(x,y + radiusY),所以这里是 ellipseRadiusY * 2 - 10。
y: gridY + gridH - ellipseRadiusY * 2 - 10,
shape: {
width: ellipseRadiusX * 2,
height: ellipseRadiusY * 2,
},
style: {
fill: '#1F61EA',
opacity: 0.8,
},
// 圆柱体的底部椭圆的只需要设置一个透明度渐变就行。他不需要移动的
enterFrom: {
style: { opacity: 0 },
}
},
] : [],
};
},
},
案例三(图三)
series: {
type: 'custom',
name: props.source[0][1],
animation: true,
renderItem: function (params: any, api: any): any {
const [x, y] = api.coord([api.value(0), api.value(1)]);
const [w, h] = api.size([api.value(0), api.value(1)]);
const { y: gridY, height: gridH } = params.coordSys;
const H = gridY + gridH;
const ellipseRadiusX = w * 0.5 * 0.25;
const ellipseRadiusY = ellipseRadiusX * 0.5;
return {
type: 'group',
children: api.value(1) > 0 [
{
// 绘制矩形
type: 'rect',
// 要保证圆柱体的中心和X轴刻度线保持对齐
x: x - ellipseRadiusX,
y: y,
shape: {
// 圆柱体的宽度,
width: ellipseRadiusX * 2,
// 圆柱体的高度
height: h - ellipseRadiusY,
},
style: {
fill: new graphic.LinearGradient(0, 0, 0, 1, [
{ color: 'rgba(18, 229, 237, 0.9)', offset: 0 },
{ color: 'rgba(18, 229, 237, 0)', offset: 1 },
]),
},
// 动画
enterFrom: {
style: { opacity: 0 },
y: H - ellipseRadiusY * 2 * 2.5,
shape: { height: 0 },
}
},
{
// 圆柱形底部的椭圆环(外)
type: 'EllipseShape',
x: x - ellipseRadiusX * 2.5,
// 顶部椭圆外环的边应该与 X 轴线对齐,减去 ellipseRadiusY * 2 * 2.5
// 是因为椭圆的圆心坐标是(x, y + radiusY),所以才要乘以 2
y: H - ellipseRadiusY * 2 * 2.5,
// 2.5 表示缩放因子
shape: {
width: ellipseRadiusX * 2 * 2.5,
height: ellipseRadiusY * 2 * 2.5,
},
style: {
stroke: new graphic.LinearGradient(0, 0, 0, 1, [
{ color: '#38A0D6', offset: 1 },
{ color: '#6DCDE6', offset: 0 },
]),
},
enterFrom: {
style: { opacity: 0 },
}
},
{
// 圆柱形底部的椭圆环(内)
type: 'EllipseShape',
x: x - ellipseRadiusX * 1.8,
y: H - ellipseRadiusY * 2 * 2.5,
shape: {
width: ellipseRadiusX * 2 * 1.8,
height: ellipseRadiusY * 2 * 1.8,
},
style: {
stroke: new graphic.LinearGradient(0, 0, 0, 1, [
{ color: '#38A0D6', offset: 1 },
{ color: '#6DCDE6', offset: 0 },
]),
},
enterFrom: {
style: { opacity: 0 },
}
},
{
// 圆柱形顶部的椭圆绘制
type: 'EllipseShape',
x: x - ellipseRadiusX,
y: y - ellipseRadiusY,
shape: {
width: ellipseRadiusX * 2,
height: ellipseRadiusY * 2,
},
style: {
fill: new graphic.LinearGradient(0, 0, 0, 1, [
{ color: '#00F0FF', offset: 0 },
{ color: '#7BF7FF', offset: 1 },
]),
},
enterFrom: {
style: { opacity: 0 },
y: H - ellipseRadiusY * 2 * 2.5,
}
},
{
// 圆柱形底部的椭圆绘制
type: 'EllipseShape',
x: x - ellipseRadiusX,
y: H - ellipseRadiusY * 2 * 2.2,
shape: {
width: ellipseRadiusX * 2,
height: ellipseRadiusY * 2,
},
style: {
fill: new graphic.LinearGradient(0, 0, 0, 1, [
{ color: '#38A0D6', offset: 1 },
{ color: '#6DCDE6', offset: 0 },
]),
},
enterFrom: {
style: { opacity: 0 },
}
},
] : [],
};
},