2016-11-16 148 views
1

我正在使用傳單和小冊子標籤。有時標記重疊,這是不好的UX,因此,我已實現了以下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有填充和邊界,但我將能夠添加所需的值,如果我能讀的內部寬度和高度莫名其妙。

回答

4

如果我知道標籤將包含什麼,是否有一個可靠的公式,我可以收集標籤容器的邊界和限制?

在HTML中,你可以不知道塊元素的computed dimensions之前塊元素被添加到DOM。這是由於各種原因造成的;主要是可能是很多不同的(非顯式的)CSS選擇器,可能在添加時會應用於該塊元素。 Plus用戶代理樣式表,GPU字體渲染,DPI字體大小等等等等等等。

我在Leaflet.LayerGroup.Collision上工作時研究了這個。

這種極其哈克的方式是我怎麼能收集有關標籤的尺寸

請不要信息。元素添加到DOM之後,使用window.getComputedStyle

您可以在同一個渲染框架內從DOM中移除該元素(或移除Leaflet層,因爲它具有相同的效果)。您可以將元素添加到DOM,獲得其計算的尺寸,刪除元素的DOM,並做到如此之快,瀏覽器不會打一幀渲染之間(除其他事項外,因爲瀏覽器的UI線程在這樣做時被阻止)。

這就是Leaflet.LayerGroup.Collision的工作方式:將所有內容添加到DOM(將所有Leaflet圖層添加到地圖中),獲取所有內容的計算樣式,將邊界框添加到rbush結構中,計算衝突,刪除來自DOM的元素(來自地圖的圖層)在一幀內。 「

+0

」在元素添加到DOM後使用window.getComputedStyle。「沒有選擇。正如我在問題中已經描述的那樣,我需要在繪製標籤之前獲取標籤的大小。你建議我畫他們,然後閱讀他們的尺寸。但我需要他們的尺寸來知道在哪裏繪製它們,所以如果它們已經繪製完成,那麼我不再對它們的尺寸感興趣。 –

+0

「您可以自由從DOM中移除該元素」不是一個選項。如果它已經在那裏了,那麼我就沒有興趣去確定它的位置,我有充分的理由不這樣做,爲了保持簡單,我不會深究。長話短說:我想知道在繪製小冊子標籤之前是否有任何方法獲得小冊子的尺寸。你的意見是,沒有辦法做到這一點。如果沒有更好的選項並重復使用這些尺寸,我可以測量標籤並查看它們有多寬,如果一個parapgraph包含n個字母,實際上有一種方法,但涉及勞動密集型研究。 –

+0

「這就是Leaflet.LayerGroup.Collision的工作原理」我沒有從DOM中讀取數據就進行了數學計算,當我知道在哪裏放置東西時,我繪製了標記。如果我們忽略與標籤有關的問題,這完美地工作。我可以預先測量不同情況下,不同瀏覽器和不同風格的標籤,但這需要一段時間。有一個解決方案,但它是痛苦的。我明白你的意見是沒有別的辦法。你可能是對的,但我們還要等待其他答案,看看是否還有一些額外的信息。 –

相關問題