From a3e77045ac87c598a6cc7c26ec69a0c49950ff16 Mon Sep 17 00:00:00 2001 From: douboer Date: Thu, 22 Jan 2026 19:50:23 +0800 Subject: [PATCH] update at 2026-01-22 19:50:23 --- public/demo.json | 16 ++++++----- public/demo.json.conf | 5 +++- refactor-plan.md | 5 +++- src/Luopan.vue | 54 ++++++++++++++++++++++++++++++------ src/composables/useLuopan.ts | 50 +++++++++++++++++++++++++++++---- src/configParser.ts | 3 ++ src/types.ts | 3 ++ todolist.md | 9 ++++-- 8 files changed, 118 insertions(+), 27 deletions(-) diff --git a/public/demo.json b/public/demo.json index c8b20d6..5923d98 100644 --- a/public/demo.json +++ b/public/demo.json @@ -1,8 +1,10 @@ { "name": "demo", "description": "luopan demo config with named color palettes", - "background": "#fff000", - "outerRadius": 500, + "background": "金", + "strokeWidth": 0.5, + "strokeColor": "冷", + "strokeOpacity": 0.5, "theme": { "name": "五行配色主题", "colorPalettes": { @@ -12,12 +14,12 @@ "木": "#43A047", "火": "#E53935", "土": "#8D6E63", - "金": "#78909C", + "金": "#D4AF37", "水": "#0288D1", "热": "#FF8F00", "冷": "#1976D2", "强": "#D32F2F", - "\u8f6f": "#FFE0B2" + "软": "#FFE0B2" } }, "layers": [ @@ -25,7 +27,7 @@ "type": "centerIcon", "centerIcon": { "rIcon": 50, - "opacity": 0.8, + "opacity": 0.5, "name": "centericon.svg" } }, @@ -89,7 +91,7 @@ }, { "divisions": 24, - "rInner": 210, + "rInner": 200, "rOuter": 240, "startAngle": 0, "sectors": [ @@ -121,7 +123,7 @@ }, { "divisions": 16, - "rInner": 240, + "rInner": 250, "rOuter": 270, "startAngle": 0, "sectors": [ diff --git a/public/demo.json.conf b/public/demo.json.conf index d73c2f6..7ded500 100644 --- a/public/demo.json.conf +++ b/public/demo.json.conf @@ -17,7 +17,10 @@ -- ======================================== -- 全局配置 -- ======================================== - "background": "#000000", -- 全局背景色,未着色扇区使用此颜色 + "background": "黑", -- 全局背景色,未着色扇区使用此颜色(支持 colorPalettes) + "strokeWidth": 0.3, -- 扇区边界线宽度(像素) + "strokeColor": "灰", -- 扇区边界线颜色(支持 colorPalettes) + "strokeOpacity": 0.15, -- 扇区边界线透明度 "outerRadius": 500, -- 罗盘外半径,单位:像素 -- ======================================== diff --git a/refactor-plan.md b/refactor-plan.md index 3b9cc57..f6acdac 100644 --- a/refactor-plan.md +++ b/refactor-plan.md @@ -188,7 +188,10 @@ function splitMultiTextUnits( export interface LuopanConfig { name: string; description?: string; - background: string; // 全局背景色 + background: string; // 全局背景色(支持主题色) + strokeWidth?: number; // 扇区边界线宽度 + strokeColor?: string; // 扇区边界线颜色(支持主题色) + strokeOpacity?: number; // 扇区边界线透明度 theme: ThemeConfig; layers: LayerConfig[]; // 扇区层 + 中心图标层 + 刻度环层 } diff --git a/src/Luopan.vue b/src/Luopan.vue index a8f8e25..7fd4b04 100644 --- a/src/Luopan.vue +++ b/src/Luopan.vue @@ -46,11 +46,8 @@ }" > - @@ -62,9 +59,6 @@ :key="s.key" :d="s.path" :fill="s.fill" - stroke="#1f2937" - :stroke-opacity="s.groupSplitVisible === false ? 0 : 0.15" - :stroke-width="SECTOR_STROKE_WIDTH" /> @@ -168,6 +162,30 @@ + + + + + + { const viewBoxMin = computed(() => -viewBoxSize.value / 2); +const strokeWidth = computed(() => + typeof config.value?.strokeWidth === 'number' + ? config.value.strokeWidth + : SECTOR_STROKE_WIDTH +); +const strokeColor = computed(() => config.value?.strokeColor ?? '#1f2937'); +const strokeOpacity = computed(() => + typeof config.value?.strokeOpacity === 'number' ? config.value.strokeOpacity : 0.15 +); +const boundaryRings = computed(() => { + const set = new Set(); + sectors.value.forEach((sector) => { + if (sector.rInner > 0) set.add(sector.rInner); + set.add(sector.rOuter); + }); + return Array.from(set); +}); + // 使用 rAF 合并缩放/拖拽更新,减少渲染频率 const scheduleTransform = () => { if (rafId !== null) return; @@ -529,7 +565,7 @@ button.active { } .svg { - background: #fff; + background: transparent; transition: transform 0.1s ease-out; user-select: none; } diff --git a/src/composables/useLuopan.ts b/src/composables/useLuopan.ts index 2fe0598..0e9a951 100644 --- a/src/composables/useLuopan.ts +++ b/src/composables/useLuopan.ts @@ -22,6 +22,18 @@ import { loadCenterIcon } from '../centerIcon'; const isSectorLayer = (layer: LayerConfig): layer is SectorLayerConfig => layer.type !== 'centerIcon' && layer.type !== 'degreeRing'; +const HEX_COLOR_RE = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/; + +const resolveThemeColor = ( + theme: LuopanConfig['theme'], + value: string | undefined, + fallback: string +) => { + if (!value) return fallback; + if (HEX_COLOR_RE.test(value)) return value; + return theme.colorPalettes[value] ?? fallback; +}; + const findDegreeRingLayer = (layers: LayerConfig[]) => layers.find((layer) => layer.type === 'degreeRing'); @@ -71,15 +83,38 @@ export function useLuopan( configObj = configPathOrObject; } - config.value = configObj; - sectors.value = buildSectors(configObj); + const resolvedBackground = resolveThemeColor( + configObj.theme, + configObj.background, + '#000000' + ); + const resolvedStrokeColor = resolveThemeColor( + configObj.theme, + configObj.strokeColor, + '#1f2937' + ); - const degreeRingLayer = findDegreeRingLayer(configObj.layers); + const resolvedConfig: LuopanConfig = { + ...configObj, + background: resolvedBackground, + strokeColor: resolvedStrokeColor, + strokeWidth: typeof configObj.strokeWidth === 'number' + ? configObj.strokeWidth + : undefined, + strokeOpacity: typeof configObj.strokeOpacity === 'number' + ? configObj.strokeOpacity + : undefined, + }; + + config.value = resolvedConfig; + sectors.value = buildSectors(resolvedConfig); + + const degreeRingLayer = findDegreeRingLayer(resolvedConfig.layers); degreeRing.value = degreeRingLayer ? buildDegreeRing(degreeRingLayer.degreeRing) : null; - const centerIconLayer = findCenterIconLayer(configObj.layers); + const centerIconLayer = findCenterIconLayer(resolvedConfig.layers); centerIcon.value = centerIconLayer ? await loadCenterIcon(centerIconLayer.centerIcon) : null; @@ -116,14 +151,17 @@ export function useLuopan( const outerMost = computed(() => { if (!config.value) return 0; - if (typeof config.value.outerRadius === 'number') return config.value.outerRadius; const radii = sectorLayers.value.map((layer) => layer.rOuter); const degreeRingLayer = findDegreeRingLayer(config.value.layers); if (degreeRingLayer) { radii.push(degreeRingLayer.degreeRing.rOuter); } - return radii.length > 0 ? Math.max(...radii) : 0; + + const maxRadius = radii.length > 0 ? Math.max(...radii) : 0; + if (maxRadius > 0) return maxRadius; + + return typeof config.value.outerRadius === 'number' ? config.value.outerRadius : 0; }); return { diff --git a/src/configParser.ts b/src/configParser.ts index 9d6bc8a..468e195 100644 --- a/src/configParser.ts +++ b/src/configParser.ts @@ -76,6 +76,9 @@ export const parseConfig = (jsonText: string): LuopanConfig => { name: config.name, description: typeof config.description === 'string' ? config.description : undefined, background: config.background, + strokeWidth: typeof config.strokeWidth === 'number' ? config.strokeWidth : undefined, + strokeColor: typeof config.strokeColor === 'string' ? config.strokeColor : undefined, + strokeOpacity: typeof config.strokeOpacity === 'number' ? config.strokeOpacity : undefined, outerRadius: typeof config.outerRadius === 'number' ? config.outerRadius : undefined, theme: normalizeTheme(config.theme), layers: config.layers as LuopanConfig['layers'], diff --git a/src/types.ts b/src/types.ts index c423cea..07d49fa 100644 --- a/src/types.ts +++ b/src/types.ts @@ -26,6 +26,9 @@ export interface LuopanConfig { name: string; description?: string; background: string; + strokeWidth?: number; + strokeColor?: string; + strokeOpacity?: number; outerRadius?: number; theme: ThemeConfig; layers: LayerConfig[]; diff --git a/todolist.md b/todolist.md index f44aeac..805ff5c 100644 --- a/todolist.md +++ b/todolist.md @@ -12,7 +12,8 @@ 字体大小和位置通过工具函数自动计算。单个扇区内的多文本单元之间无分割线。 4. 废弃目前颜色生成规则。使用在Json中严格配置。详见《Json文件配置》。总体规则是:优先级低到高分别是,全局背景色 - layer着色 - sectors着色。 5. Json中不用关注是第几层,按配置的rinner,router等参数绘制即可。 - +6. 在json中增加全局中定义strokeWidth,strokeColor,strokeOpacity, 如在json中定义,覆盖constants中SECTOR_STROKE_WIDTH,和stroke,如json每定义,使用代码中默认。颜色可以使用colorPalettes中定义的颜色,background也可以使用colorPalettes定义的颜色。 +7. 需求:json中配置的背景色,只对罗盘区域着色 ## Json文件配置 ### json字段配置说明 @@ -21,7 +22,10 @@ |--------|------|------|------|------| | name | string | 是 | 罗盘配置名称 | "demo" | | description | string | 否 | 罗盘配置描述 | "luopan demo config" | - | background | string | 是 | 全局背景色(十六进制颜色值),未着色扇区使用此颜色 | "#000000" | + | background | string | 是 | 全局背景色(支持十六进制或 colorPalettes 名称),未着色扇区使用此颜色 | "#000000" | + | strokeWidth | number | 否 | 扇区边界线宽度(像素),覆盖默认值 | 0.3 | + | strokeColor | string | 否 | 扇区边界线颜色(支持十六进制或 colorPalettes 名称) | "#1f2937" | + | strokeOpacity | number | 否 | 扇区边界线透明度(0.0-1.0) | 0.15 | | outerRadius | number | 是 | 罗盘外半径,单位:像素(去掉该参数,自动计算最大半径) | 500 | | theme | object | 是 | 主题配置对象,包含colorPalettes命名配色方案 | 见下方theme说明 | | centerIcon | object | 否 | 中心图标配置 | 见下方centerIcon说明 | @@ -209,4 +213,3 @@ "tickColor": "#000000",-- 刻度线颜色 "ringColor": "#000000" -- 圆环颜色 } -