@libs-ui/components-draw-line
v0.2.356-3
Published
> Directive vẽ đường SVG nối giữa các điểm với nhiều chế độ đường cong, hỗ trợ né obstacle và kéo thả mũi tên.
Downloads
1,304
Readme
@libs-ui/components-draw-line
Directive vẽ đường SVG nối giữa các điểm với nhiều chế độ đường cong, hỗ trợ né obstacle và kéo thả mũi tên.
Giới thiệu
LibsUiComponentsDrawLineDirective là một standalone Angular directive dùng để vẽ các đường nối SVG giữa hai điểm với nhiều chế độ vẽ khác nhau (quart-in, horizontal, vertical, horizontal-single-curve, vertical-single-curve). Directive hỗ trợ tự động né vật cản (obstacle avoidance), kéo thả điểm đầu mũi tên và xác định vùng có thể kết nối (reachable point range).
Tính năng
- ✅ 5 chế độ vẽ đường: quart-in, horizontal, vertical, horizontal-single-curve, vertical-single-curve
- ✅ Tự động né vật cản (obstacle avoidance) với obstacleRect
- ✅ Kéo thả mũi tên endpoint bằng chuột (drag-and-drop)
- ✅ Vùng có thể kết nối (reachable point range) với sự kiện khi kết nối thành công
- ✅ Tùy chỉnh style đường (stroke, dash, width), mũi tên, và circle
- ✅ Tự động tính toán viewBox cho SVG
- ✅ Hỗ trợ separatedPoints cho đường nối phức tạp
- ✅ Debug mode hiển thị rect/circle trực quan
- ✅ Chạy ngoài NgZone để tối ưu hiệu năng
Khi nào sử dụng
- Khi cần vẽ đường nối giữa các node trong flowchart, diagram, workflow editor
- Khi cần hiển thị mối quan hệ giữa các phần tử trên giao diện
- Khi cần cho phép người dùng kéo thả để tạo kết nối giữa các phần tử
- Khi cần vẽ đường cong phức tạp né tránh các vật cản trên canvas
- Phù hợp cho automation builder, mind map, ERD diagram, pipeline editor
Cài đặt
# npm
npm install @libs-ui/components-draw-line
# yarn
yarn add @libs-ui/components-draw-lineImport
import {
LibsUiComponentsDrawLineDirective,
IMoDrawLineFunctionControl,
IDrawLineDataInput,
IReachablePointRange,
IViewBoxConfig,
TYPE_MODE,
} from '@libs-ui/components-draw-line';
@Component({
standalone: true,
imports: [LibsUiComponentsDrawLineDirective],
// ...
})
export class YourComponent {}Ví dụ
Basic — Vẽ đường nối đơn giản
<div
LibsUiComponentsDrawLineDirective
(outDrawLineFunctionControl)="onFunctionControl($event)">
</div>import { LibsUiComponentsDrawLineDirective, IMoDrawLineFunctionControl, IDrawLineDataInput } from '@libs-ui/components-draw-line';
@Component({
standalone: true,
imports: [LibsUiComponentsDrawLineDirective],
// ...
})
export class BasicDrawLineComponent {
private fnControl!: IMoDrawLineFunctionControl;
onFunctionControl(ctrl: IMoDrawLineFunctionControl): void {
this.fnControl = ctrl;
const lines: IDrawLineDataInput[] = [
{
id: 'line-1',
mode: 'quart-in',
points: {
start: { x: 100, y: 100 },
end: { x: 300, y: 200 },
},
},
];
this.fnControl.setData(lines);
}
}With Controls — Sử dụng FunctionsControl
export class ControlDrawLineComponent {
private fnControl!: IMoDrawLineFunctionControl;
onFunctionControl(ctrl: IMoDrawLineFunctionControl): void {
this.fnControl = ctrl;
}
addLine(): void {
this.fnControl.setData([
{
id: 'line-horizontal',
mode: 'horizontal',
points: {
start: { x: 50, y: 150 },
end: { x: 400, y: 300 },
},
lineStyle: {
stroke: '#3B82F6',
strokeWidth: '2px',
curve: 15,
},
arrowStyle: {
fill: '#3B82F6',
stroke: '#3B82F6',
width: 5,
height: 8,
},
},
]);
}
removeLine(): void {
this.fnControl.removeLine('line-horizontal');
}
updateViewBox(): void {
this.fnControl.updateViewBox({
minX: 0,
minY: 0,
width: 800,
height: 600,
});
}
}Obstacle Avoidance — Né vật cản
export class ObstacleDrawLineComponent {
private fnControl!: IMoDrawLineFunctionControl;
onFunctionControl(ctrl: IMoDrawLineFunctionControl): void {
this.fnControl = ctrl;
this.fnControl.setData([
{
id: 'line-obstacle',
mode: 'horizontal',
points: {
start: { x: 50, y: 100 },
end: { x: 500, y: 100 },
obstacleRect: [
{ id: 'box-1', x: 200, y: 50, width: 100, height: 100 },
],
},
lineStyle: { stroke: '#EF4444', strokeWidth: '2px' },
},
]);
}
}Reachable Point Range — Vùng có thể kết nối
<div
LibsUiComponentsDrawLineDirective
(outDrawLineFunctionControl)="onFunctionControl($event)"
(outConnected)="onConnected($event)">
</div>export class ReachableDrawLineComponent {
private fnControl!: IMoDrawLineFunctionControl;
onFunctionControl(ctrl: IMoDrawLineFunctionControl): void {
this.fnControl = ctrl;
this.fnControl.setReachablePointRange([
{
id: 'target-1',
x: 400,
y: 200,
style: { r: 6, fill: '#10B981', stroke: '#059669', strokeWidth: '2px' },
},
]);
this.fnControl.setData([
{
id: 'drag-line',
mode: 'quart-in',
endCircle: true,
endCircleStyle: { r: 4, fill: '#3B82F6' },
points: {
start: { x: 100, y: 200 },
end: { x: 250, y: 200 },
},
},
]);
}
onConnected(event: { dataLine: any; dataReachablePointRange: any }): void {
console.log('Kết nối thành công:', event);
}
}API
[LibsUiComponentsDrawLineDirective]
Inputs
| Property | Type | Default | Description |
| ----------------- | ----------------- | ------------ | ------------------------------------------------ |
| [drawRectDebug] | boolean | false | Hiển thị debug rect/circle cho obstacle trên SVG |
| [svgElement] | SVGSVGElement | auto-created | SVG element để vẽ, tự tạo nếu không truyền vào |
| [viewBoxConfig] | IViewBoxConfig | undefined | Cấu hình viewBox cho SVG element |
Outputs
| Property | Type | Description |
| ------------------------------ | ----------------------------------------------------------- | -------------------------------------------------------------- |
| (outConnected) | { dataLine: IDrawLineDataInput, dataReachablePointRange: IReachablePointRange } | Emit khi kéo mũi tên đến reachable point thành công |
| (outDrawLineFunctionControl) | IMoDrawLineFunctionControl | Emit object chứa các methods điều khiển draw-line |
FunctionsControl Methods
| Method | Signature | Description |
| -------------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------- |
| removeLine | (id: string, points?: Array<IPoints>) => void | Xóa đường nối theo id, có thể chỉ xóa một số points |
| removeReachablePointRange| (ids: Array<string>) => void | Xóa các reachable point range theo danh sách id |
| setData | (data: Array<IDrawLineDataInput>) => void | Thêm dữ liệu đường nối để vẽ |
| setReachablePointRange | (data: Array<IReachablePointRange>) => void | Thêm các vùng có thể kết nối (target points) |
| updateViewBox | (viewBoxConfig?: IViewBoxConfig) => void | Cập nhật viewBox cho SVG element |
Types & Interfaces
/**
* Chế độ vẽ đường
*/
export type TYPE_MODE =
| 'quart-in'
| 'vertical'
| 'vertical-single-curve'
| 'horizontal'
| 'horizontal-single-curve';
/**
* Hướng mũi tên
*/
export type TYPE_DIRECTION = 'right' | 'left' | 'top' | 'bottom';
/**
* Dữ liệu đầu vào cho mỗi đường nối
*/
export interface IDrawLineDataInput {
id: string;
points: IPoints;
mode: TYPE_MODE;
idConnected?: string;
lineStyle?: ILineStyle;
arrowStyle?: IArrowStyle;
arrowDirection?: TYPE_DIRECTION;
ignoreDrawArrow?: boolean;
startCircle?: boolean;
startCircleStyle?: ICircleStyle;
endCircle?: boolean;
endCircleStyle?: ICircleStyle;
isRemove?: boolean;
ref?: IDrawLineDataInput;
startEndMode?: 'right-left' | 'bottom-top' | 'bottom-left';
}
/**
* Tọa độ điểm đầu và điểm cuối
*/
export interface IPoints {
start: IPoint;
end: IPoint;
initialized?: boolean;
obstacleRect?: Array<IRect>;
separatedPoints?: Array<IPoints>;
pathElement?: SVGPathElement;
arrowElement?: SVGPolylineElement;
circleStartElement?: SVGCircleElement;
circleEndElement?: SVGCircleElement;
onDestroyEvent?: Subject<void>;
mode?: TYPE_MODE;
id?: string;
name?: string;
}
/**
* Tọa độ một điểm
*/
export interface IPoint {
x: number;
y: number;
}
/**
* Vùng có thể kết nối
*/
export interface IReachablePointRange {
id: string;
x: number;
y: number;
style: ICircleStyle;
idConnected?: string;
ref?: IReachablePointRange;
element?: SVGCircleElement;
onDestroyEvent?: Subject<void>;
}
/**
* Cấu hình viewBox cho SVG
*/
export interface IViewBoxConfig {
minX?: number;
minY?: number;
width?: number;
height?: number;
ignoreViewBox?: boolean;
marginBetweenElement?: number;
}
/**
* Style cho đường nối
*/
export interface ILineStyle {
stroke?: string;
strokeDasharray?: string;
strokeWidth?: string;
fill?: string;
curve?: number;
distancePoint?: number;
}
/**
* Style cho mũi tên
*/
export interface IArrowStyle {
stroke?: string;
strokeWidth?: string;
width?: number;
height?: number;
fill?: string;
}
/**
* Style cho circle (điểm tròn)
*/
export interface ICircleStyle {
stroke?: string;
strokeWidth?: string;
fill?: string;
r?: number;
}
/**
* Vùng chữ nhật (obstacle / debug rect)
*/
export interface IRect {
id?: string;
x: number;
y: number;
width: number;
height: number;
gapXObstacleRect?: number;
gapYObstacleRect?: number;
}
/**
* Object điều khiển draw-line
*/
export interface IMoDrawLineFunctionControl {
setData: (data: Array<IDrawLineDataInput>) => void;
removeLine: (id: string, points?: Array<IPoints>) => void;
setReachablePointRange: (data: Array<IReachablePointRange>) => void;
removeReachablePointRange: (ids: Array<string>) => void;
updateViewBox: (viewBoxConfig?: IViewBoxConfig) => void;
}Công nghệ
| Technology | Version | Purpose | | ---------- | ------- | --------------------- | | Angular | ≥18.0.0 | Framework | | @libs-ui/utils | 0.2.355-14 | Utilities (cloneDeep, getDragEventByElement, checkMouseOverInContainer) | | RxJS | ~7.8.0 | Reactive programming | | SVG | - | Rendering đường nối |
Demo
npx nx serve core-uiTruy cập: http://localhost:4500/draw-line
Kiểm thử
# Chạy tests
nx test draw-line
# Lint
nx lint draw-line
# Coverage
nx test draw-line --coverage
# Watch mode
nx test draw-line --watchLicense
MIT
