是否有针对Firefox SVG的getIntersectionList,getEnclosureList,checkIntersection,checkEnclosure的polyfill?

cui*_*ing 12 javascript firefox svg

Firefox中的SVG 1.1支持:

  • SVGSVGElement:
    • 未实现的绑定:getIntersectionList,getEnclosureList,checkIntersection,checkEnclosure,deselectAll

SVG 1.1规范:5.11.2接口SVGSVGElement

由于Firefox不支持getIntersectionList,getEnclosureList,checkIntersection,checkEnclosure方法,是否有polyfill?或者如何在javascript中为4种方法编写polyfill?

mor*_*gul 1

不幸的是,我认为目前 Firefox 中还没有针对这些功能的官方 polyfill。

我尝试了一下,看看是否有一种方法可以使用可用的 API 轻松填充这些函数。

我故意分配了第二个函数,以便我们可以将原始支持的函数(当使用原生支持它的浏览器(例如 Chrome)时)与 polyfill 进行比较。

所以,我们在这里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.compareDocumentPosition(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);
Run Code Online (Sandbox Code Playgroud)
svg {
  display: block;
  border: 1px solid #000;
  margin: 20px 0;
  visibility: visible;
}

rect, circle { 
  fill: rgba(255, 0, 0, 0.2);
  visibility: visiblePainted;
}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

这个polyfillSVGElement使用模仿getIntersectionList本机实现的函数扩展了原型。它用于querySelectorAll获取 SVG 的每个元素并检查它们是否与给定的矩形相交。然后,它按文档顺序对元素进行排序,并按参考元素(如果提供)过滤它们。

getEnclosureList非常相似:

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

  // Get all elements that are completely 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.compareDocumentPosition(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);
Run Code Online (Sandbox Code Playgroud)
svg {
  display: block;
  border: 1px solid #000;
  margin: 20px 0;
  visibility: visible;
}

rect, circle { 
  fill: rgba(255, 0, 0, 0.2);
  visibility: visiblePainted;
}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

这个polyfillSVGElement使用模仿getEnclosureList本机实现的函数扩展了原型。它用于querySelectorAll获取 SVG 的每个元素并检查它们是否完全被给定的矩形包围。然后,它按文档顺序对元素进行排序,并按参考元素(如果提供)过滤它们。

checkIntersection现在:

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));
Run Code Online (Sandbox Code Playgroud)
svg {
  display: block;
  border: 1px solid #000;
  margin: 20px 0;
  visibility: visible;
}

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

这个polyfillSVGElement使用模仿checkIntersection本机实现的函数扩展了原型。它使用基于矩形交集的简单算法检查两个元素是否相交。如果两个边界框相交,它会创建一个SVGRect表示相交的对象,并检查它是否具有正面积,表明两个元素实际上相交。

checkEnclosure可能是最容易实现的:

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 completely 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));
Run Code Online (Sandbox Code Playgroud)
svg {
  display: block;
  border: 1px solid #000;
  margin: 20px 0;
  visibility: visible;
}

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

这个polyfillSVGElement使用模仿checkEnclosure本机实现的函数扩展了原型。它检查第一个元素是否完全被第二个元素包围。