Foreword
在最近做的项目中遇到了画 UML 类图,其中想画一根比较好看的曲线,而不是笔直笔直的直线,于是查了很多,得知关于贝塞尔曲线,在这里做一下记录。
如果你想要深入了解,你可以看一下张鑫旭的 深度掌握SVG路径path的贝塞尔曲线指令
下面先上一效果图:
看一下如何实现
- 看一下如何画出二次贝塞尔曲线
<path :d="`M${x0},${y0} Q${(x1},${y1} ${x2},${y2} T${x4},${y4}`"
stroke="#4CAF50" fill="none" style="stroke-width: 1px;" class="arrow_line a_line"></path>
- 看一下代码重点看一下坐标 x0,y0 x1,y1 x2,y2 x4,y4 咱们只需要 四个坐标点 就可以画出比较漂亮的二次贝塞尔曲线,这里结合了 M Q T 三个指令(二次贝塞尔曲线指令)下表是所有的指令
命令 | 名称 | 参数 |
---|---|---|
M | moveto | (x y)+ |
Z | closepath 关闭路径 | (none) |
L | lineto 画线到 | (x y)+ |
H | horizontal lineto 水平线到 | x+ |
V | vertical lineto 垂直线到 | y+ |
C | curveto 三次贝塞尔曲线到 | (x1 y1 x2 y2 x y)+ |
S | smooth curveto 光滑三次贝塞尔曲线到 | (x2 y2 x y)+ |
Q | quadratic Bézier curveto 二次贝塞尔曲线到 | (x1 y1 x y)+ |
T | smooth quadratic Bézier curveto 光滑二次贝塞尔曲线到 | (x y)+ |
A | elliptical arc 椭圆弧 | (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+ |
R | Catmull-Rom curveto* Catmull-Rom曲线 | x1 y1 (x y)+ |
- 如果你只想画出一个漂亮的曲线的话,上表中的内容不重要,在这里需要首先知道自己想要怎么样的曲线,如下图,这是我想要的:
- 那么我要画出这条曲线只需要知道四个坐标,即起点、总长度的1/4处、总长度的1/2处、终点即可
看一下代码吧
- 如果你刚好使用 vue 那你大可以直接复制下面的箭头组件到你的项目
- 但是如果你转发了本篇文章或者下面的代码,请注明出处,都不容易~~ 谢谢了
<template>
<g class="arrow">
<defs>
<marker id='tp_arrow' refX='0' refY='0' markerWidth='12' markerHeight='12' orient='auto' viewBox='0, -4, 12, 12' >
<path d='M0 -4 L0 4 L10 0' style='fill:#4CAF50' stroke-width='0'></path>
</marker>
</defs>
<!-- 直线 ---- 扩大选择区域 -->
<g v-if="lineType=='line'">
<polyline :points='`${fromX},${fromY} ${toX},${toY}`' style='stroke:transparent;stroke-width:10;'></polyline>
<polyline class="arrow_line" type='step' :points='`${fromX},${fromY} ${toX},${toY}`' style='stroke:#4CAF50;stroke-width:1;' marker-end="url(#tp_arrow)" ></polyline>
</g>
<!-- 如果想使曲线更加弯曲 可以 Q的第一个参数可以设置为 5*fromX+3*toX)/8 -->
<!-- 贝塞尔曲线 ---- 扩大选择区域 -->
<g v-if="lineType=='bezier'">
<path :d="`M${fromX},${fromY} Q${(3*fromX+toX)/4},${fromY} ${(toX+fromX)/2},${(toY+fromY)/2} T${toX},${toY}`" stroke="transparent" fill="none"
style="stroke-width: 10px;" class="a_line"></path>
<path :d="`M${fromX},${fromY} Q${(3*fromX+toX)/4},${fromY} ${(toX+fromX)/2},${(toY+fromY)/2} T${toX},${toY}`" marker-end="url(#tp_arrow)"
stroke="#4CAF50" fill="none" style="stroke-width: 1px;" class="arrow_line a_line"></path>
</g>
<!-- 删除的叉号❌ -->
<g class="arrow_cross" v-if="!moveMode" :transform="`translate(${(toX-fromX)/2 + fromX-6},${(toY-fromY)/2 + fromY-6})`" @click="delArrow">
<rect width="12" height="12" style="fill: transparent"></rect>
<polyline style="stroke:rgba(214, 0, -1, 1);stroke-width:2" points="0 0 12 12"></polyline>
<polyline style="stroke:rgba(214, 0, -1, 1);stroke-width:2" points="12 0 0 12"></polyline>
</g>
</g>
</template>
<script>
/**
* 箭头组件
* powered by pancras
* 2019年11月5日
**/
export default {
components: {},
props:{
fromX:{
type:Number,
default(){ return 0 }
},
fromY:{
type:Number,
default(){ return 0 }
},
toX:{
type:Number,
default(){ return 0 }
},
toY:{
type:Number,
default(){ return 0 }
},
/**
* 箭头的格式 直线/贝塞尔曲线
* bezier / line
* **/
lineType:{
type:String,
default(){ return 'bezier' }
},
// 移动模式下隐藏 叉号
moveMode:{
type:Boolean,
default(){ return false }
},
},
data() {
return {
showCross:false,
}
},
async mounted() {
},
methods: {
// 删除回调
delArrow(){
// console.log('umlArrow.vue:触发了删除箭头事件')
this.$emit('delArrow');
}
}
}
</script>
<style scoped>
.arrow:hover .arrow_cross{display: block;}
.arrow_cross{display: none;cursor: pointer;transition: .2s ease all;}
.a_line{transition: .2s all ease;}
</style>