diff --git a/__tests__/integration/issue-6396.spec.ts b/__tests__/integration/issue-6396.spec.ts new file mode 100644 index 0000000000..c6500c9d93 --- /dev/null +++ b/__tests__/integration/issue-6396.spec.ts @@ -0,0 +1,20 @@ +import { issue6396 as render } from '../plots/bugfix/issue-6396'; +import { createNodeGCanvas } from './utils/createNodeGCanvas'; +import { sleep } from './utils/sleep'; +import './utils/useSnapshotMatchers'; + +describe('issue6396', () => { + const canvas = createNodeGCanvas(800, 500); + + it('issue6396.render() should render expected lineXY with arrow', async () => { + const { finished } = render({ canvas }); + await finished; + await sleep(20); + const dir = `${__dirname}/snapshots/bugfix`; + await expect(canvas).toMatchDOMSnapshot(dir, render.name); + }); + + afterAll(() => { + canvas?.destroy(); + }); +}); diff --git a/__tests__/integration/snapshots/bugfix/issue6396.svg b/__tests__/integration/snapshots/bugfix/issue6396.svg new file mode 100644 index 0000000000..0a4fe25d13 --- /dev/null +++ b/__tests__/integration/snapshots/bugfix/issue6396.svg @@ -0,0 +1,531 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + value = 300 + + + + + + + + + + + + + value = 600 + + + + + + + + + + + + \ No newline at end of file diff --git a/__tests__/main.ts b/__tests__/main.ts index 51ce8e3365..4cff379f58 100644 --- a/__tests__/main.ts +++ b/__tests__/main.ts @@ -10,6 +10,7 @@ import * as interactions from './plots/interaction'; import * as animations from './plots/animation'; import * as tooltips from './plots/tooltip'; import * as apis from './plots/api'; +import * as bugfix from './plots/bugfix'; const tests = { ...createSpecRender(namespace(statics, 'static')), @@ -17,6 +18,7 @@ const tests = { ...createSpecRender(namespace(interactions, 'interaction')), ...createSpecRender(namespace(animations, 'animation')), ...createAPIRender(namespace(apis, 'api')), + ...createAPIRender(namespace(bugfix, 'bugfix')), }; const renderers = { diff --git a/__tests__/plots/bugfix/index.ts b/__tests__/plots/bugfix/index.ts new file mode 100644 index 0000000000..1ef3bf1722 --- /dev/null +++ b/__tests__/plots/bugfix/index.ts @@ -0,0 +1 @@ +export { issue6396 } from './issue-6396'; diff --git a/__tests__/plots/bugfix/issue-6396.ts b/__tests__/plots/bugfix/issue-6396.ts new file mode 100644 index 0000000000..265f004727 --- /dev/null +++ b/__tests__/plots/bugfix/issue-6396.ts @@ -0,0 +1,87 @@ +import { Chart } from '../../../src'; + +export function issue6396(context) { + const { container, canvas } = context; + + const chart = new Chart({ + container: container, + canvas, + }); + + let i = 1; + + chart.data([ + 264, 417, 438, 887, 309, 397, 550, 575, 563, 430, 525, 592, 492, 467, 513, + 546, 983, 340, 539, 243, 226, 192, + ]); + chart.animate(false); + + chart + .interval() + .encode('x', (_, idx) => idx) + .encode('y', (d) => d) + .axis(false); + + const lineY = chart + .lineY() + .style('stroke', 'red') + .style('lineDash', [2, 2]) + .style('arrow', true); + + const lineY1 = chart + .lineY() + .style('stroke', 'green') + .style('lineDash', [2, 2]) + .style('arrow', true); + + lineY.data([i * 100]).label([ + { + text: `value = ${i * 100}`, + position: 'right', + dx: -10, + textBaseline: 'bottom', + }, + ]); + + lineY1.data([i * 200]).label([ + { + text: `value = ${i * 200}`, + position: 'right', + dx: -10, + textBaseline: 'bottom', + }, + ]); + + chart.interaction('tooltip', { + render: (e, { title, items }) => items[0].value, + }); + + const intervalId = setInterval(() => { + i++; + if (i > 2) { + clearInterval(intervalId); + } + lineY.data([i * 100]).label([ + { + text: `value = ${i * 100}`, + position: 'right', + dx: -10, + textBaseline: 'bottom', + }, + ]); + lineY1.data([i * 200]).label([ + { + text: `value = ${i * 200}`, + position: 'right', + dx: -10, + textBaseline: 'bottom', + }, + ]); + chart.render(); + }, 10); + const finished = chart.render(); + return { + chart, + finished, + }; +} diff --git a/src/animation/morphing.ts b/src/animation/morphing.ts index 74860b3aa3..5ed5ddb7eb 100644 --- a/src/animation/morphing.ts +++ b/src/animation/morphing.ts @@ -5,6 +5,7 @@ import { Path, Shape, } from '@antv/g'; +import { get } from '@antv/util'; import { AnimationComponent as AC } from '../runtime'; import { copyAttributes } from '../utils/helper'; import { Animation } from './types'; @@ -158,6 +159,15 @@ function shape2path(shape: DisplayObject): string { if (hasSubPath(path)) return; return path; } +// Check if the path has a markerEnd | markerStart +function hasMarker(shape: DisplayObject): boolean { + const { nodeName } = shape; + if (nodeName === 'path') { + const attributes = get(shape, 'attributes'); + return attributes.markerEnd || attributes.markerStart; + } + return false; +} function oneToOne( shape: DisplayObject, @@ -174,8 +184,10 @@ function oneToOne( const toPath = shape2path(to); const isSameNodes = fromName === toName && fromName !== 'path'; const hasNonPathNode = fromPath === undefined || toPath === undefined; - if (isSameNodes || hasNonPathNode) return shapeToShape(from, to, timeEffect); - + // Path with mark can not use animate like ordinary path. + const isPathWithMarker = hasMarker(from) || hasMarker(to); + if (isSameNodes || hasNonPathNode || isPathWithMarker) + return shapeToShape(from, to, timeEffect); const pathShape = maybePath(shape, fromPath); // Convert Path will take transform, anchor, etc into account, // so there is no need to specify these attributes in keyframes.