统一连线样式并优化连接点位置
- 修改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;
|
||||
}
|
||||
</style>
|
||||
<script type="module" crossorigin src="/assets/index-a814442b.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index-1f5435d2.css">
|
||||
<script type="module" crossorigin src="/assets/index-a5a78393.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index-2e253ee6.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
|
|
|||
|
|
@ -164,6 +164,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -172,7 +173,7 @@ import { ref, onMounted, onUnmounted, nextTick } from 'vue';
|
|||
import MindElixir from '../lib/mind-elixir/dist/MindElixir.js';
|
||||
import '../lib/mind-elixir/dist/style.css';
|
||||
|
||||
// 自定义淡紫色主题
|
||||
// 自定义主题
|
||||
const customTheme = {
|
||||
name: 'Light Purple',
|
||||
type: 'light',
|
||||
|
|
@ -209,6 +210,13 @@ const customTheme = {
|
|||
'--panel-bgcolor': '#ffffff',
|
||||
'--panel-border-color': '#eaeaea',
|
||||
'--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';
|
||||
|
|
@ -250,6 +258,7 @@ const imagePreviewError = ref('');
|
|||
|
||||
|
||||
|
||||
|
||||
// 显示欢迎页面
|
||||
const showWelcomePage = () => {
|
||||
showWelcome.value = true;
|
||||
|
|
@ -351,6 +360,7 @@ const retryLoadImage = () => {
|
|||
startImageLoadTimeout();
|
||||
};
|
||||
|
||||
|
||||
// 图片加载超时处理
|
||||
let imageLoadTimeout = null;
|
||||
const startImageLoadTimeout = () => {
|
||||
|
|
@ -543,6 +553,7 @@ const loadMindmapData = async (data, keepPosition = false, shouldCenterRoot = tr
|
|||
maxScale: 5,
|
||||
minScale: 0.1,
|
||||
theme: customTheme, // 应用自定义主题
|
||||
enableNodeBorder: true, // 启用节点边框功能
|
||||
markdown: (text, nodeObj) => {
|
||||
// 检查内容是否包含markdown语法(包括图片)
|
||||
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,
|
||||
minScale: 0.1,
|
||||
theme: customTheme, // 应用自定义主题
|
||||
enableNodeBorder: true, // 启用节点边框功能
|
||||
markdown: (text, nodeObj) => {
|
||||
// 检查内容是否包含markdown语法(包括图片)
|
||||
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,
|
||||
minScale: 0.1,
|
||||
theme: customTheme, // 应用自定义主题
|
||||
enableNodeBorder: true, // 启用节点边框功能
|
||||
markdown: (text, nodeObj) => {
|
||||
// 检查内容是否包含markdown语法(包括图片)
|
||||
if (text.includes('|') || text.includes('**') || text.includes('`') || text.includes('#') || text.includes('![')) {
|
||||
|
|
@ -4867,5 +4880,6 @@ const updateMindMapRealtime = async (data, title) => {
|
|||
.retry-button:hover {
|
||||
background: #7d0a8e;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
|
|
|
|||
|
|
@ -77,11 +77,11 @@
|
|||
margin: 10px;
|
||||
padding: 0;
|
||||
& > me-tpc {
|
||||
border-radius: var(--main-radius);
|
||||
background-color: var(--main-bgcolor);
|
||||
border: 2px solid var(--main-color);
|
||||
color: var(--main-color);
|
||||
padding: 8px 25px;
|
||||
border-radius: var(--main-radius) !important;
|
||||
background-color: var(--main-bgcolor) !important;
|
||||
border: 2px solid var(--main-color) !important;
|
||||
color: var(--main-color) !important;
|
||||
padding: 8px 25px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -118,9 +118,12 @@
|
|||
z-index: 10;
|
||||
me-tpc {
|
||||
position: relative;
|
||||
border-radius: 3px;
|
||||
border-radius: var(--main-radius, 20px); // 使用椭圆圆角
|
||||
color: var(--color);
|
||||
padding: var(--topic-padding);
|
||||
// 所有节点都使用紫色椭圆边框
|
||||
background-color: var(--node-border-fill, #ffffff);
|
||||
border: 2px solid var(--node-border-color, #660874);
|
||||
|
||||
// drag preview
|
||||
.insert-preview {
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ function MindElixir(
|
|||
handleWheel,
|
||||
markdown,
|
||||
imageProxy,
|
||||
enableNodeBorder,
|
||||
}: Options
|
||||
): void {
|
||||
let ele: HTMLElement | null = null
|
||||
|
|
@ -93,6 +94,7 @@ function MindElixir(
|
|||
this.handleWheel = handleWheel ?? true
|
||||
this.markdown = markdown || undefined // Custom markdown parser function
|
||||
this.imageProxy = imageProxy || undefined // Image proxy function
|
||||
this.enableNodeBorder = enableNodeBorder ?? false // Node border feature
|
||||
// this.parentMap = {} // deal with large amount of nodes
|
||||
this.currentNodes = [] // selected <tpc/> elements
|
||||
this.currentArrow = null // the selected link svg element
|
||||
|
|
|
|||
|
|
@ -41,6 +41,11 @@ export interface Topic extends HTMLElement {
|
|||
image?: HTMLImageElement
|
||||
icons?: HTMLSpanElement
|
||||
tags?: HTMLDivElement
|
||||
|
||||
// 节点边框相关属性
|
||||
borderRect?: SVGRectElement
|
||||
borderObserver?: MutationObserver
|
||||
borderResizeHandler?: () => void
|
||||
}
|
||||
|
||||
export interface Expander extends HTMLElement {
|
||||
|
|
|
|||
|
|
@ -56,6 +56,13 @@ export type Theme = {
|
|||
'--panel-bgcolor': string
|
||||
'--panel-border-color': 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
|
||||
*/
|
||||
imageProxy?: (url: string) => string
|
||||
/**
|
||||
* Enable node border feature
|
||||
* @default false
|
||||
*/
|
||||
enableNodeBorder?: boolean
|
||||
}
|
||||
|
||||
export type Uid = string
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import { LEFT } from '../const'
|
||||
import type { Topic, Wrapper, Parent, Children, Expander } from '../types/dom'
|
||||
import type { MindElixirInstance, NodeObj } from '../types/index'
|
||||
import { encodeHTML } from '../utils/index'
|
||||
import { encodeHTML, getOffsetLT } from '../utils/index'
|
||||
import { layoutChildren } from './layout'
|
||||
// 移除imageProcessor引用,使用MindElixir原生image属性
|
||||
|
||||
|
||||
// DOM manipulation
|
||||
const $d = document
|
||||
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)
|
||||
shapeTpc.call(this, tpc, nodeObj)
|
||||
p.appendChild(tpc)
|
||||
|
||||
return { p, tpc }
|
||||
}
|
||||
|
||||
|
||||
export const createChildren = function (this: MindElixirInstance, wrappers: Wrapper[]) {
|
||||
const children = $d.createElement('me-children') as Children
|
||||
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) {
|
||||
const GAP = parseInt(this.container.style.getPropertyValue('--node-gap-x')) // cache?
|
||||
// const GAP = 30
|
||||
let y1 = 0
|
||||
let end = 0
|
||||
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
|
||||
// 完全复制main函数的逻辑
|
||||
let x1 = pL + pW / 2
|
||||
const y1 = pT + pH / 2
|
||||
let x2
|
||||
if (direction === DirectionClass.LHS) {
|
||||
xMid = pL
|
||||
x1 = xMid + GAP
|
||||
x2 = xMid - GAP
|
||||
end = cL + GAP
|
||||
return `M ${x1} ${y1} C ${xMid} ${y1} ${xMid + offset} ${y2} ${x2} ${y2} H ${end}`
|
||||
x2 = cL + cW - 31 // 向内31像素,确保连接到边框内部
|
||||
} else {
|
||||
xMid = pL + pW
|
||||
x1 = xMid - GAP
|
||||
x2 = xMid + GAP
|
||||
end = cL + cW - GAP
|
||||
return `M ${x1} ${y1} C ${xMid} ${y1} ${xMid - offset} ${y2} ${x2} ${y2} H ${end}`
|
||||
x2 = cL + 31 // 向内31像素,确保连接到边框内部
|
||||
}
|
||||
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