有没有办法在绘图前获得标签的宽度和高度?

时间:2016-11-16 14:57:45

标签: javascript leaflet leaflet-label

我正在使用带有传单标签的传单。有时标记会重叠,这对用户体验很不利,因此我实现了以下Spiderfier功能:

/*Geometry*/
//Abstract Shape function capable to check intersection
function Shape(params) {
    Initializable.call(this, params);
    this.initialize("Type", "Default");
    //Let's know whether intersection is symmetric
    this.initialize("Symmetric", true);
    this.initialize("Intersects", function (shape) {
        return false;
    });
}
//These rectangles have two horizontal and two vertical sides
function HorizontalVerticalRectangle(params) {
    params.Type = "HorizontalVerticalRectangle";
    var self = this;
    if (typeof params.Intersects !== "function") {
        //Default Intersects function
        params.Intersects = function (shape) {
            //If the two shapes have the same types and self is not to the right, left, bottom or top compared to shape then they intersect each-other
            if (shape.Type === self.Type) {
                return !((self.TopLeft.x > shape.BottomRight.x) ||
                         (self.BottomRight.x < shape.TopLeft.x) ||
                         (self.TopLeft.y > shape.BottomRight.y) ||
                         (self.BottomRight.y < shape.TopLeft.y));
                //In case of top half circles, we need to make sure that the horizontal square collides the circle and in the top half
            } else if (shape.Type === "TopHalfCircle") {
                return (self.TopLeft.y <= shape.Center.y) && HorizontalVerticalRectangle.prototype.CollidesCircle(self, shape.Center.x, shape.Center.y, shape.Diameter / 2);
            }
            //Not implemented
            return false;
        };
    }
    Shape.call(this, params);
    this.initialize("TopLeft", { x: 0, y: 0 });
    this.initialize("BottomRight", { x: 0, y: 0 });
    //Make sure the x and y coordinates are kept as floats
    this.TopLeft.x = parseFloat(this.TopLeft.x);
    this.TopLeft.y = parseFloat(this.TopLeft.y);
    this.BottomRight.x = parseFloat(this.BottomRight.x);
    this.BottomRight.y = parseFloat(this.BottomRight.y);
    //Coordinate setters
    this.setTopLeftX = function (x) {
        self.TopLeft.x = parseFloat(x);
    };
    this.setTopLeftY = function (y) {
        self.TopLeft.y = parseFloat(y);
    };
    this.setBottomRightX = function (x) {
        self.BottomRight.x = parseFloat(x);
    };
    this.setBottomRightY = function (y) {
        self.BottomRight.y = parseFloat(y);
    };
}
HorizontalVerticalRectangle.prototype.CollidesCircle = function (horizontalRectangle, centerX, centerY, radius) {
    var deltaX = centerX - Math.max(horizontalRectangle.TopLeft.x, Math.min(centerX, horizontalRectangle.BottomRight.x));
    var deltaY = centerY - Math.max(horizontalRectangle.TopLeft.y, Math.min(centerY, horizontalRectangle.BottomRight.y));
    return Math.pow(deltaX, 2) + Math.pow(deltaY, 2) <= Math.pow(radius, 2);
};
//These are circles where the center has the maximum y and the shape is upwards on screens
function TopHalfCircle(params) {
    params.Type = "TopHalfCircle";
    var self = this;
    if (typeof params.Intersects !== "function") {
        //Default Intersects function
        params.Intersects = function (shape) {
            //If the two shapes have identical type, none of them is above (below in coordinates) the other by more than the other's radius and the full circles intersect, 
            //then the half circles intersect each-other
            if (shape.Type === self.Type) {
                return ((self.Center.y - shape.Center.y) < (self.Diameter / 2)) &&
                       ((shape.Center.y - self.Center.y) < (shape.Diameter / 2)) &&
                       (Math.pow(self.Center.x - shape.Center.x, 2) + Math.pow(self.Center.y - shape.Center.y, 2) < Math.pow(((self.Diameter + shape.Diameter) / 2), 2));
                //In case of top horizontal vertical rectangle, we need to make sure that the horizontal square collides the circle and in the top half
            } else if (shape.Type === "HorizontalVerticalRectangle") {
                return (shape.TopLeft.y <= self.Center.y) && HorizontalVerticalRectangle.prototype.CollidesCircle(shape, self.Center.x, self.Center.y, self.Diameter / 2);
            }
            //Not Implemented
            return false;
        };
    }
    Shape.call(this, params);
    this.initialize("Center", { x: 0, y: 0 });
    this.initialize("Diameter", 0);
    //Make sure the coordinates and diameter are kept as floats
    this.Center.x = parseFloat(this.Center.x);
    this.Center.y = parseFloat(this.Center.y);
    this.Diameter = parseFloat(this.Diameter);
    //Setters
    this.setCenterX = function (x) {
        self.Center.x = parseFloat(x);
    };
    this.setCenterY = function (y) {
        self.Center.y = parseFloat(y);
    };
    this.setDiameter = function (d) {
        self.Diameter = parseFloat(d);
    };
}
//Placement strategies for markers, but they can be used for different purposes as well
var PlacementStrategies = {
    //This function finds groups of shapes seeing which shape intersects which other shape
    Group: function (shapes, comparator) {
        if (typeof comparator !== "function") {
            comparator = function () {
                return true;
            };
        }
        //This variable is empty at start, but at the end will hold the shape groups
        var groups = [];
        //Traverse the shapes to build the groups
        for (var shapeIndex in shapes) {
            //This variable will hold false if the shape does not fit into any existing group and the group index otherwise
            var foundGroup = false;
            //Traverse the groups to find whether a group where the shape fits in already exists
            for (var groupIndex = 0; groupIndex < groups.length; groupIndex++) {
                //Traverse the shapes of the current group to see whether any of them intersects the shape
                for (var innerShapeIndex = 0; (groupIndex < groups.length) && (innerShapeIndex < groups[groupIndex].length) ; innerShapeIndex++) {
                    //If shape intersects with the current group's current shape, then set foundGroup and exit two for cycles
                    if (Shape.prototype.intersects(shapes[shapeIndex], shapes[groups[groupIndex][innerShapeIndex]])) {
                        foundGroup = groupIndex;
                        innerShapeIndex = groups[groupIndex].length;
                        groupIndex = groups.length;
                    }
                }
            }
            //If the shape does not fit into any groups, then we create its own group
            if (foundGroup === false) {
                groups.push([shapeIndex]);
                //Otherwise we search for the location where the shape fits best
            } else {
                //Desired location. If it results in false, then the shape will be pushed to the end, otherwise it will be inserted at insertIndex
                var insertIndex = false;
                //Traverse the shapes of the found group to find the desired location to insert
                for (var innerShapeIndex = 0; innerShapeIndex < groups[foundGroup].length; innerShapeIndex++) {
                    //If the shape to be inserted is "smaller" than the found group's current shape, then store the index and quit the cycle
                    if (!comparator(shapes[groups[foundGroup][innerShapeIndex]], shapes[shapeIndex])) {
                        insertIndex = innerShapeIndex;
                        innerShapeIndex = groups[foundGroup].length;
                    }
                }
                //Insert the shape into the desired location or to the end if there was no desired middle location
                if (insertIndex === false) {
                    groups[foundGroup].push(shapeIndex);
                } else {
                    groups[foundGroup].splice(insertIndex, 0, shapeIndex);
                }
            }
        }
        return groups;
    },
    //This function merges shape groups if they intersect each-other
    MergeGroup: function (shapes, groups, merged, comparator) {
        if (typeof comparator !== "function") {
            comparator = function () {
                return true;
            };
        }
        //This time we merge the contents of the groups into the first index
        mergeIssued = true;
        while (mergeIssued) {
            //There was no merge issued yet
            mergeIssued = false;
            //Traverse the main groups
            for (var mergeIndex in merged) {
                //Traverse the groups to merge with
                for (var innerMergeIndex in merged[mergeIndex]) {
                    //If the group to merge with is empty, then it was already parsed
                    if ((merged[merged[mergeIndex][innerMergeIndex]]) && (merged[merged[mergeIndex][innerMergeIndex]].length > 0)) {
                        //Traverse the inner groups of the inner group
                        for (var toMove in merged[merged[mergeIndex][innerMergeIndex]]) {
                            //Move them if they are not yet present in the main merge group
                            if (merged[mergeIndex].indexOf(merged[merged[mergeIndex][innerMergeIndex]][toMove]) === -1) {
                                merged[mergeIndex].push(merged[merged[mergeIndex][innerMergeIndex]][toMove]);
                                mergeIssued = true;
                            }
                            //Remove the content of the inner group to avoid duplicates
                            merged[merged[mergeIndex][innerMergeIndex]] = [];
                        }
                    }
                }
            }
        }

        //Traverse the merge groups to move the shapes
        for (var mergeIndex in merged) {
            //Traverse the inner groups where we read the shapes from
            for (var innerMergeIndex in merged[mergeIndex]) {
                //Traverse the shapes of the inner group
                for (var shapeIndex in groups[merged[mergeIndex][innerMergeIndex]]) {
                    //If the shape is not yet present in the target group, we move it
                    if (groups[mergeIndex].indexOf(groups[merged[mergeIndex][innerMergeIndex]][shapeIndex]) === -1) {
                        //A variable which will hold the index of insertion or false, if the element should be the lasts
                        var insertLocation = false;
                        //Traverse the shapes of the target group to find the correct location
                        for (var targetIndex = 0; (insertLocation === false) && (targetIndex < groups[mergeIndex].length) ; targetIndex++) {
                            //If the shape located at the current index is not "smaller" than the shape to be inserted, then we found the target location
                            if (!comparator(shapes[groups[mergeIndex][targetIndex]], shapes[groups[merged[mergeIndex][innerMergeIndex]][shapeIndex]])) {
                                insertLocation = targetIndex;
                            }
                        }
                        //If there was no "bigger" element, then push at the end of the array
                        if (insertLocation === false) {
                            groups[mergeIndex].push(groups[merged[mergeIndex][innerMergeIndex]][shapeIndex]);
                            //Otherwise insert it to the correct location
                        } else {
                            groups[mergeIndex].splice(insertLocation, 0, groups[merged[mergeIndex][innerMergeIndex]][shapeIndex]);
                        }
                    }
                }
                //Clear the group where we moved the shapes from
                groups[merged[mergeIndex][innerMergeIndex]] = [];
            }
        }

        //We copy the non-empty groups into another container
        var finalGroups = [];
        for (var groupIndex in groups) {
            if (groups[groupIndex].length > 0) {
                finalGroups.push(groups[groupIndex]);
            }
        }
        //And return it
        return finalGroups;
    },
    //This strategy moves rectangles inside a group into a semi circle upwards on the screen
    SemiCircleHorizontalRectangles: function (shapes, groups) {
        //If groups is falsy, then this is the first try
        if (!groups) {
            //Which means that we need to create it by calling PlacementStrategies.Group with the comparator desired here
            groups = PlacementStrategies.Group(shapes, function (shape1, shape2) {
                //The shapes to the left are "smaller" to minimize line collisions
                return shape1.TopLeft.x < shape2.TopLeft.x;
            });
        }
        //This will hold top circles of the groups of shapes
        var groupTopCircles = [];
        //Traverse the raw groups
        for (var groupIndex in groups) {
            //We need to know the center of the circle, which will be the middle point of the horizontal coordinates and the lowest point in the circle
            var maxY = false;
            var minX = false;
            var maxX = false;
            //We need to know the half periphery to calculate the diameter
            var halfPeriphery = 0;
            //Traverse the shapes in the group
            for (var innerShapeIndex in groups[groupIndex]) {
                //Calculate the values where we calculate the center coordinates from
                if ((minX === false) || (minX > shapes[groups[groupIndex][innerShapeIndex]].TopLeft.x)) {
                    minX = shapes[groups[groupIndex][innerShapeIndex]].TopLeft.x;
                }
                if ((maxX === false) || (maxX < shapes[groups[groupIndex][innerShapeIndex]].BottomRight.x)) {
                    maxX = shapes[groups[groupIndex][innerShapeIndex]].BottomRight.x;
                }
                if ((maxY === false) || (maxY < shapes[groups[groupIndex][innerShapeIndex]].BottomRight.y)) {
                    maxY = shapes[groups[groupIndex][innerShapeIndex]].BottomRight.y;
                }
                //Add the length of the diagonal of the shape to halfPeriphery
                halfPeriphery += Math.sqrt(Math.pow(shapes[groups[groupIndex][innerShapeIndex]].BottomRight.x - shapes[groups[groupIndex][innerShapeIndex]].TopLeft.x, 2) + Math.pow(shapes[groups[groupIndex][innerShapeIndex]].BottomRight.y - shapes[groups[groupIndex][innerShapeIndex]].TopLeft.y, 2));
            }
            //Add the half circle to the container
            groupTopCircles[groupIndex] = new TopHalfCircle({ Center: { x: (minX + maxX) / 2, y: maxY }, Diameter: 2 * halfPeriphery / Math.PI });
        }
        //Container for groups to be merged
        var merged;
        //Traverse all the shapes
        for (var halfCircleIndex = 0; halfCircleIndex < groupTopCircles.length; halfCircleIndex++) {
            var s1 = (groups[halfCircleIndex].length === 1) ? shapes[groups[halfCircleIndex][0]] : groupTopCircles[halfCircleIndex];
        //Traverse the "later" shapes
            for (var secondHalfCircleIndex = halfCircleIndex + 1; secondHalfCircleIndex < groupTopCircles.length; secondHalfCircleIndex++) {
                var s2 = (groups[secondHalfCircleIndex].length === 1) ? shapes[groups[secondHalfCircleIndex][0]] : groupTopCircles[secondHalfCircleIndex];
                //If the two half circles intersect each-other, then merge them
                if (Shape.prototype.intersects(s1, s2)) {
                    if (!merged) {
                        merged = {};
                    }
                    if (!merged[halfCircleIndex]) {
                        merged[halfCircleIndex] = [];
                    }
                    //We always merge into the first group
                    merged[halfCircleIndex].push(secondHalfCircleIndex);
                }
            }
        }
        //If there was a merge then we do the effective merging and repeat this strategy for the resulting half-circles
        if (merged) {
            return PlacementStrategies.SemiCircleHorizontalRectangles(shapes, PlacementStrategies.MergeGroup(shapes, groups, merged, function (shape1, shape2) {
                //We will order horizontal-verticle rectangles here, we might refactor this function to get a comparator instead later
                return shape1.TopLeft.x < shape2.TopLeft.x;
            }));
        }
        //Angle iterator for the half circle
        var angle;
        //The amount of step with the angle iterator
        var angleStep;
        //Traverse the groups to change the coordinates
        for (var groupIndex in groups) {
            //If the group has a single element, then we jump over it
            if (groups[groupIndex].length > 1) {
                //Initialize the angle iterator and calculate its step size
                angle = Math.PI;
                angleStep = angle / (groups[groupIndex].length - 1);
                //Traverse the shapes
                for (var shapeIndex in groups[groupIndex]) {
                    //The translation is calculated based on circle coordinates
                    var translation = {
                        x: groupTopCircles[groupIndex].Center.x + (groupTopCircles[groupIndex].Diameter * Math.cos(angle) / 2),
                        y: groupTopCircles[groupIndex].Center.y + (groupTopCircles[groupIndex].Diameter * Math.sin(angle) / 2)
                    };
                    //The middle of the rectangles will place at the desired point and we need the middle coordinates for that
                    var halfDiffX = (shapes[groups[groupIndex][shapeIndex]].BottomRight.x - shapes[groups[groupIndex][shapeIndex]].TopLeft.x) / 2;
                    var halfDiffY = (shapes[groups[groupIndex][shapeIndex]].BottomRight.y - shapes[groups[groupIndex][shapeIndex]].TopLeft.y) / 2;
                    //Calculate the new bounds of the rectangle and step the iterator
                    shapes[groups[groupIndex][shapeIndex]].setTopLeftX(translation.x - halfDiffX);
                    shapes[groups[groupIndex][shapeIndex]].setTopLeftY(translation.y - halfDiffY);
                    shapes[groups[groupIndex][shapeIndex]].setBottomRightX(translation.x + halfDiffX);
                    shapes[groups[groupIndex][shapeIndex]].setBottomRightY(translation.y + halfDiffY);
                    angle += angleStep;
                }
            }
        }
        return shapes;
    }
};
//General intersects function for shapes, which gets two shapes and checks whether they intersect each-other
Shape.prototype.intersects = function (shape1, shape2) {
    //If the first shape is symmetric and the types of shapes match, it is enough to check a single direction of intersection
    //Otherwise we need to check both directions
    return ((shape1.Symmetric) && (shape1.Type === shape2.Type)) ? (shape1.Intersects(shape2)) : (shape1.Intersects(shape2) || shape2.Intersects(shape1));
};
/*Geometry*/
/*Spiderfier*/
function Spiderfier(params) {
    Initializable.call(this, params);
    var self = this;
    var isSpiderfied = false;
    this.defaultFunction = function () { };
    //Custom Spiderfy Events
    this.initialize("OnSpiderfy", this.defaultFunction, true);
    this.initialize("OnUnspiderfy", this.defaultFunction, true);
    this.initialize("rows", [], true);
    this.initialize("cm", function () {
        return cachedMarkers;
    }, true);
    this.initialize("options", {});
    this.SpiderLines = {};
    this.isCurrentlySpiderfied = function () {
        return isSpiderfied;
    };
    this.refreshRows = function (r, stopRefresh) {
        rows = r;
        if (isSpiderfied && (!stopRefresh)) {
            self.spiderfy();
        }
    };
    this.spiderfy = function (r) {
        if (r) {
            self.refreshRows(r, true);
        }
        params.OnSpiderfy(rows, self);
        isSpiderfied = true;
    };
    this.unspiderfy = function (r) {
        if (r) {
            self.refreshRows(r, true);
        }
        params.OnUnspiderfy(rows, self);
        isSpiderfied = false;
    };
    //Handles marker draw and spiderfying
    this.drawAndSpiderfy = function (r, o) {
        //First handle the spiderfy thing
        if (o) {
            self.options = o;
        }
        if (self.isCurrentlySpiderfied()) {
            self.spiderfy(r, params.cm());
            drawSpiderMarkers(r, params.cm(), self);
        } else {
            self.unspiderfy(r, params.cm());
        }
        //And then draw the markers
        drawMarkers(rows, options);
    };
}
//Gets the rectangles of the markers
function markersToRectangles(rows) {
    var shapes = [];
    var lowPoint;
    for (var rowIndex in rows) {
        //Convert the geographical point of the marker into graphical point
        lowPoint = map.latLngToLayerPoint(L.latLng(rows[rowIndex].RealLat, rows[rowIndex].RealLon));
        shapes.push(new HorizontalVerticalRectangle({
            TopLeft: { x: lowPoint.x - 18, y: lowPoint.y - 44 },
            BottomRight: { x: lowPoint.x + 18 + 0, y: lowPoint.y }
        }));
    }
    return shapes;
}
//Spiderfies rectangles with half circle strategy
function RectangleHalfCircleSpiderfy(rows, spdfr) {
    //Initialize real latitude and longitude if not already done so
    for (var rowIndex in rows) {
        if (!rows[rowIndex].RealLat) {
            rows[rowIndex].RealLat = rows[rowIndex].Lat;
            rows[rowIndex].RealLon = rows[rowIndex].Lon;
        }
    }
    //Gather the desired rectangles
    var rectangles = PlacementStrategies.SemiCircleHorizontalRectangles(markersToRectangles(rows));
    //Store the geographic coordinates
    for (var rowIndex in rectangles) {
        //Convert graphical coordinates into geographic coordinates
        var location = map.layerPointToLatLng(L.point(rectangles[rowIndex].TopLeft.x + 14, rectangles[rowIndex].BottomRight.y));
        rows[rowIndex].Lat = location.lat;
        rows[rowIndex].Lon = location.lng;
    }
}
function normalUnspiderfy(rows, spiderfier) {
    for (var rowIndex in rows) {
        if (rows[rowIndex].RealLat !== undefined) {
            rows[rowIndex].Lat = rows[rowIndex].RealLat;
            rows[rowIndex].Lon = rows[rowIndex].RealLon;
            delete rows[rowIndex].RealLat;
            delete rows[rowIndex].RealLon;
        }
    }
    for (var lineIndex in spiderfier.SpiderLines) {
        map.removeLayer(spiderfier.SpiderLines[lineIndex].polyLine);
    }
    spiderfier.SpiderLines = {};
}
//Draws spider markers
function drawSpiderMarkers(rows, cachedMarkers, spiderfier) {
    //For each row...
    for (var i = 0; i < rows.length; i++) {
        //If real location exists and differs from the display location and there is either no spider line yet or points to a different location than the expected one
        if (rows[i].RealLat && rows[i].RealLon &&
            ((rows[i].Lat != rows[i].RealLat) || (rows[i].Lon != rows[i].RealLon)) &&
            ((!spiderfier.SpiderLines[i]) || (spiderfier.SpiderLines[i].location.Lat != rows[i].Lat) || (spiderfier.SpiderLines[i].location.Lon != rows[i].Lon))
           ) {
            //Then check whether the spider line exists and remove it if so
            if (spiderfier.SpiderLines[i]) {
                map.removeLayer(spiderfier.SpiderLines[i].polyLine);
            }
            //And generate a new spider line
            spiderfier.SpiderLines[i] = { location: new L.LatLng(rows[i].Lat, rows[i].Lon), realLocation: new L.LatLng(rows[i].RealLat, rows[i].RealLon) };
            spiderfier.SpiderLines[i].polyLine = L.polyline([spiderfier.SpiderLines[i].location, spiderfier.SpiderLines[i].realLocation]);
            spiderfier.SpiderLines[i].polyLine.options.weight = 2;
            spiderfier.SpiderLines[i].polyLine.options.color = "#5f0df1";
            spiderfier.SpiderLines[i].polyLine.addTo(map);
        }
    }
}
var spiderfier;
/*Spiderfier*/

