VChart
VChart copied to clipboard
[Bug] 单值环形图更新后,循环动画异常
Version
vscreen
Link to Minimal Reproduction
2.0.3
Steps to Reproduce
const spec = {
"theme": {},
"type": "circularProgress",
"valueField": "measureValue",
"radiusField": "measureValue",
"padding": {
"left": 6,
"right": 6,
"top": 6,
"bottom": 6
},
"outerRadius": 0.92,
"innerRadius": 0.8200000000000001,
"cornerRadius": 20,
"track": {
"style": {
"fill": "#434343",
"fillOpacity": 0.29
}
},
"categoryField": "name",
"roundCap": false,
"data": [
{
"id": "data",
"values": [
{
"current": 0.8138999999999998,
"name": "Sales Proportion",
"emptyName": "",
"percent": "-",
"measureValue": 0.8138999999999998,
"detail": "undefined"
}
]
}
],
"progress": {
"style": {
"cornerRadius": 20,
"fill": "#006EFF",
"fillOpacity": 1
},
"state": {
"selected": {}
}
},
"axes": [
{
"type": "linear",
"orient": "angle",
"min": 0,
"max": 1,
"grid": {
"visible": false
},
"label": {
"visible": false,
"autoHideSeparation": 4
},
"ticks": false
}
],
"indicator": {
"pickable": false,
"visible": true,
"fixed": true,
"title": {
"space": 5,
"visible": false,
"style": {
"visible": false,
"text": "-",
"fontSize": 12,
"fill": "#FFF",
"fontWeight": "normal",
"fontFamily": "D-DIN",
"pickable": false
},
"autoLimit": false
},
"content": [
{
"space": 5,
"visible": true,
"style": {
"visible": true,
"text": "81.39%",
"fontSize": 28,
"fill": "#FFF",
"fontWeight": "bold",
"fontFamily": "D-DIN-Bold",
"pickable": false
},
"autoLimit": false
},
{
"space": 5,
"visible": true,
"style": {
"visible": true,
"text": "Sales Proportion",
"fontSize": 14,
"fill": "rgba(255,255,255,0.7)",
"fontWeight": "normal",
"fontFamily": "D-DIN",
"pickable": false
},
"autoLimit": false
}
]
},
"region": [
{
"clip": true
}
],
"background": "rgba(0, 0, 0, 1)",
"animation": true,
"tooltip": {
"visible": true,
"renderMode": "canvas",
"mark": {
"visible": false
},
"style": {
"panel": {
"padding": {
"top": 5,
"bottom": 10,
"left": 10,
"right": 10
},
"backgroundColor": "rgba(8, 28, 48, 0.95)",
"border": {
"color": "#CFCFCF",
"width": 0,
"radius": 2
},
"shadow": {
"x": 0,
"y": 4,
"blur": 12,
"spread": 0,
"color": "rgba(0, 0, 0, 0.2)"
}
},
"titleLabel": {
"fontSize": 14,
"fontColor": "#FFF",
"fontWeight": "bold",
"fontFamily": "D-DIN",
"align": "left",
"lineHeight": 18
},
"keyLabel": {
"fontSize": 12,
"fontColor": "rgba(255,255,255,0.65)",
"fontWeight": "normal",
"fontFamily": "SourceHanSansCN-Normal",
"align": "left",
"lineHeight": 18
},
"valueLabel": {
"fontSize": 12,
"fontColor": "#FFF",
"fontWeight": "normal",
"fontFamily": "D-DIN",
"align": "right",
"lineHeight": 18
},
"shape": {
"size": 10,
"spacing": 10,
"shapeLineWidth": 0
},
"spaceRow": 10
},
"dimension": {
"visible": false
}
},
"crosshair": {
"xField": {
"line": {
"style": {
"fillOpacity": 1,
"fill": "rgba(80,156,255,0.1)"
}
},
"visible": false
},
"yField": {
"line": {
"style": {
"fillOpacity": 1,
"fill": "rgba(80,156,255,0.1)"
}
},
"visible": false
}
},
"morph": {
"enable": false
},
"plotLayout": {
"clip": false
},
"select": {
"enable": true
},
"animationAppear": {
"progress": {
"loop": false,
"duration": 1000,
"easing": "linear"
}
},
"animationNormal": {
"progress": {
"loop": true,
"startTime": 1200,
"delayAfter": 1200,
"duration": 1000,
"easing": "linear",
"controlOptions": {
"immediatelyApply": false
},
"channel": {
startAngle: {
from: (...args) => {
const { startAngle, endAngle } = args[1].attribute
const minAngle = Math.min(startAngle, endAngle)
const maxAngle = Math.max(startAngle, endAngle)
return minAngle
},
to: (...args) => {
const { startAngle, endAngle } = args[1].attribute
const minAngle = Math.min(startAngle, endAngle)
const maxAngle = Math.max(startAngle, endAngle)
return minAngle
},
},
endAngle: {
from: (...args) => {
const { startAngle, endAngle } = args[1].attribute
const minAngle = Math.min(startAngle, endAngle)
const maxAngle = Math.max(startAngle, endAngle)
return minAngle
},
to: (...args) => {
const { startAngle, endAngle } = args[1].attribute
const minAngle = Math.min(startAngle, endAngle)
const maxAngle = Math.max(startAngle, endAngle)
return maxAngle
},
},
}
}
},
"animationEnter": {
"duration": 1000,
"easing": "circInOut"
},
"animationUpdate": {
"duration": 1000,
"easing": "circInOut"
},
"animationExit": {},
"hash": "96bb1fa575d942c43c517b6d7d66cdb2",
"width": 400,
"height": 225
}
const spec2 = {
"theme": {},
"type": "circularProgress",
"valueField": "measureValue",
"radiusField": "measureValue",
"padding": {
"left": 6,
"right": 6,
"top": 6,
"bottom": 6
},
"outerRadius": 0.92,
"innerRadius": 0.8200000000000001,
"cornerRadius": 20,
"track": {
"style": {
"fill": "#434343",
"fillOpacity": 0.29
}
},
"categoryField": "name",
"roundCap": false,
"data": [
{
"id": "data",
"values": [
{
"current": 0.1,
"name": "Sales Proportion",
"emptyName": "",
"percent": "-",
"measureValue": 0.1,
"detail": "undefined"
}
]
}
],
"progress": {
"style": {
"cornerRadius": 20,
"fill": "#006EFF",
"fillOpacity": 1
},
"state": {
"selected": {}
}
},
"axes": [
{
"type": "linear",
"orient": "angle",
"min": 0,
"max": 1,
"grid": {
"visible": false
},
"label": {
"visible": false,
"autoHideSeparation": 4
},
"ticks": false
}
],
"indicator": {
"pickable": false,
"visible": true,
"fixed": true,
"title": {
"space": 5,
"visible": false,
"style": {
"visible": false,
"text": "-",
"fontSize": 12,
"fill": "#FFF",
"fontWeight": "normal",
"fontFamily": "D-DIN",
"pickable": false
},
"autoLimit": false
},
"content": [
{
"space": 5,
"visible": true,
"style": {
"visible": true,
"text": "10.00%",
"fontSize": 28,
"fill": "#FFF",
"fontWeight": "bold",
"fontFamily": "D-DIN-Bold",
"pickable": false
},
"autoLimit": false
},
{
"space": 5,
"visible": true,
"style": {
"visible": true,
"text": "Sales Proportion",
"fontSize": 14,
"fill": "rgba(255,255,255,0.7)",
"fontWeight": "normal",
"fontFamily": "D-DIN",
"pickable": false
},
"autoLimit": false
}
]
},
"region": [
{
"clip": true
}
],
"background": "rgba(0, 0, 0, 1)",
"animation": true,
"tooltip": {
"visible": true,
"renderMode": "canvas",
"mark": {
"visible": false
},
"style": {
"panel": {
"padding": {
"top": 5,
"bottom": 10,
"left": 10,
"right": 10
},
"backgroundColor": "rgba(8, 28, 48, 0.95)",
"border": {
"color": "#CFCFCF",
"width": 0,
"radius": 2
},
"shadow": {
"x": 0,
"y": 4,
"blur": 12,
"spread": 0,
"color": "rgba(0, 0, 0, 0.2)"
}
},
"titleLabel": {
"fontSize": 14,
"fontColor": "#FFF",
"fontWeight": "bold",
"fontFamily": "D-DIN",
"align": "left",
"lineHeight": 18
},
"keyLabel": {
"fontSize": 12,
"fontColor": "rgba(255,255,255,0.65)",
"fontWeight": "normal",
"fontFamily": "SourceHanSansCN-Normal",
"align": "left",
"lineHeight": 18
},
"valueLabel": {
"fontSize": 12,
"fontColor": "#FFF",
"fontWeight": "normal",
"fontFamily": "D-DIN",
"align": "right",
"lineHeight": 18
},
"shape": {
"size": 10,
"spacing": 10,
"shapeLineWidth": 0
},
"spaceRow": 10
},
"dimension": {
"visible": false
}
},
"crosshair": {
"xField": {
"line": {
"style": {
"fillOpacity": 1,
"fill": "rgba(80,156,255,0.1)"
}
},
"visible": false
},
"yField": {
"line": {
"style": {
"fillOpacity": 1,
"fill": "rgba(80,156,255,0.1)"
}
},
"visible": false
}
},
"morph": {
"enable": false
},
"plotLayout": {
"clip": false
},
"select": {
"enable": true
},
"animationAppear": {
"progress": {
"loop": false,
"duration": 1000,
"easing": "linear"
}
},
"animationNormal": {
"progress": {
"loop": true,
"startTime": 1200,
"delayAfter": 1200,
"duration": 1000,
"easing": "linear",
"controlOptions": {
"immediatelyApply": false
},
"channel": {
"channel": {
startAngle: {
from: (...args) => {
const { startAngle, endAngle } = args[1].attribute
const minAngle = Math.min(startAngle, endAngle)
const maxAngle = Math.max(startAngle, endAngle)
return minAngle
},
to: (...args) => {
const { startAngle, endAngle } = args[1].attribute
const minAngle = Math.min(startAngle, endAngle)
const maxAngle = Math.max(startAngle, endAngle)
return minAngle
},
},
endAngle: {
from: (...args) => {
const { startAngle, endAngle } = args[1].attribute
const minAngle = Math.min(startAngle, endAngle)
const maxAngle = Math.max(startAngle, endAngle)
return minAngle
},
to: (...args) => {
const { startAngle, endAngle } = args[1].attribute
const minAngle = Math.min(startAngle, endAngle)
const maxAngle = Math.max(startAngle, endAngle)
return maxAngle
},
},
}
}
}
},
"animationEnter": {
"duration": 1000,
"easing": "circInOut"
},
"animationUpdate": {
"duration": 1000,
"easing": "circInOut"
},
"animationExit": {},
"hash": "96bb1fa575d942c43c517b6d7d66cdb2",
"width": 400,
"height": 225
}
const getAnimationSpec = spec => {
if (!spec) {
return {};
}
return {
animation: spec.animation,
animationAppear: spec.animationAppear,
animationUpdate: spec.animationUpdate,
animationEnter: spec.animationEnter,
animationExit: spec.animationExit
};
};
const chartUpdate = (chartInstance, preChartSpec, curSpec) => {
const preAnimationSpec = getAnimationSpec(preChartSpec);
const curAnimationSpec = getAnimationSpec(curSpec);
const reAnimate = JSON.stringify(preAnimationSpec) !== JSON.stringify(curAnimationSpec);
// VChart图表分批updateSpec, 流程待图表库底层优化
// step1. update除data外的spec
// step2. updateData
const specWithoutDataChange = {
...curSpec,
data: preChartSpec?.data ?? [
{
id: 'data',
values: []
}
]
};
const curData = curSpec.data;
chartInstance.updateSpec(specWithoutDataChange, false, undefined);
chartInstance.updateFullDataSync(curData, true);
chartInstance.reRunNormalAnimation()
};
const vchart = new VChart(spec, { dom: CONTAINER_ID });
vchart.renderSync();
setTimeout(() => {
chartUpdate(vchart, spec, spec2)
}, 1000)
// Just for the convenience of console debugging, DO NOT COPY!
window['vchart'] = vchart;
Current Behavior
Expected Behavior
更新后,循环动画正常
Environment
- OS:
- Browser:
- Framework:
Any additional comments?
No response