Canvas: Add support for line animation (#85556)

This commit is contained in:
Adela Almasan 2024-04-05 08:53:40 -06:00 committed by GitHub
parent 8e8bfae761
commit 883a41e8aa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 103 additions and 18 deletions

View File

@ -5,6 +5,7 @@ import { PanelOptionsSupplier } from '@grafana/data/src/panel/PanelPlugin';
import { ColorDimensionConfig, ScaleDimensionConfig } from '@grafana/schema';
import { config } from 'app/core/config';
import { LineStyleConfig } from '../../plugins/panel/canvas/editor/LineStyleEditor';
import { DimensionContext } from '../dimensions';
import { BackgroundConfig, Constraint, LineConfig, Placement, StandardEditorConfig } from './types';
@ -56,7 +57,7 @@ export interface CanvasConnection {
path: ConnectionPath;
color?: ColorDimensionConfig;
size?: ScaleDimensionConfig;
lineStyle?: string;
lineStyle?: LineStyleConfig;
vertices?: ConnectionCoordinates[];
radius?: ScaleDimensionConfig;
direction?: ConnectionDirection;

View File

@ -144,12 +144,8 @@ export const ConnectionSVG = ({
const xDist = x2 - x1;
const yDist = y2 - y1;
const { strokeColor, strokeWidth, strokeRadius, arrowDirection, lineStyle } = getConnectionStyles(
info,
scene,
defaultArrowSize,
defaultArrowDirection
);
const { strokeColor, strokeWidth, strokeRadius, arrowDirection, lineStyle, shouldAnimate } =
getConnectionStyles(info, scene, defaultArrowSize, defaultArrowDirection);
const isSelected = selectedConnection === v && scene.panel.context.instanceState.selectedConnection;
@ -345,6 +341,16 @@ export const ConnectionSVG = ({
? `url(#${CONNECTION_HEAD_ID_END})`
: undefined;
const getAnimationDirection = () => {
let values = '100;0';
if (arrowDirection === ConnectionDirection.Reverse) {
values = '0;100';
}
return values;
};
return (
<svg className={styles.connection} key={idx}>
<g onClick={() => selectConnection(v)}>
@ -389,10 +395,22 @@ export const ConnectionSVG = ({
stroke={strokeColor}
strokeWidth={strokeWidth}
strokeDasharray={lineStyle}
strokeDashoffset={1}
fill={'none'}
markerEnd={markerEnd}
markerStart={markerStart}
/>
>
{shouldAnimate && (
<animate
attributeName="stroke-dashoffset"
values={getAnimationDirection()}
dur="5s"
calcMode="linear"
repeatCount="indefinite"
fill={'freeze'}
/>
)}
</path>
{isSelected && (
<g>
{vertices.map((value, index) => {
@ -455,12 +473,24 @@ export const ConnectionSVG = ({
markerEnd={markerEnd}
markerStart={markerStart}
strokeDasharray={lineStyle}
strokeDashoffset={1}
x1={x1}
y1={y1}
x2={x2}
y2={y2}
cursor={connectionCursorStyle}
/>
>
{shouldAnimate && (
<animate
attributeName="stroke-dashoffset"
values={getAnimationDirection()}
dur="5s"
calcMode="linear"
repeatCount="indefinite"
fill={'freeze'}
/>
)}
</line>
{isSelected && (
<circle
id={CONNECTION_VERTEX_ADD_ID}

View File

@ -1,24 +1,63 @@
import React, { useCallback } from 'react';
import { SelectableValue, StandardEditorProps } from '@grafana/data';
import { RadioButtonGroup } from '@grafana/ui/src';
import { Field, RadioButtonGroup, Switch } from '@grafana/ui/src';
import { LineStyle } from '../types';
const options: Array<SelectableValue<LineStyle>> = [
{ value: LineStyle.Solid, label: 'Solid' },
{ value: LineStyle.Dashed, label: 'Dashed' },
{ value: LineStyle.Dotted, label: 'Dotted' },
];
export const LineStyleEditor = ({ value, onChange }: StandardEditorProps<string, undefined, undefined>) => {
const lineStyle = value ?? LineStyle.Solid;
export interface LineStyleConfig {
style: LineStyle;
animate?: boolean;
}
type Props = StandardEditorProps<LineStyleConfig>;
export const defaultLineStyleConfig: LineStyleConfig = {
style: LineStyle.Solid,
animate: false,
};
export const LineStyleEditor = ({ value, onChange }: Props) => {
if (!value) {
value = defaultLineStyleConfig;
} else if (typeof value !== 'object') {
value = {
style: value,
animate: false,
};
}
const onLineStyleChange = useCallback(
(lineStyle: string) => {
onChange(lineStyle);
(lineStyle: LineStyle) => {
onChange({ ...value, style: lineStyle });
},
[onChange]
[onChange, value]
);
return <RadioButtonGroup value={lineStyle} options={options} onChange={onLineStyleChange} fullWidth />;
const onAnimateChange = useCallback(
(animate: boolean) => {
onChange({ ...value, animate });
},
[onChange, value]
);
return (
<>
<RadioButtonGroup value={value.style} options={options} onChange={onLineStyleChange} fullWidth />
{value.style !== LineStyle.Solid && (
<>
<br />
<Field label="Animate">
<Switch value={value.animate} onChange={(e) => onAnimateChange(e.currentTarget.checked)} />
</Field>
</>
)}
</>
);
};

View File

@ -47,9 +47,11 @@ export interface ConnectionState {
export enum LineStyle {
Solid = 'solid',
Dashed = 'dashed',
Dotted = 'dotted',
}
export enum StrokeDasharray {
Solid = '0',
Dashed = '8 8',
Dotted = '3',
}

View File

@ -403,8 +403,21 @@ export const getConnectionStyles = (
const strokeWidth = info.size ? scene.context.getScale(info.size).get(lastRowIndex) : defaultArrowSize;
const strokeRadius = info.radius ? scene.context.getScale(info.radius).get(lastRowIndex) : 0;
const arrowDirection = info.direction ? info.direction : defaultArrowDirection;
const lineStyle = info.lineStyle === LineStyle.Dashed ? StrokeDasharray.Dashed : StrokeDasharray.Solid;
return { strokeColor, strokeWidth, strokeRadius, arrowDirection, lineStyle };
const lineStyle = getLineStyle(info.lineStyle?.style);
const shouldAnimate = info.lineStyle?.animate;
return { strokeColor, strokeWidth, strokeRadius, arrowDirection, lineStyle, shouldAnimate };
};
const getLineStyle = (lineStyle?: LineStyle) => {
switch (lineStyle) {
case LineStyle.Dashed:
return StrokeDasharray.Dashed;
case LineStyle.Dotted:
return StrokeDasharray.Dotted;
default:
return StrokeDasharray.Solid;
}
};
export const getParentBoundingClientRect = (scene: Scene) => {