function getStrategyName(code) {
    switch (code) {
        case 2: return "Grouped";
        case 3: return "RectangleHalfCircleSpiderfy";
        default: return "Unspecified";
    }
}

function drawStrategicMarkers(rows, drawOpt) {
    if (drawOpt.strategy < 3) {
        if (drawOpt.strategy === 2) {
            drawOpt.grouped = true;
        }
        return drawMarkers(rows, drawOpt);
    } else {
        if (!spiderfier) {
            window["spiderfier"] = new Spiderfier({
                OnSpiderfy: window[getStrategyName(drawOpt.strategy)],
                OnUnspiderfy: normalUnspiderfy,
            });
        }
        spiderfier.drawAndSpiderfy(rows);
    }
}

说明:这将计算标记矩形的图形坐标,并找出哪些矩形属于一个组。一个组将是一个上半圈,其中标记显示在外围,当我们有这样的半圆时,它们相互检查,所以如果它们相互交叉,那么它们将合并为一个新组。如果一个组包含单个标记,则考虑其矩形而不是其上半部分。最后,标记被转换为它们在其组(上半圆周边)上的所需位置。

这很有效,但问题是这只考虑了标记的矩形而根本没有考虑标签尺寸(标签显示在相应标记的右侧,两者一起应该是被视为单个矩形)。原因很简单:我可以收集标签尺寸,但只能在绘制完所有内容之后。我的问题如下:如果我知道标签将包含什么,是否有一个可靠的公式,我可以收集标签容器的边界和限制,以便我可以检查它不与其他标记或标签重叠好?

