基于 vue3.x 的流程图绘制( 四 )

曲线的中心点位置,可以直接拿三阶贝塞尔曲线公式求出
// 获取三阶贝塞尔曲线的中点坐标const getBezierCenterPoint = (points: [number, number][]) => {return getThreeBezierPoint(0.5, points[0], points[1], points[2], points[3])}/** * @desc 获取三阶贝塞尔曲线的线上坐标 * @param {number} t 当前百分比 * @param {Array} p1 起点坐标 * @param {Array} p2 终点坐标 * @param {Array} cp1 控制点1 * @param {Array} cp2 控制点2 */export const getThreeBezierPoint = (t: number,p1: [number, number],cp1: [number, number],cp2: [number, number],p2: [number, number]): [number, number] => {const [x1, y1] = p1const [x2, y2] = p2const [cx1, cy1] = cp1const [cx2, cy2] = cp2const x =x1 * (1 - t) * (1 - t) * (1 - t) +3 * cx1 * t * (1 - t) * (1 - t) +3 * cx2 * t * t * (1 - t) +x2 * t * t * tconst y =y1 * (1 - t) * (1 - t) * (1 - t) +3 * cy1 * t * (1 - t) * (1 - t) +3 * cy2 * t * t * (1 - t) +y2 * t * t * treturn [x | 0, y | 0]}复制代码在算出每一条的中心点位置后,在目标位置添加备注信息即可:

基于 vue3.x 的流程图绘制

文章插图
 
节点的样式调整【基于 vue3.x 的流程图绘制】节点的样式调整主要是位置及大小,而这些属性就是节点里面的 attr,在相应的事件下根据鼠标移动的方向及位置,来调整节点的样式 。
基于 vue3.x 的流程图绘制

文章插图
 
还有批量操作也是同样,不过批量操作是要先计算出哪些节点的范围 。
// 获取范围选中内的组件export const getSelectedComponent = (componentList: WF.ComponentType[], areaPosi: WF.Attr) => {let selectedArea: WF.Attr | null = nulllet minx = Infinity, miny = Infinity, maxx = -Infinity, maxy = -Infinityconst selectedComponents = componentList.filter((component: WF.ComponentType) => {const res = areaPosi.x <= component.attr.x &&areaPosi.y <= component.attr.y &&areaPosi.x + areaPosi.w >= component.attr.x + component.attr.w &&areaPosi.y + areaPosi.h >= component.attr.y + component.attr.hif (res) {minx = Math.min(minx, component.attr.x)miny = Math.min(miny, component.attr.y)maxx = Math.max(maxx, component.attr.x + component.attr.w)maxy = Math.max(maxy, component.attr.y + component.attr.h)}return res})if (selectedComponents.length) {selectedArea = {x: minx,y: miny,w: maxx - minx,h: maxy - miny}return {selectedArea, selectedComponents}}return null}复制代码
基于 vue3.x 的流程图绘制

文章插图
 
这个有个小功能没有做,就是在批量调整大小的时候,节点间的相对距离应该是不动的,这里忽略了 。
节点移动时的吸附这里的吸附功能其实是做了一个简单版的,就是 x 和 y 轴都只有一条校准线,且校准的优先级是从左至右,从上至下 。
基于 vue3.x 的流程图绘制

文章插图
 
这里吸附的标准是节点的 6 个点:X 轴的左中右,Y 轴的上中下,当前节点在移动的时候,会用当前节点的 6 个点,一一去与其它节点的 6 个点做比较,在误差正负 2px 的情况,自动更新为0,即自定对齐 。
因为移动当前节点时候,其它的节点是不动的,所以这里是做了一步预处理,即在鼠标按下去的时候,把其它的节点的 6 个点都线算出来,用 Set 结构保存,在移动的过程的比较中,计算量会相对较少 。
// 计算其它节点的所有点位置export const clearupPostions = (componentList: WF.ComponentType[], currId: string) => {// x 坐标集合const coordx = new Set<number>()// y 坐标集合const coordy = new Set<number>()componentList.forEach((component: WF.ComponentType) => {if (component.id === currId) {return}const { x, y, w, h } = component.attrcoordx.add(x)coordx.add(x + (w >> 1))coordx.add(x + w)coordy.add(y)coordy.add(y + (h >> 1))coordy.add(y + h)})return [coordx, coordy]}复制代码判读是否有可吸附的点
// 可吸附范围const ADSORBRANGE = 2// 查询是否有可吸附坐标const hasAdsorbable = (coords: Set<number>[], x: number, y: number, w: number, h: number) => {// x, y, w, h, w/2, h/2const coord: (number | null)[] = [null, null, null, null, null, null]// 查询 x 坐标for (let i = 0; i <= ADSORBRANGE; i++) {if (coords[0].has(x + i)) {coord[0] = ibreak}if (coords[0].has(x - i)) {coord[0] = -ibreak}}// 查询 y 坐标for (let i = 0; i <= ADSORBRANGE; i++) {if (coords[1].has(y + i)) {coord[1] = ibreak}if (coords[1].has(y - i)) {coord[1] = -ibreak}}// 查询 x + w 坐标for (let i = 0; i <= ADSORBRANGE; i++) {if (coords[0].has(x + w + i)) {coord[2] = ibreak}if (coords[0].has(x + w - i)) {coord[2] = -ibreak}}// 查询 y + h 坐标for (let i = 0; i <= ADSORBRANGE; i++) {if (coords[1].has(y + h + i)) {coord[3] = ibreak}if (coords[1].has(y + h - i)) {coord[3] = -ibreak}}// 查询 x + w/2 坐标for (let i = 0; i <= ADSORBRANGE; i++) {if (coords[0].has(x + (w >> 1) + i)) {coord[4] = ibreak}if (coords[0].has(x + (w >> 1) - i)) {coord[4] = -ibreak}}// 查询 y + h/2 坐标for (let i = 0; i <= ADSORBRANGE; i++) {if (coords[1].has(y + (h >> 1) + i)) {coord[5] = ibreak}if (coords[1].has(y + (h >> 1) - i)) {coord[5] = -ibreak}}return coord}复制代码


推荐阅读