最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Is there a polyfill for getIntersectionList, getEnclosureList, checkIntersection, checkEnclosure for Firefox SVG? -

programmeradmin7浏览0评论

SVG 1.1 Support in Firefox:

  • SVGSVGElement:
  • Unimplemented bindings: getIntersectionList, getEnclosureList, checkIntersection, checkEnclosure, deselectAll

SVG 1.1 spec: 5.11.2 Interface SVGSVGElement

Since Firefox does not support getIntersectionList, getEnclosureList, checkIntersection, checkEnclosure methods, is there a polyfill? Or how to write a polyfill for the 4 methods in JavaScript?

SVG 1.1 Support in Firefox:

  • SVGSVGElement:
  • Unimplemented bindings: getIntersectionList, getEnclosureList, checkIntersection, checkEnclosure, deselectAll

SVG 1.1 spec: 5.11.2 Interface SVGSVGElement

Since Firefox does not support getIntersectionList, getEnclosureList, checkIntersection, checkEnclosure methods, is there a polyfill? Or how to write a polyfill for the 4 methods in JavaScript?

Share Improve this question edited Jun 11, 2022 at 0:51 Hasan Haghniya 2,5554 gold badges22 silver badges32 bronze badges asked Jan 20, 2016 at 7:14 cuixipingcuixiping 25.5k9 gold badges87 silver badges94 bronze badges 1
  • 3 As an alternative until Firefox implements the said methods, have you tried kld-intersections ? – Alex Pappas Commented Oct 24, 2019 at 2:53
Add a ment  | 

2 Answers 2

Reset to default 1

I dont think there is any official polyfill for those functions in Firefox right now unfortunately.

I gave this a shot to see if there was a way to easily polyfill those functions with available API.

I, intentionaly, assigned a second function so we can pare the original supported function (when using a browser that natively support it like Chrome) with the polyfill.

So, here we are for getIntersectionList:

const getIntersectionListPolyfill = function(rect, referenceElement) {
  var intersectionList = [];
  var root = this.ownerSVGElement || this;

  // Get all elements that intersect with rect
  var elements = root.querySelectorAll('*');
  for (var i = 0; i < elements.length; i++) {
    var element = elements[i];
    if (element !== this && element instanceof SVGGraphicsElement) {
      var bbox = element.getBBox();
      if (rect.width && rect.height && bbox.width && bbox.height) {
        if (bbox.x + bbox.width > rect.x &&
            bbox.y + bbox.height > rect.y &&
            bbox.x < rect.x + rect.width &&
            bbox.y < rect.y + rect.height) {
          intersectionList.push(element);
        }
      }
    }
  }

  // Sort elements in document order
  intersectionList.sort(function(a, b) {
    return (a.pareDocumentPosition(b) & 2) ? 1 : -1;
  });

  // Filter elements by referenceElement
  if (referenceElement) {
    intersectionList = intersectionList.filter(function(element) {
      return element === referenceElement || element.contains(referenceElement);
    });
  }

  return intersectionList;
}

if (!SVGElement.prototype.getIntersectionList) {
  SVGElement.prototype.getIntersectionList = getIntersectionListPolyfill;
}

// The code below is for the snippet only
SVGElement.prototype.getIntersectionList2 = getIntersectionListPolyfill;

const mySVG = document.getElementById('mySVG');
const myRect = mySVG.createSVGRect();

myRect.width = myRect.height = 1;
myRect.x = myRect.y = 20;


console.log('Original', mySVG.getIntersectionList(myRect, null).length);
console.log('Polyfill', mySVG.getIntersectionList2(myRect, null).length);
svg {
  display: block;
  border: 1px solid #000;
  margin: 20px 0;
  visibility: visible;
}

rect, circle { 
  fill: rgba(255, 0, 0, 0.2);
  visibility: visiblePainted;
}
<svg id="mySVG" width="500" height="400">
  <rect x="10" y="10" width="200" height="100"></rect>
  <rect x="20" y="20" width="200" height="100"></rect>
  <circle cx="70" cy="70" r="50"></circle>
</svg>

This polyfill extends the SVGElement prototype with a getIntersectionList function that imit the native implementation. It uses querySelectorAll to get every elements of the SVG and checks if they intersect with the given rect. It then sorts the elements in document order and filters them by the reference element, if provided.

getEnclosureList is pretty similar:

const getEnclosureListPolyfill = function(rect, referenceElement) {
  var enclosureList = [];
  var root = this.ownerSVGElement || this;

  // Get all elements that are pletely enclosed by rect
  var elements = root.querySelectorAll('*');
  for (var i = 0; i < elements.length; i++) {
    var element = elements[i];
    if (element !== this && element instanceof SVGGraphicsElement) {
      var bbox = element.getBBox();
      if (rect.width && rect.height && bbox.width && bbox.height) {
        if (bbox.x >= rect.x &&
            bbox.y >= rect.y &&
            bbox.x + bbox.width <= rect.x + rect.width &&
            bbox.y + bbox.height <= rect.y + rect.height) {
          enclosureList.push(element);
        }
      }
    }
  }

  // Sort elements in document order
  enclosureList.sort(function(a, b) {
    return (a.pareDocumentPosition(b) & 2) ? 1 : -1;
  });

  // Filter elements by referenceElement
  if (referenceElement) {
    enclosureList = enclosureList.filter(function(element) {
      return element === referenceElement || element.contains(referenceElement);
    });
  }

  return enclosureList;
};

if (!SVGElement.prototype.getEnclosureList) {
  SVGElement.prototype.getEnclosureList = getEnclosureListPolyfill;
}

// The code below is for the snippet only
SVGElement.prototype.getEnclosureList2 = getEnclosureListPolyfill;