生成标签后,这种非常黑客的方式是我如何收集有关标签大小的信息:

function getLabelSize(index) {
    var labelContext = $(".leaflet-map-pane .leaflet-label:eq(" + index + ")");
    return {width: labelContext.width(), height: labelContext.height()};
}

要绘制标记,测量标记,然后重新绘制标记只​​是为了获得标签大小这种方式是如此hacky我宁愿允许标签交叉标记或其他标签,这是一个悲伤的决定。因此我想知道:有没有办法获得一个尚未根据其未来内容绘制的标签的宽度和高度?

内容如下:

<div class="leaflet-label  leaflet-zoom-animated leaflet-label-right" style="z-index: 540; transform: translate3d(912px, 500px, 0px); opacity: 1;">
    <p class="orange">34534</p>
    <p>3343453</p>
</div>

当然,这个div有填充和边框,但是如果我能够以某种方式读取内部宽度和高度,我将能够添加所需的值。

1 个答案:

答案 0 :(得分:4)

  

如果我知道标签会包含什么,是否有可靠的公式可以用来收集标签容器的边界和限制?

没有

在HTML中,在将该块元素添加到DOM之前,您无法知道块元素的computed dimensions。这是由于各种原因造成的;主要是这样的事实:可能是许多不同的(非显式的)CSS选择器,可能在添加时应用于该块元素。加上用户代理样式表,GPU字体渲染,DPI字体大小等等。

我在处理Leaflet.LayerGroup.Collision时研究过这个问题。

  

这种非常黑客的方式是我如何收集有关标签大小的信息

请不要。在将元素添加到DOM 之后使用window.getComputedStyle

您可以在同一个渲染帧中自由地从DOM中删除该元素(或删除Leaflet图层,因为它将具有相同的效果)。您可以向DOM添加元素,获取其计算的维度,将该元素移除到DOM,并快速执行以至于浏览器在其间点击帧渲染(除此之外,因为执行此操作时阻止浏览器UI线程。

这是Leaflet.LayerGroup.Collision的工作方式:将所有内容添加到DOM(将所有传单图层添加到地图中),获取所有内容的计算样式,将边界框添加到rbush结构,计算碰撞,从DOM中删除元素(地图中的图层)在一个框架内