当前位置: 首页 > news >正文

LineConnector 组件(连接线)-技术博客

1. 组件概述

LineConnector 是一个基于 Vue 3 的线条连接组件,用于在两个点之间绘制各种类型的连接线,支持直线、折线和曲线,并提供丰富的自定义选项。该组件适用于流程图、拓扑图、连接线可视化等场景。

2. 组件特性

  • 📏 多种线条类型:支持直线(straight)、折线(polyline)、曲线(curve)
  • 🎨 高度可定制:颜色、宽度、虚线样式、箭头等
  • 📍 点标记:可显示/隐藏连接点,支持自定义标签
  • 🔄 灵活的折线类型:支持4种不同的折线连接方式
  • 📱 响应式设计:适应不同屏幕尺寸

3. 组件结构

核心文件

  • index.vue:LineConnector 组件的主文件
  • demo.vue:组件的演示和使用示例

技术栈

  • Vue 3
  • TypeScript
  • SVG
  • SCSS

4. 安装与导入

组件源码(来自 LineConnector.vue)

<template><div class="line-connector-container" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"><!-- Points --><div v-if="showPoints" v-for="(point, index) in [pointA, pointB]" :key="index" class="point":class="index === 0 ? 'point-a' : 'point-b'" :style="{top: `${point.y}px`,left: `${point.x}px`,background: strokeColor}">{{ index === 0 ? labelA : labelB }}</div><!-- SVG Line --><svg class="line-svg"style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none;"><!-- Arrow Marker --><marker v-if="showArrow" id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6"orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" :fill="strokeColor" /></marker><!-- Straight Line --><line v-if="lineType === 'straight'" :x1="pointA.x + pointSize / 2" :y1="pointA.y + pointSize / 2":x2="pointB.x + pointSize / 2" :y2="pointB.y + pointSize / 2" :stroke="strokeColor":stroke-width="strokeWidth" :stroke-dasharray="dashed ? '5,5' : 'none'":marker-end="showArrow ? 'url(#arrow)' : undefined" /><!-- Polyline --><path v-else-if="lineType === 'polyline'" :d="polylinePath" :stroke="strokeColor":stroke-width="strokeWidth" :stroke-dasharray="dashed ? '5,5' : 'none'" fill="none":marker-end="showArrow ? 'url(#arrow)' : undefined" /><!-- Curve --><path v-else-if="lineType === 'curve'" :d="curvePath" :stroke="strokeColor" :stroke-width="strokeWidth":stroke-dasharray="dashed ? '5,5' : 'none'" fill="none":marker-end="showArrow ? 'url(#arrow)' : undefined" /></svg></div>
</template><script>
export default {name: 'LineConnector',props: {// 点A的位置(x, y 单位为像素)--- 默认值为 { x: 0, y: 0 }pointA: {type: Object,default: () => ({ x: 0, y: 0 }),validator: (value) => typeof value.x === 'number' && typeof value.y === 'number'},// 点B的位置(x, y 单位为像素)--- 必须提供pointB: {type: Object,required: true,validator: (value) => typeof value.x === 'number' && typeof value.y === 'number'},// 线条类型:straight(直线), polyline(折线), curve(曲线)lineType: {type: String,default: 'straight',validator: (value) => ['straight', 'polyline', 'curve'].includes(value)},// 是否为虚线dashed: {type: Boolean,default: false},// 是否显示箭头showArrow: {type: Boolean,default: false},// 是否显示点showPoints: {type: Boolean,default: true},// 点A的标签labelA: {type: String,// default: 'A'default: ''},// 点B的标签labelB: {type: String,// default: 'B'default: ''},// 线条和点的颜色strokeColor: {type: String,default: '#fff'// default: '#f00'},// 线条宽度strokeWidth: {type: Number,default: 8},// 点的大小(宽和高,单位为像素)pointSize: {type: Number,default: 0},// 折线类型:1-横竖横, 2-竖横竖, 3-横竖, 4-竖横polylineType: {type: String,default: '1',validator: (value) => ['1', '2', '3', '4'].includes(value)}},computed: {// Path for polyline (right angle)polylinePath() {const { x: x1, y: y1 } = this.pointA;const { x: x2, y: y2 } = this.pointB;const midX = (x1 + x2) / 2;const midY = (y1 + y2) / 2;// Calculate center offset based on point sizeconst centerOffset = this.pointSize / 2;// Add center offset to all coordinatesconst aX = x1 + centerOffset;const aY = y1 + centerOffset;const bX = x2 + centerOffset;const bY = y2 + centerOffset;const mX = midX + centerOffset;const mY = midY + centerOffset;switch (this.polylineType) {case '1': // 横竖横 (H-V-H)return `M ${aX} ${aY} L ${mX} ${aY} L ${mX} ${bY} L ${bX} ${bY}`;case '2': // 竖横竖 (V-H-V)return `M ${aX} ${aY} L ${aX} ${mY} L ${bX} ${mY} L ${bX} ${bY}`;case '3': // 横竖 (H-V)return `M ${aX} ${aY} L ${bX} ${aY} L ${bX} ${bY}`;case '4': // 竖横 (V-H)return `M ${aX} ${aY} L ${aX} ${bY} L ${bX} ${bY}`;default:return `M ${aX} ${aY} L ${mX} ${aY} L ${mX} ${bY} L ${bX} ${bY}`;}},// Path for curve (quadratic Bezier)curvePath() {const { x: x1, y: y1 } = this.pointA;const { x: x2, y: y2 } = this.pointB;const midX = (x1 + x2) / 2;const midY = (y1 + y2) / 2;// Control point above the midpoint for an upward curveconst controlY = midY - 50;// Calculate center offset based on point sizeconst centerOffset = this.pointSize / 2;return `M ${x1 + centerOffset} ${y1 + centerOffset} Q ${midX + centerOffset} ${controlY + centerOffset} ${x2 + centerOffset} ${y2 + centerOffset}`;}}
};
</script><style scoped>
.point {position: absolute;width: v-bind('pointSize + "px"');height: v-bind('pointSize + "px"');line-height: v-bind('pointSize + "px"');text-align: center;color: white;border-radius: 50%;cursor: move;user-select: none;z-index: 10;
}.line-svg {z-index: 1;
}
</style>

局部导入

import { defineAsyncComponent } from 'vue'
const LineConnector = defineAsyncComponent(() => import('/@/components/Line/index.vue'))

全局注册

// main.ts
import { createApp } from 'vue'
import LineConnector from './components/Line/index.vue'const app = createApp(App)
app.component('LineConnector', LineConnector)
app.mount('#app')

5. 使用示例

基本用法

<template><div class="container"><LineConnector :pointA="{ x: 0, y: 50 }" :pointB="{ x: 100, y: 150 }" line-type="straight"/></div>
</template>

完整示例(来自 demo.vue)

<template><div class="granulator-screen"><div class="device-content-menu"><!-- 直线 --><LineConnector :pointA="{ x: 0, y: 50 }" :pointB="{ x: 100, y: 150 }" line-type="straight" :dashed="false"label-a="" label-b="" stroke-color="#fff" :stroke-width="2" /><!-- 折线类型1-横竖横 --><LineConnector :pointA="{ x: 0, y: 150 }" :pointB="{ x: 100, y: 200 }" line-type="polyline":polyline-type="'1'" stroke-color="#fff" :stroke-width="2" /><!-- 折线类型2-竖横竖 --><LineConnector :pointA="{ x: 0, y: 300 }" :pointB="{ x: 100, y: 400 }" line-type="polyline":polyline-type="'2'" stroke-color="#409eff" :stroke-width="2" /><!-- 折线类型3-横竖 --><LineConnector :pointA="{ x: 0, y: 450 }" :pointB="{ x: 100, y: 550 }" line-type="polyline":polyline-type="'3'" stroke-color="#67c23a" :stroke-width="2" /><!-- 折线类型4-竖横 --><LineConnector :pointA="{ x: 0, y: 600 }" :pointB="{ x: 100, y: 650 }" line-type="polyline":polyline-type="'4'" stroke-color="#e6a23c" :stroke-width="2" /></div></div>
</template>

6. API 参考

Props

参数名 类型 默认值 说明
pointA Object { x: 0, y: 0 } 起点坐标(像素单位)
pointB Object 必填 终点坐标(像素单位)
lineType String 'straight' 线条类型:straight(直线)、polyline(折线)、curve(曲线)
dashed Boolean false 是否为虚线
showArrow Boolean false 是否显示箭头
showPoints Boolean true 是否显示连接点
labelA String '' 起点标签
labelB String '' 终点标签
strokeColor String '#fff' 线条和点的颜色
strokeWidth Number 8 线条宽度
pointSize Number 0 点的大小(宽和高,像素单位)
polylineType String '1' 折线类型:1(横竖横)、2(竖横竖)、3(横竖)、4(竖横)

计算属性

polylinePath

根据折线类型和点坐标生成 SVG path 字符串:

  • 类型1 (横竖横): M aX aY L mX aY L mX bY L bX bY
  • 类型2 (竖横竖): M aX aY L aX mY L bX mY L bX bY
  • 类型3 (横竖): M aX aY L bX aY L bX bY
  • 类型4 (竖横): M aX aY L aX bY L bX bY

curvePath

生成曲线(二次贝塞尔曲线)的 SVG path 字符串,控制点位于中点上方 50px 处。

7. 组件实现细节

核心渲染逻辑

<!-- SVG Line -->
<svg class="line-svg" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none;"><!-- Arrow Marker --><marker v-if="showArrow" id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" :fill="strokeColor" /></marker><!-- Straight Line --><line v-if="lineType === 'straight'" :x1="pointA.x + pointSize / 2" :y1="pointA.y + pointSize / 2":x2="pointB.x + pointSize / 2" :y2="pointB.y + pointSize / 2" :stroke="strokeColor":stroke-width="strokeWidth" :stroke-dasharray="dashed ? '5,5' : 'none'":marker-end="showArrow ? 'url(#arrow)' : undefined" /><!-- Polyline --><path v-else-if="lineType === 'polyline'" :d="polylinePath" :stroke="strokeColor":stroke-width="strokeWidth" :stroke-dasharray="dashed ? '5,5' : 'none'" fill="none":marker-end="showArrow ? 'url(#arrow)' : undefined" /><!-- Curve --><path v-else-if="lineType === 'curve'" :d="curvePath" :stroke="strokeColor" :stroke-width="strokeWidth":stroke-dasharray="dashed ? '5,5' : 'none'" fill="none":marker-end="showArrow ? 'url(#arrow)' : undefined" />
</svg>

点标记实现

<!-- Points -->
<div v-if="showPoints" v-for="(point, index) in [pointA, pointB]" :key="index" class="point":class="index === 0 ? 'point-a' : 'point-b'" :style="{top: `${point.y}px`,left: `${point.x}px`,background: strokeColor}">{{ index === 0 ? labelA : labelB }}
</div>

8. 样式设计

组件使用 CSS 变量和动态绑定实现样式的灵活控制:

.point {position: absolute;width: v-bind('pointSize + "px"');height: v-bind('pointSize + "px"');line-height: v-bind('pointSize + "px"');text-align: center;color: white;border-radius: 50%;cursor: move;user-select: none;z-index: 10;
}.line-svg {z-index: 1;
}

9. 扩展与定制

添加新的折线类型

要添加新的折线类型,只需在 polylinePath 计算属性中添加新的 case:

switch (this.polylineType) {case '1': // 横竖横 (H-V-H)return `M ${aX} ${aY} L ${mX} ${aY} L ${mX} ${bY} L ${bX} ${bY}`;case '2': // 竖横竖 (V-H-V)return `M ${aX} ${aY} L ${aX} ${mY} L ${bX} ${mY} L ${bX} ${bY}`;// 添加新类型case '5': // 自定义类型return `M ${aX} ${aY} L ${aX + 50} ${aY} L ${bX - 50} ${bY} L ${bX} ${bY}`;default:return `M ${aX} ${aY} L ${mX} ${aY} L ${mX} ${bY} L ${bX} ${bY}`;
}

自定义曲线控制点

可以扩展 props,允许用户自定义曲线的控制点:

// 添加新的 props
controlPoint: {type: Object,default: null
}// 在 curvePath 中使用
curvePath() {const { x: x1, y: y1 } = this.pointA;const { x: x2, y: y2 } = this.pointB;const midX = (x1 + x2) / 2;const midY = (y1 + y2) / 2;// 使用自定义控制点或默认值const controlX = this.controlPoint?.x || midX;const controlY = this.controlPoint?.y || (midY - 50);const centerOffset = this.pointSize / 2;return `M ${x1 + centerOffset} ${y1 + centerOffset} Q ${controlX + centerOffset} ${controlY + centerOffset} ${x2 + centerOffset} ${y2 + centerOffset}`;
}

10. 性能优化

  • 使用 SVG:SVG 矢量图形确保在任何缩放级别下都保持清晰,且性能优于 Canvas 绘制大量线条的场景
  • 条件渲染:仅渲染需要的元素(如箭头、点标记)
  • 事件优化:设置 pointer-events: none 避免 SVG 干扰页面交互
  • 异步导入:支持动态加载,减少初始加载时间

11. 浏览器兼容性

LineConnector 组件基于 Vue 3 和 SVG,支持所有现代浏览器:

  • Chrome 88+
  • Firefox 85+
  • Safari 14+
  • Edge 88+

12. 总结

LineConnector 是一个功能强大、易于使用的 Vue 3 连接线组件,提供了丰富的自定义选项和灵活的配置方式。无论是构建简单的流程图还是复杂的拓扑图,该组件都能满足需求。通过合理的 API 设计和性能优化,确保了组件的易用性和高效性。

http://www.jsqmd.com/news/452970/

相关文章:

  • 2026年工厂智能数字孪生选哪家?盘点性价比TOP3公司,省钱又高效!
  • 2026年信誉好的模具展览会分析,专业模具展价格多少 - 工业品牌热点
  • 分析2026年四川热门财税品牌,华光讯财税学院课程值得选购吗 - 工业推荐榜
  • 奥林巴斯清洁度检测设备推荐:苏州西恩士工业,技术团队加持,售后无忧 - 工业干货社
  • 支持国产操作系统的FTP:助力企业数据安全传输与自主创新 - 飞驰云联
  • 观澜社张庆携社员参与环保公益慈善 守护绿水青山美家园 - 博客湾
  • 2026年 上海专线物流公司推荐榜:工程设备/大件运输专业实力与高效服务深度解析 - 品牌企业推荐师(官方)
  • 隐藏框
  • 2026七大CRM系统横评:从客户分层到报表全链路对决 - 毛毛鱼的夏天
  • 探讨烧腊批发配送联系方式,广州嘉记烧腊选购时有啥注意点? - mypinpai
  • 2026年惠州财税服务权威推荐:创业范财务代理,工商注册/代理记账/税务筹划/财税合规/企业注销全链条一站式服务商精选 - 品牌推荐官
  • 说说常州网红值得打卡地,苏州纸飞机艺术岛价格贵不贵? - 工业设备
  • 打开网站显示526 Invalid SSL certificate SSL证书无效错误怎么办|已解决
  • 蒂升电梯定制化服务靠谱吗,2026上海蒂升电梯选购指南 - 工业推荐榜
  • 常见问题解答:永辉超市卡回收线上平台是否值得信赖? - 团团收购物卡回收
  • 2026年高架库公司怎么挑?看这5个维度选对合作伙伴,高架库/全自动仓库/立体仓储/立体仓库,高架库订制厂家口碑推荐榜单 - 品牌推荐师
  • 天猫超市购物卡变现,线上回收成最优解 - 京顺回收
  • 盘点2026年上海高性价比电梯,蒂升电梯运行效率高值得选吗 - myqiye
  • Focal Loss 是什么?有什么用?
  • 2026年北京地区服务不错的麦颂智能运营企业推荐,专业之选 - 工业设备
  • 合作动态 | 广州玖花璞华易研PLM项目正式启动,领航跨境电商美妆个护行业研发数智化
  • 回收大润发购物卡的快速方法揭秘:线上平台让你更省心 - 团团收购物卡回收
  • 打开网站显示530 Unable to resolve the origin hostname 无法解析源站的主机名错误怎么办|已解决
  • 分析RDF压块机厂家,靠谱的有哪些,费用怎么算? - 工业品网
  • 分析2026年泉州发电机租赁口碑好的品牌,怎么选择不踩坑 - 工业品牌热点
  • 2026冷镦非标件实地厂家,谁在引领行业?,冷镦非标件/深孔钻加工/棒料机打孔/数控车床加工,冷镦非标件供应链选哪家 - 品牌推荐师
  • 聊聊湖南旧房翻新家装公司十强,金空间装饰性价比高靠谱吗 - mypinpai
  • 急需资金怎么办?选择银行短期小额贷款 - 资讯焦点
  • 2026年2月值得关注!口碑好的磁铁机厂家推荐排行,对折浴帘机/全自动雨衣机/开衫雨衣机/雨衣机,磁铁机产品哪家好 - 品牌推荐师
  • 细聊哈尔滨小威贴膜,在行业内地位、评价及能否满足不同需求揭秘 - 工业品牌热点