Skip to content

自定义样式

SDK 支持灵活的地图样式自定义功能,可以通过多种方式修改地图外观,包括图层样式覆盖、主题切换等。

样式覆盖

使用 setStyle 方法

setStyle 方法允许你覆盖特定图层的样式:

javascript
sdk.setStyle({
  // 单个图层样式
  "airport-label": {
    paint: {
      "text-color": "#ff0000",
      "text-size": 14,
    },
  },
  
  // 多个图层样式
  "airline": {
    paint: {
      "line-color": "#00ff00",
      "line-width": 2,
    },
  },
});

数组类型图层样式

对于数组类型的图层(如 ammcontrolledrestrictedairspaceairline),需要使用数组格式:

javascript
sdk.setStyle({
  amm: [
    {
      "amm-layer-1": {
        paint: {
          "fill-color": "#ff0000",
        },
      },
    },
    {
      "amm-layer-2": {
        paint: {
          "fill-color": "#00ff00",
        },
      },
    },
  ],
  
  controlled: [
    {
      "controlled-layer-1": {
        paint: {
          "fill-color": "#0000ff",
        },
      },
    },
  ],
});

使用 Theme 类

SDK 内部使用 Theme 类来管理样式,你也可以直接使用:

javascript
// Theme 类会自动处理样式应用
const theme = sdk.theme;

// 应用样式
theme.setStyle({
  "airport-label": {
    paint: {
      "text-color": "#ff0000",
    },
  },
});

图层样式更新

更新单个图层样式

javascript
sdk.updateLayerStyle("airport-label", {
  paint: {
    "text-color": "#ff0000",
    "text-size": 16,
  },
  layout: {
    "text-field": ["get", "name"],
    "text-font": ["Open Sans Semibold", "Arial Unicode MS Bold"],
  },
});

批量更新图层样式

javascript
// 更新多个图层
const styles = {
  "airport-label": {
    paint: { "text-color": "#ff0000" },
  },
  "airline": {
    paint: { "line-color": "#00ff00" },
  },
};

Object.entries(styles).forEach(([layerId, style]) => {
  sdk.updateLayerStyle(layerId, style);
});

动态样式切换

根据缩放级别切换样式

javascript
sdk.on("zoom", () => {
  const zoom = sdk.getZoom();
  
  if (zoom < 10) {
    // 低缩放级别:简化样式
    sdk.setStyle({
      "airport-label": {
        layout: {
          visibility: "none",
        },
      },
    });
  } else {
    // 高缩放级别:显示详细样式
    sdk.setStyle({
      "airport-label": {
        layout: {
          visibility: "visible",
        },
        paint: {
          "text-size": 14,
        },
      },
    });
  }
});

根据时间切换样式(夜间模式)

javascript
function toggleNightMode(isNight) {
  if (isNight) {
    sdk.setStyle({
      "airport-label": {
        paint: {
          "text-color": "#ffffff",
        },
      },
      "airline": {
        paint: {
          "line-color": "#ffff00",
        },
      },
    });
  } else {
    sdk.setStyle({
      "airport-label": {
        paint: {
          "text-color": "#000000",
        },
      },
      "airline": {
        paint: {
          "line-color": "#0000ff",
        },
      },
    });
  }
}

// 根据时间自动切换
const hour = new Date().getHours();
toggleNightMode(hour >= 18 || hour < 6);

主题配置

使用预定义主题

SDK 提供了预定义的主题样式文件:

javascript
// 加载暗色主题
const darkTheme = await fetch("/styles/dark.json").then(r => r.json());
sdk.setMapStyle(darkTheme);

// 加载亮色主题
const lightTheme = await fetch("/styles/light.json").then(r => r.json());
sdk.setMapStyle(lightTheme);

自定义主题配置

创建自定义主题配置文件:

json
{
  "airport-label": {
    "paint": {
      "text-color": "#ff0000",
      "text-size": 14
    }
  },
  "airline": {
    "paint": {
      "line-color": "#00ff00",
      "line-width": 2
    }
  },
  "amm": [
    {
      "amm-layer-1": {
        "paint": {
          "fill-color": "#0000ff"
        }
      }
    }
  ]
}

加载自定义主题:

javascript
async function loadCustomTheme(themePath) {
  const theme = await fetch(themePath).then(r => r.json());
  sdk.setStyle(theme);
}

loadCustomTheme("/styles/custom-theme.json");

样式属性

Paint 属性

paint 属性控制图层的视觉外观:

javascript
sdk.setStyle({
  "airport-label": {
    paint: {
      // 文本颜色
      "text-color": "#ff0000",
      // 文本大小
      "text-size": 14,
      // 文本描边颜色
      "text-halo-color": "#ffffff",
      // 文本描边宽度
      "text-halo-width": 2,
    },
  },
  
  "airline": {
    paint: {
      // 线条颜色
      "line-color": "#00ff00",
      // 线条宽度
      "line-width": 2,
      // 线条透明度
      "line-opacity": 0.8,
      // 虚线样式
      "line-dasharray": [2, 2],
    },
  },
});

Layout 属性

layout 属性控制图层的布局和可见性:

javascript
sdk.setStyle({
  "airport-label": {
    layout: {
      // 可见性
      visibility: "visible", // "visible" | "none"
      // 文本字段
      "text-field": ["get", "name"],
      // 文本字体
      "text-font": ["Open Sans Semibold", "Arial Unicode MS Bold"],
      // 文本锚点
      "text-anchor": "center",
      // 文本偏移
      "text-offset": [0, 1],
    },
  },
});

样式表达式

SDK 支持 MapBox 样式表达式,可以实现动态样式:

javascript
sdk.setStyle({
  "airport-label": {
    paint: {
      // 根据属性值设置颜色
      "text-color": [
        "case",
        ["==", ["get", "type"], "civil"],
        "#0000ff", // 民用机场:蓝色
        "#ff0000", // 军用机场:红色
      ],
      
      // 根据缩放级别设置大小
      "text-size": [
        "interpolate",
        ["linear"],
        ["zoom"],
        10, 12,  // 缩放级别 10 时大小为 12
        15, 16,  // 缩放级别 15 时大小为 16
      ],
    },
  },
});

完整示例

javascript
let sdk;

async function initMap() {
  sdk = new navMap.MapSDK({
    container: "map",
    center: [116.39, 39.9],
    zoom: 10,
  });

  sdk.on("loadComplete", () => {
    setupCustomStyles();
  });
}

function setupCustomStyles() {
  // 1. 基础样式覆盖
  sdk.setStyle({
    "airport-label": {
      paint: {
        "text-color": "#ff0000",
        "text-size": 14,
      },
    },
  });

  // 2. 动态样式切换
  sdk.on("zoom", () => {
    const zoom = sdk.getZoom();
    
    sdk.setStyle({
      "airport-label": {
        paint: {
          "text-size": zoom < 12 ? 12 : 16,
        },
      },
    });
  });

  // 3. 主题切换按钮
  document.getElementById("dark-theme-btn").addEventListener("click", () => {
    loadTheme("/styles/dark.json");
  });

  document.getElementById("light-theme-btn").addEventListener("click", () => {
    loadTheme("/styles/light.json");
  });
}

async function loadTheme(themePath) {
  const theme = await fetch(themePath).then(r => r.json());
  sdk.setStyle(theme);
}

initMap();

注意事项

  1. 图层ID:确保使用的图层ID正确,错误的ID会被忽略
  2. 样式时机:在地图 loadComplete 后再应用样式
  3. 性能考虑:频繁的样式更新可能影响性能,考虑批量更新
  4. 样式优先级:后应用的样式会覆盖先应用的样式
  5. 数组图层:对于 ammcontrolled 等数组类型图层,必须使用数组格式