新增自定义颜色方案
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user