在 Safari(版本 18.0.1)中运行此代码时,生成的图像为:
但是,在Chrome(版本131.0.6778.139)中运行代码时,生成的图像是:
如果我在 svg 本身中定义宽度和高度,我可以让这两个匹配...
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20"...
但是,出于无关紧要的原因,修改原始 SVG 对于实际情况来说是不切实际的。
有没有办法将 OpenLayers 与 SVG 结合使用,这样无论浏览器如何,这些情况都会匹配?
const osm = new ol.source.OSM();
osm.setTileGridForProjection(
"EPSG:4326",
ol.tilegrid.createXYZ({ extent: [-180, -90, 180, 90] })
);
const tileLayer = new ol.layer.Tile({ source: osm });
const coordinate = [-97, 38];
const svg =
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.7.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M256 0c17.7 0 32 14.3 32 32l0 10.4c93.7 13.9 167.7 88 181.6 181.6l10.4 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-10.4 0c-13.9 93.7-88 167.7-181.6 181.6l0 10.4c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-10.4C130.3 455.7 56.3 381.7 42.4 288L32 288c-17.7 0-32-14.3-32-32s14.3-32 32-32l10.4 0C56.3 130.3 130.3 56.3 224 42.4L224 32c0-17.7 14.3-32 32-32zM107.4 288c12.5 58.3 58.4 104.1 116.6 116.6l0-20.6c0-17.7 14.3-32 32-32s32 14.3 32 32l0 20.6c58.3-12.5 104.1-58.4 116.6-116.6L384 288c-17.7 0-32-14.3-32-32s14.3-32 32-32l20.6 0C392.1 165.7 346.3 119.9 288 107.4l0 20.6c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-20.6C165.7 119.9 119.9 165.7 107.4 224l20.6 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-20.6 0zM256 224a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>';
console.log("svg", svg);
const svgPoint = new ol.Feature({
geometry: new ol.geom.Point(coordinate),
properties: {
name: "svgPoint"
}
});
const layer = new ol.layer.Vector({
source: new ol.source.Vector({
features: [svgPoint]
}),
style: (feature) => {
const properties = feature.getProperties();
let style;
if (properties.properties.name === "svgPoint") {
style = new ol.style.Style({
image: new ol.style.Icon({
opacity: 1,
src: "data:image/svg+xml;base64," + btoa(svg),
width: 20,
height: 20
})
});
}
return style;
}
});
const map = new ol.Map({
target: "map",
layers: [tileLayer, layer],
view: new ol.View({
projection: "EPSG:4326",
center: coordinate,
zoom: 5
}),
controls: ol.control.defaults.defaults({ attribution: false, zoom: false })
});
ol.proj.get("EPSG:4326").setExtent([-180, -85, 180, 85]);
#map {
height: 256px;
width: 256px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/10.2.1/ol.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/10.2.1/dist/ol.min.js"></script>
<div id="map"></div>
绘制到画布上的未调整大小的 SVG 的大小将取决于(地图)画布的大小。 在 Firefox 中它根本不起作用。 如果您不需要支持 Firefox,您可以在图标选项中添加
color: 'transparent
,` 以获得更一致的结果,但最好在运行时将真正的默认值注入 SVG - 可以获取 SVG,解析、添加或更改属性,然后序列化回 SVG 文本(您需要使代码异步)
const svgDimensions = (text) => {
const xml = new DOMParser().parseFromString(text, 'application/xml');
const svg = xml.getElementsByTagName('svg')[0];
const defaultWidth = 300;
const defaultHeight = 150;
// extract any width or height values with 2 character units
const defaultUnit = '';
const ww = svg?.getAttribute('width');
const wUnit = ww?.match(/[a-z]{2}$/)?.[0] || defaultUnit;
let w = Number(ww?.replace(new RegExp(wUnit + '$'), ''));
const hh = svg?.getAttribute('height');
const hUnit = hh?.match(/[a-z]{2}$/)?.[0] || defaultUnit;
let h = Number(hh?.replace(new RegExp(hUnit + '$'), ''));
// disregard NaN percentage values
w = w > 0 ? w : 0;
h = h > 0 ? h : 0;
const absent = !(w > 0 && h > 0);
const viewBox = svg
?.getAttribute('viewBox')
?.split(/[\s,]+?/)
.map(Number);
if (viewBox || absent) {
const wView = viewBox?.[2];
const hView = viewBox?.[3];
if (absent && wView > 0 && hView > 0) {
// calculate missing dimensions based on viewBox aspect ratio
// and default height
if (w > 0) {
h = (w * hView) / wView;
svg.setAttribute('height', h + wUnit);
} else if (h > 0) {
w = (h * wView) / hView;
svg.setAttribute('width', w + hUnit);
} else {
h = defaultHeight;
w = (h * wView) / hView;
svg.setAttribute('width', w + defaultUnit);
svg.setAttribute('height', h + defaultUnit);
}
} else {
// if no valid viewBox use defaults for missing dimensions
if (w === 0) {
w = defaultWidth;
svg.setAttribute('width', w + defaultUnit);
}
if (h === 0) {
h = defaultHeight;
svg.setAttribute('height', h + defaultUnit);
}
}
svg.setAttribute('preserveAspectRatio', 'none');
text = new XMLSerializer().serializeToString(xml) || text;
}
return text;
}
const injectDimensions = async (src) => {
const blob = await fetch(src)
.then((response) => {
if (response.headers.get('content-type').includes('image/svg+xml')) {
return response.text().then((text) => {
return new Blob([svgDimensions(text)], {type: 'image/svg+xml'});
});
}
return response.blob();
});
return URL.createObjectURL(blob);
}
(async () => {
const osm = new ol.source.OSM();
osm.setTileGridForProjection(
"EPSG:4326",
ol.tilegrid.createXYZ({ extent: [-180, -90, 180, 90] })
);
const tileLayer = new ol.layer.Tile({ source: osm });
const coordinate = [-97, 38];
const svg =
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.7.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M256 0c17.7 0 32 14.3 32 32l0 10.4c93.7 13.9 167.7 88 181.6 181.6l10.4 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-10.4 0c-13.9 93.7-88 167.7-181.6 181.6l0 10.4c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-10.4C130.3 455.7 56.3 381.7 42.4 288L32 288c-17.7 0-32-14.3-32-32s14.3-32 32-32l10.4 0C56.3 130.3 130.3 56.3 224 42.4L224 32c0-17.7 14.3-32 32-32zM107.4 288c12.5 58.3 58.4 104.1 116.6 116.6l0-20.6c0-17.7 14.3-32 32-32s32 14.3 32 32l0 20.6c58.3-12.5 104.1-58.4 116.6-116.6L384 288c-17.7 0-32-14.3-32-32s14.3-32 32-32l20.6 0C392.1 165.7 346.3 119.9 288 107.4l0 20.6c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-20.6C165.7 119.9 119.9 165.7 107.4 224l20.6 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-20.6 0zM256 224a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>';
//console.log("svg", svg);
const svgPoint = new ol.Feature({
geometry: new ol.geom.Point(coordinate),
properties: {
name: "svgPoint"
}
});
const src = await injectDimensions("data:image/svg+xml;base64," + btoa(svg));
//const src = await injectDimensions('https://upload.wikimedia.org/wikipedia/commons/f/fd/Ghostscript_Tiger.svg');
const layer = new ol.layer.Vector({
source: new ol.source.Vector({
features: [svgPoint]
}),
style: (feature) => {
const properties = feature.getProperties();
let style;
if (properties.properties.name === "svgPoint") {
style = new ol.style.Style({
image: new ol.style.Icon({
opacity: 1,
src: src,
width: 75,
height: 50
})
});
}
return style;
}
});
const map = new ol.Map({
target: "map",
layers: [tileLayer, layer],
view: new ol.View({
projection: "EPSG:4326",
center: coordinate,
zoom: 5
}),
controls: ol.control.defaults.defaults({ attribution: false, zoom: false })
});
ol.proj.get("EPSG:4326").setExtent([-180, -85, 180, 85]);
}) ();
#map {
height: 256px;
width: 256px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/10.2.1/ol.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/10.2.1/dist/ol.min.js"></script>
<div id="map"></div>