const mySVG = document.getElementById('mySVG');
const myRect = mySVG.createSVGRect();

myRect.width = myRect.height = 210;
myRect.x = myRect.y = 0;


console.log('Original', mySVG.getEnclosureList(myRect, null).length);
console.log('Polyfill', mySVG.getEnclosureList2(myRect, null).length);
svg {
  display: block;
  border: 1px solid #000;
  margin: 20px 0;
  visibility: visible;
}

rect, circle { 
  fill: rgba(255, 0, 0, 0.2);
  visibility: visiblePainted;
}
<svg id="mySVG" width="500" height="400">
  <rect x="10" y="10" width="200" height="100"></rect>
  <rect x="20" y="20" width="200" height="100"></rect>
  <circle cx="70" cy="70" r="50"></circle>
</svg>

This polyfill extends the SVGElement prototype with a getEnclosureList function that imit the native implementation. It uses querySelectorAll to get every elements of the SVG and checks if they pletely enclosed by the given rect. It then sorts the elements in document order and filters them by the reference element, if provided.

checkIntersection now:

const checkIntersectionPolyfill = function(element, rect) {
  var root = this.ownerSVGElement || this;

  // Get the bounding boxes of the two elements
  var bbox1 = element.getBBox();
  var bbox2 = rect;

  // Check if the two bounding boxes intersect
  if (bbox1.x + bbox1.width > bbox2.x &&
      bbox1.y + bbox1.height > bbox2.y &&
      bbox2.x + bbox2.width > bbox1.x &&
      bbox2.y + bbox2.height > bbox1.y) {
    // Check if the two elements actually intersect
    var intersection = root.createSVGRect();
    intersection.x = Math.max(bbox1.x, bbox2.x);
    intersection.y = Math.max(bbox1.y, bbox2.y);
    intersection.width = Math.min(bbox1.x + bbox1.width, bbox2.x + bbox2.width) - intersection.x;
    intersection.height = Math.min(bbox1.y + bbox1.height, bbox2.y + bbox2.height) - intersection.y;
    return intersection.width > 0 && intersection.height > 0;
  } else {
    return false;
  }
};

if (!SVGElement.prototype.checkIntersection) {
  SVGElement.prototype.checkIntersection = checkIntersectionPolyfill;
}

// The code below is for the snippet only
SVGElement.prototype.checkIntersection2 = checkIntersectionPolyfill;

const mySVG = document.getElementById('mySVG');
const myRect1 = document.getElementById('myRect1');
const myRect2 = mySVG.createSVGRect();

myRect2.width = myRect2.height = 100;
myRect2.x = myRect2.y = 0;

console.log('Original', mySVG.checkIntersection(myRect1, myRect2));
console.log('Polyfill', mySVG.checkIntersection2(myRect1, myRect2));
svg {
  display: block;
  border: 1px solid #000;
  margin: 20px 0;
  visibility: visible;
}

rect, circle { 
  fill: rgba(255, 0, 0, 0.2);
  visibility: visiblePainted;
}
<svg id="mySVG" width="500" height="400">
  <rect id="myRect1" x="10" y="10" width="200" height="100"></rect>
</svg>

This polyfill extends the SVGElement prototype with a checkIntersection function that imit the native implementation. It checks if the 2 elements intersect using a simple algorithm based on rectangle intersection. If the two bounding boxes intersect, it creates an SVGRect object representing the intersection and checks if it has a positive area, indicating that the two elements actually intersect.

checkEnclosure is probably the easiest to implement:

const checkEnclosurePolyfill = function(element, rect) {
  var root = this.ownerSVGElement || this;

  // Get the bounding boxes of the two elements
  var bbox1 = rect;
  var bbox2 = element.getBBox();

  // Check if bbox2 is pletely enclosed by bbox1
  return bbox1.x <= bbox2.x &&
         bbox1.y <= bbox2.y &&
         bbox1.x + bbox1.width >= bbox2.x + bbox2.width &&
         bbox1.y + bbox1.height >= bbox2.y + bbox2.height;
};

if (!SVGElement.prototype.checkEnclosure) {
  SVGElement.prototype.checkEnclosure = checkEnclosurePolyfill;
}

// The code below is for the snippet only
SVGElement.prototype.checkEnclosure2 = checkEnclosurePolyfill;

const mySVG = document.getElementById('mySVG');
const myRect1 = document.getElementById('myRect1');
const myRect2 = mySVG.createSVGRect();

myRect2.width = myRect2.height = 250;
myRect2.x = myRect2.y = 0;

console.log('Original', mySVG.checkEnclosure(myRect1, myRect2));
console.log('Polyfill', mySVG.checkEnclosure2(myRect1, myRect2));
svg {
  display: block;
  border: 1px solid #000;
  margin: 20px 0;
  visibility: visible;
}

rect, circle { 
  fill: rgba(255, 0, 0, 0.2);
  visibility: visiblePainted;
}
<svg id="mySVG" width="500" height="400">
  <rect id="myRect1" x="10" y="10" width="200" height="100"></rect>
</svg>

This polyfill extends the SVGElement prototype with a checkEnclosure function that imit the native implementation. It checks if the first element is pletely enclosed by the second one.

I think there is a minor bug in the above polyfills--

  // Filter elements by referenceElement
  if (referenceElement) {
    enclosureList = enclosureList.filter(function(element) {
      return element === referenceElement || element.contains(referenceElement);
    });
  }

it should instead be referenceElement.contains(element)

https://www.w3/TR/SVG11/struct.html#__svg__SVGSVGElement__getEnclosureList

SVGElement referenceElement

If not null, then any intersected element that doesn't have the referenceElement as ancestor must not be included in the returned NodeList.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论