[Bug] 单值环形图更新后,循环动画异常
Open
Nobody has claimed this yet.
bug
- Dominant language
- TypeScript
- Stars
- 1.8k
- Forks
- 221
- Avg merge
- 1d 6h
- Merged PRs (30d)
- 26
Description
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
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by running the TypeScript reproduction using the provided circularProgress specs in version 2.0.3, then compare behavior before and after the data update from 0.8139 to 0.1. Trace the animationNormal progress configuration and determine what behavior should replace the abnormal looping animation after the update.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- data-visualization
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100