新增自定义颜色方案

This commit is contained in:
JIANG
2025-11-20 18:34:54 +08:00
parent 11ccd53672
commit 23154b2b5f
3 changed files with 167 additions and 16 deletions

View File

@@ -49,6 +49,7 @@ interface StyleConfig {
opacity: number;
adjustWidthByProperty: boolean; // 是否根据属性调整线条宽度
customBreaks?: number[]; // 自定义断点(用于 custom_breaks
customColors?: string[]; // 自定义颜色(用于 colorType="custom"
}
// 图层样式状态接口
@@ -149,6 +150,29 @@ const CLASSIFICATION_METHODS = [
{ name: "自定义", value: "custom_breaks" },
];
const rgbaToHex = (rgba: string) => {
try {
const c = parseColor(rgba);
const toHex = (n: number) => {
const hex = Math.round(n).toString(16);
return hex.length === 1 ? "0" + hex : hex;
};
return `#${toHex(c.r)}${toHex(c.g)}${toHex(c.b)}`;
} catch (e) {
return "#000000";
}
};
const hexToRgba = (hex: string) => {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result
? `rgba(${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(
result[3],
16
)}, 1)`
: "rgba(0, 0, 0, 1)";
};
const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
layerStyleStates,
setLayerStyleStates,
@@ -199,6 +223,7 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
opacity: 0.9,
adjustWidthByProperty: true,
customBreaks: [],
customColors: [],
});
// 根据分段数生成相应数量的渐进颜色
@@ -467,7 +492,18 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
})
: styleConfig.colorType === "gradient"
? generateGradientColors(breaksLength)
: generateRainbowColors(breaksLength);
: styleConfig.colorType === "rainbow"
? generateRainbowColors(breaksLength)
: (() => {
// 自定义颜色
const custom = styleConfig.customColors || [];
// 如果自定义颜色数量不足,用默认颜色补齐(这里简单用红色,或者重复最后一个)
const result = [...custom];
while (result.length < breaksLength) {
result.push(result[result.length - 1] || "rgba(255, 0, 0, 1)");
}
return result.slice(0, breaksLength);
})();
// 计算每个分段的线条粗细和点大小
const dimensions: number[] =
layerType === "linestring"
@@ -889,6 +925,22 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
currentPipeCalData,
]);
// 初始化或调整自定义颜色数组长度
useEffect(() => {
const numColors = styleConfig.segments;
setStyleConfig((prev) => {
const prevColors = prev.customColors || [];
if (prevColors.length === numColors) return prev;
const newColors = [...prevColors];
const baseColors = RAINBOW_PALETTES[0].colors;
while (newColors.length < numColors) {
newColors.push(baseColors[newColors.length % baseColors.length]);
}
return { ...prev, customColors: newColors.slice(0, numColors) };
});
}, [styleConfig.segments]);
const getColorSetting = () => {
if (styleConfig.colorType === "single") {
return (
@@ -1069,6 +1121,54 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
</FormControl>
);
}
if (styleConfig.colorType === "custom") {
return (
<Box className="mt-3">
<Typography variant="subtitle2" gutterBottom>
</Typography>
<Box
className="flex flex-col gap-2"
sx={{ maxHeight: "160px", overflowY: "auto", paddingTop: "4px" }}
>
{Array.from({ length: styleConfig.segments }).map((_, idx) => {
const color =
(styleConfig.customColors && styleConfig.customColors[idx]) ||
"rgba(0,0,0,1)";
return (
<Box key={idx} className="flex items-center gap-2">
<Typography variant="caption" sx={{ width: 40 }}>
{idx + 1}
</Typography>
<input
type="color"
value={rgbaToHex(color)}
onChange={(e) => {
const hex = e.target.value;
const newColor = hexToRgba(hex);
setStyleConfig((prev) => {
const newColors = [...(prev.customColors || [])];
while (newColors.length < styleConfig.segments)
newColors.push("rgba(0,0,0,1)");
newColors[idx] = newColor;
return { ...prev, customColors: newColors };
});
}}
style={{
width: "100%",
height: "32px",
cursor: "pointer",
border: "1px solid #ccc",
borderRadius: "4px",
}}
/>
</Box>
);
})}
</Box>
</Box>
);
}
};
// 根据不同图层的类型和颜色分类方案显示不同的大小设置
const getSizeSetting = () => {
@@ -1084,6 +1184,12 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
const rainbowColors =
RAINBOW_PALETTES[styleConfig.rainbowPaletteIndex].colors;
colors = [rainbowColors[0], rainbowColors[rainbowColors.length - 1]];
} else if (styleConfig.colorType === "custom") {
const customColors = styleConfig.customColors || [];
colors = [
customColors[0] || "rgba(0,0,0,1)",
customColors[customColors.length - 1] || "rgba(0,0,0,1)",
];
}
if (selectedRenderLayer?.get("type") === "point") {
@@ -1362,7 +1468,21 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
<Slider
value={styleConfig.segments}
onChange={(_, value) =>
setStyleConfig((prev) => ({ ...prev, segments: value as number }))
setStyleConfig((prev) => {
const newSegments = value as number;
const newCustomColors = [...(prev.customColors || [])];
if (newSegments > newCustomColors.length) {
const baseColors = RAINBOW_PALETTES[0].colors;
for (let i = newCustomColors.length; i < newSegments; i++) {
newCustomColors.push(baseColors[i % baseColors.length]);
}
}
return {
...prev,
segments: newSegments,
customColors: newCustomColors,
};
})
}
min={2}
max={10}
@@ -1379,7 +1499,7 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
</Typography>
<Box
className="flex flex-col gap-2"
sx={{ maxHeight: "240px", overflowY: "auto", paddingTop: "12px" }}
sx={{ maxHeight: "160px", overflowY: "auto", paddingTop: "12px" }}
>
{Array.from({ length: styleConfig.segments }).map((_, idx) => (
<TextField
@@ -1400,7 +1520,7 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
? [...prev.customBreaks]
: [];
// 保证长度
while (prevBreaks.length < styleConfig.segments + 1)
while (prevBreaks.length < styleConfig.segments)
prevBreaks.push(0);
prevBreaks[idx] = isNaN(v) ? 0 : Math.max(0, v);
return { ...prev, customBreaks: prevBreaks };
@@ -1430,16 +1550,32 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
</InputLabel>
<Select
value={styleConfig.colorType}
onChange={(e) =>
setStyleConfig((prev) => ({
...prev,
colorType: e.target.value as "single" | "gradient" | "rainbow",
}))
}
onChange={(e) => {
const newColorType = e.target.value;
setStyleConfig((prev) => {
let newCustomColors = prev.customColors;
if (
newColorType === "custom" &&
(!prev.customColors || prev.customColors.length === 0)
) {
const baseColors = RAINBOW_PALETTES[0].colors;
newCustomColors = Array.from(
{ length: prev.segments },
(_, i) => baseColors[i % baseColors.length]
);
}
return {
...prev,
colorType: newColorType,
customColors: newCustomColors,
};
});
}}
>
<MenuItem value="single"></MenuItem>
<MenuItem value="gradient"></MenuItem>
<MenuItem value="rainbow"></MenuItem>
<MenuItem value="custom"></MenuItem>
</Select>
{getColorSetting()}
</FormControl>

View File

@@ -666,10 +666,11 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
layers: [],
});
deckFlowRef.current = deckFlow;
const deckFlowLayer = new DeckLayer(deckFlow);
deckFlowLayer.set("name", "水流动画");
deckFlowLayer.set("value", "waterflow");
deckFlowLayer.set("type", "animation");
const deckFlowLayer = new DeckLayer(deckFlow, {
name: "水流动画",
value: "waterflow",
type: "animation",
});
// 初始化用户可见性状态(默认为 true
deckFlowLayer.initUserVisibility("waterflowLayer", true);
// 设置可见性变化回调,同步更新 waterflowUserVisible

View File

@@ -11,9 +11,18 @@ export class DeckLayer extends Layer {
private onVisibilityChange?: (layerId: string, visible: boolean) => void;
private userVisibility: Map<string, boolean> = new Map(); // 存储用户设置的可见性
constructor(deckInstance: Deck) {
super({});
/**
* @param deckInstance deck.gl 实例
* @param layerProperties 可选:在构造时直接设置到 OpenLayers Layer 的 properties
*/
constructor(deckInstance: Deck, layerProperties?: Record<string, any>) {
// 将 layerProperties 作为 Layer 的 properties 传入
super({ properties: layerProperties || {} });
this.deck = deckInstance;
// 再次确保属性应用到实例(兼容场景)
if (layerProperties) {
this.setProperties(layerProperties);
}
}
// 设置可见性变化回调
@@ -118,4 +127,9 @@ export class DeckLayer extends Layer {
this.setDeckLayerVisible(layerId, !currentVisible);
}
}
// 可选的封装:在外部用更语义化的方式设置属性(内部可直接调用 setProperties
setLayerProperties(props: Record<string, any>): void {
this.setProperties(props);
}
}