update at 2026-01-22 19:50:23
This commit is contained in:
@@ -46,11 +46,8 @@
|
||||
}"
|
||||
>
|
||||
<!-- 背景 -->
|
||||
<rect
|
||||
:x="viewBoxMin"
|
||||
:y="viewBoxMin"
|
||||
:width="viewBoxSize"
|
||||
:height="viewBoxSize"
|
||||
<circle
|
||||
:r="outerMost || viewBoxSize / 2"
|
||||
:fill="config?.background || '#ffffff'"
|
||||
/>
|
||||
|
||||
@@ -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"
|
||||
/>
|
||||
</g>
|
||||
|
||||
@@ -168,6 +162,30 @@
|
||||
</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]">
|
||||
<circle
|
||||
@@ -352,6 +370,24 @@ const viewBoxSize = computed(() => {
|
||||
|
||||
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 合并缩放/拖拽更新,减少渲染频率
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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[];
|
||||
|
||||
Reference in New Issue
Block a user