update at 2026-01-22 19:50:23
This commit is contained in:
@@ -1,8 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "demo",
|
"name": "demo",
|
||||||
"description": "luopan demo config with named color palettes",
|
"description": "luopan demo config with named color palettes",
|
||||||
"background": "#fff000",
|
"background": "金",
|
||||||
"outerRadius": 500,
|
"strokeWidth": 0.5,
|
||||||
|
"strokeColor": "冷",
|
||||||
|
"strokeOpacity": 0.5,
|
||||||
"theme": {
|
"theme": {
|
||||||
"name": "五行配色主题",
|
"name": "五行配色主题",
|
||||||
"colorPalettes": {
|
"colorPalettes": {
|
||||||
@@ -12,12 +14,12 @@
|
|||||||
"木": "#43A047",
|
"木": "#43A047",
|
||||||
"火": "#E53935",
|
"火": "#E53935",
|
||||||
"土": "#8D6E63",
|
"土": "#8D6E63",
|
||||||
"金": "#78909C",
|
"金": "#D4AF37",
|
||||||
"水": "#0288D1",
|
"水": "#0288D1",
|
||||||
"热": "#FF8F00",
|
"热": "#FF8F00",
|
||||||
"冷": "#1976D2",
|
"冷": "#1976D2",
|
||||||
"强": "#D32F2F",
|
"强": "#D32F2F",
|
||||||
"\u8f6f": "#FFE0B2"
|
"软": "#FFE0B2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"layers": [
|
"layers": [
|
||||||
@@ -25,7 +27,7 @@
|
|||||||
"type": "centerIcon",
|
"type": "centerIcon",
|
||||||
"centerIcon": {
|
"centerIcon": {
|
||||||
"rIcon": 50,
|
"rIcon": 50,
|
||||||
"opacity": 0.8,
|
"opacity": 0.5,
|
||||||
"name": "centericon.svg"
|
"name": "centericon.svg"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -89,7 +91,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"divisions": 24,
|
"divisions": 24,
|
||||||
"rInner": 210,
|
"rInner": 200,
|
||||||
"rOuter": 240,
|
"rOuter": 240,
|
||||||
"startAngle": 0,
|
"startAngle": 0,
|
||||||
"sectors": [
|
"sectors": [
|
||||||
@@ -121,7 +123,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"divisions": 16,
|
"divisions": 16,
|
||||||
"rInner": 240,
|
"rInner": 250,
|
||||||
"rOuter": 270,
|
"rOuter": 270,
|
||||||
"startAngle": 0,
|
"startAngle": 0,
|
||||||
"sectors": [
|
"sectors": [
|
||||||
|
|||||||
@@ -17,7 +17,10 @@
|
|||||||
-- ========================================
|
-- ========================================
|
||||||
-- 全局配置
|
-- 全局配置
|
||||||
-- ========================================
|
-- ========================================
|
||||||
"background": "#000000", -- 全局背景色,未着色扇区使用此颜色
|
"background": "黑", -- 全局背景色,未着色扇区使用此颜色(支持 colorPalettes)
|
||||||
|
"strokeWidth": 0.3, -- 扇区边界线宽度(像素)
|
||||||
|
"strokeColor": "灰", -- 扇区边界线颜色(支持 colorPalettes)
|
||||||
|
"strokeOpacity": 0.15, -- 扇区边界线透明度
|
||||||
"outerRadius": 500, -- 罗盘外半径,单位:像素
|
"outerRadius": 500, -- 罗盘外半径,单位:像素
|
||||||
|
|
||||||
-- ========================================
|
-- ========================================
|
||||||
|
|||||||
@@ -188,7 +188,10 @@ function splitMultiTextUnits(
|
|||||||
export interface LuopanConfig {
|
export interface LuopanConfig {
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
background: string; // 全局背景色
|
background: string; // 全局背景色(支持主题色)
|
||||||
|
strokeWidth?: number; // 扇区边界线宽度
|
||||||
|
strokeColor?: string; // 扇区边界线颜色(支持主题色)
|
||||||
|
strokeOpacity?: number; // 扇区边界线透明度
|
||||||
theme: ThemeConfig;
|
theme: ThemeConfig;
|
||||||
layers: LayerConfig[]; // 扇区层 + 中心图标层 + 刻度环层
|
layers: LayerConfig[]; // 扇区层 + 中心图标层 + 刻度环层
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,11 +46,8 @@
|
|||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<!-- 背景 -->
|
<!-- 背景 -->
|
||||||
<rect
|
<circle
|
||||||
:x="viewBoxMin"
|
:r="outerMost || viewBoxSize / 2"
|
||||||
:y="viewBoxMin"
|
|
||||||
:width="viewBoxSize"
|
|
||||||
:height="viewBoxSize"
|
|
||||||
:fill="config?.background || '#ffffff'"
|
:fill="config?.background || '#ffffff'"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -62,9 +59,6 @@
|
|||||||
:key="s.key"
|
:key="s.key"
|
||||||
:d="s.path"
|
:d="s.path"
|
||||||
:fill="s.fill"
|
:fill="s.fill"
|
||||||
stroke="#1f2937"
|
|
||||||
:stroke-opacity="s.groupSplitVisible === false ? 0 : 0.15"
|
|
||||||
:stroke-width="SECTOR_STROKE_WIDTH"
|
|
||||||
/>
|
/>
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
@@ -168,6 +162,30 @@
|
|||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
|
<!-- 扇区边界线 -->
|
||||||
|
<g
|
||||||
|
v-memo="[sectors, boundaryRings, strokeColor, strokeOpacity, strokeWidth]"
|
||||||
|
:stroke="strokeColor"
|
||||||
|
:stroke-opacity="strokeOpacity"
|
||||||
|
:stroke-width="strokeWidth"
|
||||||
|
>
|
||||||
|
<circle
|
||||||
|
v-for="r in boundaryRings"
|
||||||
|
:key="'ring-outline-' + r"
|
||||||
|
:r="r"
|
||||||
|
fill="none"
|
||||||
|
/>
|
||||||
|
<template v-for="s in sectors" :key="'split-' + s.key">
|
||||||
|
<line
|
||||||
|
v-if="s.groupSplitVisible !== false"
|
||||||
|
:x1="toXY(s.aStart, s.rInner).x"
|
||||||
|
:y1="toXY(s.aStart, s.rInner).y"
|
||||||
|
:x2="toXY(s.aStart, s.rOuter).x"
|
||||||
|
:y2="toXY(s.aStart, s.rOuter).y"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</g>
|
||||||
|
|
||||||
<!-- 形心点(仅辅助线开启时显示) -->
|
<!-- 形心点(仅辅助线开启时显示) -->
|
||||||
<g v-if="showGuides" v-memo="[sectors]">
|
<g v-if="showGuides" v-memo="[sectors]">
|
||||||
<circle
|
<circle
|
||||||
@@ -352,6 +370,24 @@ const viewBoxSize = computed(() => {
|
|||||||
|
|
||||||
const viewBoxMin = computed(() => -viewBoxSize.value / 2);
|
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<number>();
|
||||||
|
sectors.value.forEach((sector) => {
|
||||||
|
if (sector.rInner > 0) set.add(sector.rInner);
|
||||||
|
set.add(sector.rOuter);
|
||||||
|
});
|
||||||
|
return Array.from(set);
|
||||||
|
});
|
||||||
|
|
||||||
// 使用 rAF 合并缩放/拖拽更新,减少渲染频率
|
// 使用 rAF 合并缩放/拖拽更新,减少渲染频率
|
||||||
const scheduleTransform = () => {
|
const scheduleTransform = () => {
|
||||||
if (rafId !== null) return;
|
if (rafId !== null) return;
|
||||||
@@ -529,7 +565,7 @@ button.active {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.svg {
|
.svg {
|
||||||
background: #fff;
|
background: transparent;
|
||||||
transition: transform 0.1s ease-out;
|
transition: transform 0.1s ease-out;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,18 @@ import { loadCenterIcon } from '../centerIcon';
|
|||||||
const isSectorLayer = (layer: LayerConfig): layer is SectorLayerConfig =>
|
const isSectorLayer = (layer: LayerConfig): layer is SectorLayerConfig =>
|
||||||
layer.type !== 'centerIcon' && layer.type !== 'degreeRing';
|
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[]) =>
|
const findDegreeRingLayer = (layers: LayerConfig[]) =>
|
||||||
layers.find((layer) => layer.type === 'degreeRing');
|
layers.find((layer) => layer.type === 'degreeRing');
|
||||||
|
|
||||||
@@ -71,15 +83,38 @@ export function useLuopan(
|
|||||||
configObj = configPathOrObject;
|
configObj = configPathOrObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
config.value = configObj;
|
const resolvedBackground = resolveThemeColor(
|
||||||
sectors.value = buildSectors(configObj);
|
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
|
degreeRing.value = degreeRingLayer
|
||||||
? buildDegreeRing(degreeRingLayer.degreeRing)
|
? buildDegreeRing(degreeRingLayer.degreeRing)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
const centerIconLayer = findCenterIconLayer(configObj.layers);
|
const centerIconLayer = findCenterIconLayer(resolvedConfig.layers);
|
||||||
centerIcon.value = centerIconLayer
|
centerIcon.value = centerIconLayer
|
||||||
? await loadCenterIcon(centerIconLayer.centerIcon)
|
? await loadCenterIcon(centerIconLayer.centerIcon)
|
||||||
: null;
|
: null;
|
||||||
@@ -116,14 +151,17 @@ export function useLuopan(
|
|||||||
|
|
||||||
const outerMost = computed(() => {
|
const outerMost = computed(() => {
|
||||||
if (!config.value) return 0;
|
if (!config.value) return 0;
|
||||||
if (typeof config.value.outerRadius === 'number') return config.value.outerRadius;
|
|
||||||
|
|
||||||
const radii = sectorLayers.value.map((layer) => layer.rOuter);
|
const radii = sectorLayers.value.map((layer) => layer.rOuter);
|
||||||
const degreeRingLayer = findDegreeRingLayer(config.value.layers);
|
const degreeRingLayer = findDegreeRingLayer(config.value.layers);
|
||||||
if (degreeRingLayer) {
|
if (degreeRingLayer) {
|
||||||
radii.push(degreeRingLayer.degreeRing.rOuter);
|
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 {
|
return {
|
||||||
|
|||||||
@@ -76,6 +76,9 @@ export const parseConfig = (jsonText: string): LuopanConfig => {
|
|||||||
name: config.name,
|
name: config.name,
|
||||||
description: typeof config.description === 'string' ? config.description : undefined,
|
description: typeof config.description === 'string' ? config.description : undefined,
|
||||||
background: config.background,
|
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,
|
outerRadius: typeof config.outerRadius === 'number' ? config.outerRadius : undefined,
|
||||||
theme: normalizeTheme(config.theme),
|
theme: normalizeTheme(config.theme),
|
||||||
layers: config.layers as LuopanConfig['layers'],
|
layers: config.layers as LuopanConfig['layers'],
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ export interface LuopanConfig {
|
|||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
background: string;
|
background: string;
|
||||||
|
strokeWidth?: number;
|
||||||
|
strokeColor?: string;
|
||||||
|
strokeOpacity?: number;
|
||||||
outerRadius?: number;
|
outerRadius?: number;
|
||||||
theme: ThemeConfig;
|
theme: ThemeConfig;
|
||||||
layers: LayerConfig[];
|
layers: LayerConfig[];
|
||||||
|
|||||||
@@ -12,7 +12,8 @@
|
|||||||
字体大小和位置通过工具函数自动计算。单个扇区内的多文本单元之间无分割线。
|
字体大小和位置通过工具函数自动计算。单个扇区内的多文本单元之间无分割线。
|
||||||
4. 废弃目前颜色生成规则。使用在Json中严格配置。详见《Json文件配置》。总体规则是:优先级低到高分别是,全局背景色 - layer着色 - sectors着色。
|
4. 废弃目前颜色生成规则。使用在Json中严格配置。详见《Json文件配置》。总体规则是:优先级低到高分别是,全局背景色 - layer着色 - sectors着色。
|
||||||
5. Json中不用关注是第几层,按配置的rinner,router等参数绘制即可。
|
5. Json中不用关注是第几层,按配置的rinner,router等参数绘制即可。
|
||||||
|
6. 在json中增加全局中定义strokeWidth,strokeColor,strokeOpacity, 如在json中定义,覆盖constants中SECTOR_STROKE_WIDTH,和stroke,如json每定义,使用代码中默认。颜色可以使用colorPalettes中定义的颜色,background也可以使用colorPalettes定义的颜色。
|
||||||
|
7. 需求:json中配置的背景色,只对罗盘区域着色
|
||||||
## Json文件配置
|
## Json文件配置
|
||||||
### json字段配置说明
|
### json字段配置说明
|
||||||
|
|
||||||
@@ -21,7 +22,10 @@
|
|||||||
|--------|------|------|------|------|
|
|--------|------|------|------|------|
|
||||||
| name | string | 是 | 罗盘配置名称 | "demo" |
|
| name | string | 是 | 罗盘配置名称 | "demo" |
|
||||||
| description | string | 否 | 罗盘配置描述 | "luopan demo config" |
|
| 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 |
|
| outerRadius | number | 是 | 罗盘外半径,单位:像素(去掉该参数,自动计算最大半径) | 500 |
|
||||||
| theme | object | 是 | 主题配置对象,包含colorPalettes命名配色方案 | 见下方theme说明 |
|
| theme | object | 是 | 主题配置对象,包含colorPalettes命名配色方案 | 见下方theme说明 |
|
||||||
| centerIcon | object | 否 | 中心图标配置 | 见下方centerIcon说明 |
|
| centerIcon | object | 否 | 中心图标配置 | 见下方centerIcon说明 |
|
||||||
@@ -209,4 +213,3 @@
|
|||||||
"tickColor": "#000000",-- 刻度线颜色
|
"tickColor": "#000000",-- 刻度线颜色
|
||||||
"ringColor": "#000000" -- 圆环颜色
|
"ringColor": "#000000" -- 圆环颜色
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user