统一连线样式并优化连接点位置
- 修改sub函数采用与main函数相同的二次贝塞尔曲线样式 - 统一所有连线的视觉风格,消除曲线和横线的混合 - 优化连接点位置,向内偏移31像素确保连线连接到节点边框内部 - 消除连线与节点之间的视觉间隔,达到完美贴合效果
This commit is contained in:
parent
d69def44ca
commit
4bb72ba6a4
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -23,8 +23,8 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script type="module" crossorigin src="/assets/index-a814442b.js"></script>
|
<script type="module" crossorigin src="/assets/index-a5a78393.js"></script>
|
||||||
<link rel="stylesheet" href="/assets/index-1f5435d2.css">
|
<link rel="stylesheet" href="/assets/index-2e253ee6.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -172,7 +173,7 @@ import { ref, onMounted, onUnmounted, nextTick } from 'vue';
|
||||||
import MindElixir from '../lib/mind-elixir/dist/MindElixir.js';
|
import MindElixir from '../lib/mind-elixir/dist/MindElixir.js';
|
||||||
import '../lib/mind-elixir/dist/style.css';
|
import '../lib/mind-elixir/dist/style.css';
|
||||||
|
|
||||||
// 自定义淡紫色主题
|
// 自定义主题
|
||||||
const customTheme = {
|
const customTheme = {
|
||||||
name: 'Light Purple',
|
name: 'Light Purple',
|
||||||
type: 'light',
|
type: 'light',
|
||||||
|
|
@ -209,6 +210,13 @@ const customTheme = {
|
||||||
'--panel-bgcolor': '#ffffff',
|
'--panel-bgcolor': '#ffffff',
|
||||||
'--panel-border-color': '#eaeaea',
|
'--panel-border-color': '#eaeaea',
|
||||||
'--map-padding': '50px',
|
'--map-padding': '50px',
|
||||||
|
// 节点边框相关配置 - 默认启用封闭边框
|
||||||
|
'--enable-node-border': 'true',
|
||||||
|
'--node-border-padding': '8',
|
||||||
|
'--node-border-radius': '8',
|
||||||
|
'--node-border-color': '#660874',
|
||||||
|
'--node-border-fill': '#ffffff',
|
||||||
|
'--node-border-width': '1',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
import { mindmapAPI } from '../api/mindmap.js';
|
import { mindmapAPI } from '../api/mindmap.js';
|
||||||
|
|
@ -250,6 +258,7 @@ const imagePreviewError = ref('');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 显示欢迎页面
|
// 显示欢迎页面
|
||||||
const showWelcomePage = () => {
|
const showWelcomePage = () => {
|
||||||
showWelcome.value = true;
|
showWelcome.value = true;
|
||||||
|
|
@ -351,6 +360,7 @@ const retryLoadImage = () => {
|
||||||
startImageLoadTimeout();
|
startImageLoadTimeout();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// 图片加载超时处理
|
// 图片加载超时处理
|
||||||
let imageLoadTimeout = null;
|
let imageLoadTimeout = null;
|
||||||
const startImageLoadTimeout = () => {
|
const startImageLoadTimeout = () => {
|
||||||
|
|
@ -543,6 +553,7 @@ const loadMindmapData = async (data, keepPosition = false, shouldCenterRoot = tr
|
||||||
maxScale: 5,
|
maxScale: 5,
|
||||||
minScale: 0.1,
|
minScale: 0.1,
|
||||||
theme: customTheme, // 应用自定义主题
|
theme: customTheme, // 应用自定义主题
|
||||||
|
enableNodeBorder: true, // 启用节点边框功能
|
||||||
markdown: (text, nodeObj) => {
|
markdown: (text, nodeObj) => {
|
||||||
// 检查内容是否包含markdown语法(包括图片)
|
// 检查内容是否包含markdown语法(包括图片)
|
||||||
if (text.includes('|') || text.includes('**') || text.includes('`') || text.includes('#') || text.includes('![')) {
|
if (text.includes('|') || text.includes('**') || text.includes('`') || text.includes('#') || text.includes('![')) {
|
||||||
|
|
@ -645,6 +656,7 @@ const loadMindmapData = async (data, keepPosition = false, shouldCenterRoot = tr
|
||||||
maxScale: 5,
|
maxScale: 5,
|
||||||
minScale: 0.1,
|
minScale: 0.1,
|
||||||
theme: customTheme, // 应用自定义主题
|
theme: customTheme, // 应用自定义主题
|
||||||
|
enableNodeBorder: true, // 启用节点边框功能
|
||||||
markdown: (text, nodeObj) => {
|
markdown: (text, nodeObj) => {
|
||||||
// 检查内容是否包含markdown语法(包括图片)
|
// 检查内容是否包含markdown语法(包括图片)
|
||||||
if (text.includes('|') || text.includes('**') || text.includes('`') || text.includes('#') || text.includes('![')) {
|
if (text.includes('|') || text.includes('**') || text.includes('`') || text.includes('#') || text.includes('![')) {
|
||||||
|
|
@ -748,6 +760,7 @@ const loadMindmapData = async (data, keepPosition = false, shouldCenterRoot = tr
|
||||||
maxScale: 5,
|
maxScale: 5,
|
||||||
minScale: 0.1,
|
minScale: 0.1,
|
||||||
theme: customTheme, // 应用自定义主题
|
theme: customTheme, // 应用自定义主题
|
||||||
|
enableNodeBorder: true, // 启用节点边框功能
|
||||||
markdown: (text, nodeObj) => {
|
markdown: (text, nodeObj) => {
|
||||||
// 检查内容是否包含markdown语法(包括图片)
|
// 检查内容是否包含markdown语法(包括图片)
|
||||||
if (text.includes('|') || text.includes('**') || text.includes('`') || text.includes('#') || text.includes('![')) {
|
if (text.includes('|') || text.includes('**') || text.includes('`') || text.includes('#') || text.includes('![')) {
|
||||||
|
|
@ -4867,5 +4880,6 @@ const updateMindMapRealtime = async (data, title) => {
|
||||||
.retry-button:hover {
|
.retry-button:hover {
|
||||||
background: #7d0a8e;
|
background: #7d0a8e;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -77,11 +77,11 @@
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
& > me-tpc {
|
& > me-tpc {
|
||||||
border-radius: var(--main-radius);
|
border-radius: var(--main-radius) !important;
|
||||||
background-color: var(--main-bgcolor);
|
background-color: var(--main-bgcolor) !important;
|
||||||
border: 2px solid var(--main-color);
|
border: 2px solid var(--main-color) !important;
|
||||||
color: var(--main-color);
|
color: var(--main-color) !important;
|
||||||
padding: 8px 25px;
|
padding: 8px 25px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -118,9 +118,12 @@
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
me-tpc {
|
me-tpc {
|
||||||
position: relative;
|
position: relative;
|
||||||
border-radius: 3px;
|
border-radius: var(--main-radius, 20px); // 使用椭圆圆角
|
||||||
color: var(--color);
|
color: var(--color);
|
||||||
padding: var(--topic-padding);
|
padding: var(--topic-padding);
|
||||||
|
// 所有节点都使用紫色椭圆边框
|
||||||
|
background-color: var(--node-border-fill, #ffffff);
|
||||||
|
border: 2px solid var(--node-border-color, #660874);
|
||||||
|
|
||||||
// drag preview
|
// drag preview
|
||||||
.insert-preview {
|
.insert-preview {
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ function MindElixir(
|
||||||
handleWheel,
|
handleWheel,
|
||||||
markdown,
|
markdown,
|
||||||
imageProxy,
|
imageProxy,
|
||||||
|
enableNodeBorder,
|
||||||
}: Options
|
}: Options
|
||||||
): void {
|
): void {
|
||||||
let ele: HTMLElement | null = null
|
let ele: HTMLElement | null = null
|
||||||
|
|
@ -93,6 +94,7 @@ function MindElixir(
|
||||||
this.handleWheel = handleWheel ?? true
|
this.handleWheel = handleWheel ?? true
|
||||||
this.markdown = markdown || undefined // Custom markdown parser function
|
this.markdown = markdown || undefined // Custom markdown parser function
|
||||||
this.imageProxy = imageProxy || undefined // Image proxy function
|
this.imageProxy = imageProxy || undefined // Image proxy function
|
||||||
|
this.enableNodeBorder = enableNodeBorder ?? false // Node border feature
|
||||||
// this.parentMap = {} // deal with large amount of nodes
|
// this.parentMap = {} // deal with large amount of nodes
|
||||||
this.currentNodes = [] // selected <tpc/> elements
|
this.currentNodes = [] // selected <tpc/> elements
|
||||||
this.currentArrow = null // the selected link svg element
|
this.currentArrow = null // the selected link svg element
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,11 @@ export interface Topic extends HTMLElement {
|
||||||
image?: HTMLImageElement
|
image?: HTMLImageElement
|
||||||
icons?: HTMLSpanElement
|
icons?: HTMLSpanElement
|
||||||
tags?: HTMLDivElement
|
tags?: HTMLDivElement
|
||||||
|
|
||||||
|
// 节点边框相关属性
|
||||||
|
borderRect?: SVGRectElement
|
||||||
|
borderObserver?: MutationObserver
|
||||||
|
borderResizeHandler?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Expander extends HTMLElement {
|
export interface Expander extends HTMLElement {
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,13 @@ export type Theme = {
|
||||||
'--panel-bgcolor': string
|
'--panel-bgcolor': string
|
||||||
'--panel-border-color': string
|
'--panel-border-color': string
|
||||||
'--map-padding': string
|
'--map-padding': string
|
||||||
|
// 节点边框相关配置
|
||||||
|
'--enable-node-border'?: string
|
||||||
|
'--node-border-padding'?: string
|
||||||
|
'--node-border-radius'?: string
|
||||||
|
'--node-border-color'?: string
|
||||||
|
'--node-border-fill'?: string
|
||||||
|
'--node-border-width'?: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -162,6 +169,11 @@ export interface Options {
|
||||||
* @default undefined
|
* @default undefined
|
||||||
*/
|
*/
|
||||||
imageProxy?: (url: string) => string
|
imageProxy?: (url: string) => string
|
||||||
|
/**
|
||||||
|
* Enable node border feature
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
enableNodeBorder?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Uid = string
|
export type Uid = string
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import { LEFT } from '../const'
|
import { LEFT } from '../const'
|
||||||
import type { Topic, Wrapper, Parent, Children, Expander } from '../types/dom'
|
import type { Topic, Wrapper, Parent, Children, Expander } from '../types/dom'
|
||||||
import type { MindElixirInstance, NodeObj } from '../types/index'
|
import type { MindElixirInstance, NodeObj } from '../types/index'
|
||||||
import { encodeHTML } from '../utils/index'
|
import { encodeHTML, getOffsetLT } from '../utils/index'
|
||||||
import { layoutChildren } from './layout'
|
import { layoutChildren } from './layout'
|
||||||
// 移除imageProcessor引用,使用MindElixir原生image属性
|
// 移除imageProcessor引用,使用MindElixir原生image属性
|
||||||
|
|
||||||
|
|
||||||
// DOM manipulation
|
// DOM manipulation
|
||||||
const $d = document
|
const $d = document
|
||||||
export const findEle = function (this: MindElixirInstance, id: string, el?: HTMLElement) {
|
export const findEle = function (this: MindElixirInstance, id: string, el?: HTMLElement) {
|
||||||
|
|
@ -175,9 +176,11 @@ export const createParent = function (this: MindElixirInstance, nodeObj: NodeObj
|
||||||
const tpc = this.createTopic(nodeObj)
|
const tpc = this.createTopic(nodeObj)
|
||||||
shapeTpc.call(this, tpc, nodeObj)
|
shapeTpc.call(this, tpc, nodeObj)
|
||||||
p.appendChild(tpc)
|
p.appendChild(tpc)
|
||||||
|
|
||||||
return { p, tpc }
|
return { p, tpc }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const createChildren = function (this: MindElixirInstance, wrappers: Wrapper[]) {
|
export const createChildren = function (this: MindElixirInstance, wrappers: Wrapper[]) {
|
||||||
const children = $d.createElement('me-children') as Children
|
const children = $d.createElement('me-children') as Children
|
||||||
children.append(...wrappers)
|
children.append(...wrappers)
|
||||||
|
|
|
||||||
|
|
@ -50,31 +50,27 @@ export function main({ pT, pL, pW, pH, cT, cL, cW, cH, direction, containerHeigh
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sub(this: MindElixirInstance, { pT, pL, pW, pH, cT, cL, cW, cH, direction, isFirst }: SubLineParams) {
|
export function sub(this: MindElixirInstance, { pT, pL, pW, pH, cT, cL, cW, cH, direction, isFirst }: SubLineParams) {
|
||||||
const GAP = parseInt(this.container.style.getPropertyValue('--node-gap-x')) // cache?
|
// 完全复制main函数的逻辑
|
||||||
// const GAP = 30
|
let x1 = pL + pW / 2
|
||||||
let y1 = 0
|
const y1 = pT + pH / 2
|
||||||
let end = 0
|
let x2
|
||||||
if (isFirst) {
|
|
||||||
y1 = pT + pH / 2
|
|
||||||
} else {
|
|
||||||
y1 = pT + pH
|
|
||||||
}
|
|
||||||
const y2 = cT + cH
|
|
||||||
let x1 = 0
|
|
||||||
let x2 = 0
|
|
||||||
let xMid = 0
|
|
||||||
const offset = (Math.abs(y1 - y2) / 300) * GAP
|
|
||||||
if (direction === DirectionClass.LHS) {
|
if (direction === DirectionClass.LHS) {
|
||||||
xMid = pL
|
x2 = cL + cW - 31 // 向内31像素,确保连接到边框内部
|
||||||
x1 = xMid + GAP
|
|
||||||
x2 = xMid - GAP
|
|
||||||
end = cL + GAP
|
|
||||||
return `M ${x1} ${y1} C ${xMid} ${y1} ${xMid + offset} ${y2} ${x2} ${y2} H ${end}`
|
|
||||||
} else {
|
} else {
|
||||||
xMid = pL + pW
|
x2 = cL + 31 // 向内31像素,确保连接到边框内部
|
||||||
x1 = xMid - GAP
|
|
||||||
x2 = xMid + GAP
|
|
||||||
end = cL + cW - GAP
|
|
||||||
return `M ${x1} ${y1} C ${xMid} ${y1} ${xMid - offset} ${y2} ${x2} ${y2} H ${end}`
|
|
||||||
}
|
}
|
||||||
|
const y2 = cT + cH / 2
|
||||||
|
|
||||||
|
// 使用与main函数相同的偏移计算方式
|
||||||
|
const containerHeight = this.container.offsetHeight || 1000 // 提供一个默认值
|
||||||
|
const pct = Math.abs(y2 - y1) / containerHeight
|
||||||
|
const offset = (1 - pct) * 0.25 * (pW / 2)
|
||||||
|
|
||||||
|
if (direction === DirectionClass.LHS) {
|
||||||
|
x1 = x1 - pW / 10 - offset
|
||||||
|
} else {
|
||||||
|
x1 = x1 + pW / 10 + offset
|
||||||
|
}
|
||||||
|
|
||||||
|
return `M ${x1} ${y1} Q ${x1} ${y2} ${x2} ${y2}`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue