You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

重叠多边形引发填充颜色透明度不一致的技术问询

解决多边形重叠时透明度叠加导致颜色不均的问题

问题描述

我们通过生成含360个顶点的多边形模拟红色、10%透明度的圆形,但多个此类多边形重叠时,重叠区域因颜色叠加效应,透明度高于预期的10%,出现部分区域颜色过深、透明度不一致的情况。需要实现无论多边形是否重叠,所有圆形填充色的透明度始终稳定在10%。

当前效果

当前效果
当前状态

期望效果

期望效果

核心原因

原实现中每个多边形单独设置了带10%透明度的填充色,绘制时重叠区域会多次叠加透明度(例如两个重叠区域的透明度为 1 - (1-0.1)*(1-0.1) = 0.19),导致颜色变深。

解决方案

提供两种可行方案,根据业务场景选择:

方案1:合并重叠多边形(推荐)

将所有需要绘制的重叠多边形合并为一个单一多边形(计算几何并集),这样整个区域仅绘制一次,自然不会出现透明度叠加问题。可以使用turf库的多边形合并功能:

  1. 引入turf依赖(在pubspec.yaml中添加):
dependencies:
  turf: ^0.1.0
  1. 修改代码合并多边形:
import 'package:turf/turf.dart';

// 收集所有需要绘制的多边形坐标列表
List<List<Position>> allPolygonCoordinates = [];
for (var resultItem in result) {
  List<Position> points = listgeneratePolygonPoints(
    resultItem.lat,
    resultItem.lng,
    resultItem.radius,
  ).map((p) => Position(p.lng, p.lat)).toList();
  allPolygonCoordinates.add(points);
}

// 合并所有多边形为一个
Feature<Polygon>? mergedPolygon;
for (var coords in allPolygonCoordinates) {
  var polygon = Polygon(coordinates: [coords]);
  if (mergedPolygon == null) {
    mergedPolygon = Feature(geometry: polygon);
  } else {
    mergedPolygon = union(mergedPolygon, Feature(geometry: polygon));
  }
}

// 创建单个合并后的多边形注解
if (mergedPolygon != null) {
  nonSmokingAreas["merged"] = await nonSmokingAreaManager?.create(
    PolygonAnnotationOptions(
      fillColor: Colors.red.withOpacity(0.1).value,
      fillSortKey: 1,
      geometry: mergedPolygon.geometry!.toJson(),
    ),
  );
}

方案2:使用图层级透明度与混合模式

不通过单个注解设置带透明度的颜色,而是将所有多边形放在同一个图层中,设置图层级的透明度,并使用src混合模式避免叠加:

// 创建GeoJSON数据源
await mapboxMap.style.addSource(
  GeoJsonSource(
    id: "non-smoking-source",
    data: {
      "type": "FeatureCollection",
      "features": result.map((item) {
        List<Position> points = listgeneratePolygonPoints(
          item.lat,
          item.lng,
          item.radius,
        ).map((p) => Position(p.lng, p.lat)).toList();
        return {
          "type": "Feature",
          "geometry": {
            "type": "Polygon",
            "coordinates": [points.map((p) => [p.lng, p.lat]).toList()]
          }
        };
      }).toList(),
    },
  ),
);

// 创建填充图层,设置全局透明度与混合模式
await mapboxMap.style.addLayer(
  FillLayer(
    id: "non-smoking-layer",
    source: "non-smoking-source",
    fillColor: Colors.red.value, // 不设置颜色内的透明度
    fillOpacity: 0.1, // 图层级别的统一透明度
    fillBlendMode: BlendMode.src, // 确保重叠区域不叠加
  ),
);

方案3:调整单个注解的透明度设置

如果必须保留单个注解,可将颜色透明度与fillOpacity分离,避免叠加:

nonSmokingAreas[result[i].id] =
    (await nonSmokingAreaManager?.create(
  PolygonAnnotationOptions(
      fillColor: Colors.red.value, // 纯红色,不包含透明度
      fillOpacity: 0.1, // 单独设置注解的透明度
      fillSortKey: 1,
      fillBlendMode: BlendMode.src, // 若API支持则添加
      geometry: Polygon(
        coordinates: [
          List.generate(
            points.length,
            (index) => Position(
              points[index].lng,
              points[index].lat,
            ),
          ),
        ],
      ).toJson()),
))!;

原生成多边形代码(无需修改)

import 'dart:math';

/// 生成围绕指定地理坐标、指定半径的正多边形顶点列表
List<({double lat, double lng})> listgeneratePolygonPoints(
    double lat, double lng, double radius) {
  List<({double lat, double lng})> points = [];

  for (int i = 0; i < 360; i++) {
    double angle = (2 * pi * i) / 360;
    double dx = radius * cos(angle);
    double dy = radius * sin(angle);
    final point = _translateCoordinate((lat: lat, lng: lng), dx, dy);
    points.add(point);
  }

  return points;
}

/// 根据原点位置、经纬位移量转换地理坐标
({double lat, double lng}) _translateCoordinate(
    ({double lat, double lng}) coordinate, double dx, double dy) {
  const double earthRadius = 6378137; // 地球半径(米)
  double lat = coordinate.lat + (dy / earthRadius) * (180 / pi);
  double lon = coordinate.lng +
      (dx / earthRadius) * (180 / pi) / cos(coordinate.lat * pi / 180);
  return (lat: lat, lng: lon);
}

内容的提问来源于stack exchange,提问作者Kyrill

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 15:45:25