@wayz/react-gl
Version:
React Component for DeckGL, Base on AMap, Mapbox GL
169 lines (168 loc) • 6.09 kB
JavaScript
// @ts-nocheck
/**
* 将普通 SVG 字符串或 SVG DOM 节点转换为具有自定义尺寸的图像
*
* @param {Object} settings 默认设置
* @returns {Promise}
*/
function SVGToImage(settings) {
let _settings = {
svg: null,
// 通常所有 SVG 都具有透明度,因此默认情况下使用 PNG
mimetype: 'image/png',
quality: 0.92,
width: 'auto',
height: 'auto',
outputFormat: 'base64',
};
// 重写默认设置
for (let key in settings) {
_settings[key] = settings[key];
}
return new Promise(function (resolve, reject) {
let svgNode;
if (typeof _settings.svg == 'string') {
let SVGContainer = document.createElement('div');
SVGContainer.style.display = 'none';
SVGContainer.innerHTML = _settings.svg;
svgNode = SVGContainer.firstElementChild;
}
else {
svgNode = _settings.svg;
}
let canvas = document.createElement('canvas');
let context = canvas.getContext('2d');
let svgXml = new XMLSerializer().serializeToString(svgNode);
let svgBase64 = 'data:image/svg+xml;base64,' + window.btoa(unescape(encodeURIComponent(svgXml)));
const image = new Image();
image.onload = function () {
let _this = this;
let finalWidth, finalHeight;
// 如果设置为 auto 并且指定了高度,则计算宽度(以保持纵横比)
if (_settings.width === 'auto' && _settings.height !== 'auto') {
finalWidth = (_this.width / _this.height) * _settings.height;
// 使用图像原始宽度
}
else if (_settings.width === 'auto') {
finalWidth = _this.naturalWidth;
// 使用自定义宽度
}
else {
finalWidth = _settings.width;
}
// 如果设置为 auto 并且指定了宽度,则计算高度(以保持纵横比)
if (_settings.height === 'auto' && _settings.width !== 'auto') {
finalHeight = (_this.height / _this.width) * _settings.width;
// 使用图像原始高度
}
else if (_settings.height === 'auto') {
finalHeight = _this.naturalHeight;
// 使用自定义高度
}
else {
finalHeight = _settings.height;
}
// 定义canvas内在大小
canvas.width = finalWidth;
canvas.height = finalHeight;
// 渲染图像
context.drawImage(_this, 0, 0, finalWidth, finalHeight);
if (_settings.outputFormat === 'blob') {
// 返回Blob
canvas.toBlob(function (blob) {
resolve(blob);
}, _settings.mimetype, _settings.quality);
}
else {
// 返回Base64
resolve(canvas.toDataURL(_settings.mimetype, _settings.quality));
}
};
// 加载base64 SVG
image.src = svgBase64;
});
}
/**
* 获取dom对应图片的base64地址
* @param {HTMLElement} target
* @returns 图片的base64地址
*/
function getSVGString(target) {
//克隆一份dom节点
const cloneDom = target.cloneNode(true);
cloneDom.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
const { width, height } = target.getBoundingClientRect();
//转成svg字符串
const xmlContent = `
<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">
<foreignObject x="0" y="0" width="100%" height="100%">
${target.outerHTML}
</foreignObject>
</svg>
`;
return xmlContent;
//处理部分特殊字符
// eslint-disable-next-line
return xmlContent.replace(/\n/g, '').replace(/\t/g, '').replace(/#/g, '%23');
}
/**
* 根据数据获取dom
* @param {any[]} data
* @param {Function} getHtml
* @return {HTMLDivElement} dom元素
*/
function getDom(data, getHtml) {
const iconMappingDom = document.createElement('div');
iconMappingDom.setAttribute('style', 'z-index: -1;position: absolute;top: 0;left: 0;display: flex;algin-item: center;flex-wrap: wrap;');
let html = '';
for (let [, item] of Object.entries(data)) {
const dom = getHtml(item);
html += dom;
}
iconMappingDom.innerHTML = html;
return iconMappingDom;
}
/**
* 根据数据和自定义html生成函数, 生成iconAtlas和 iconMapping
*
* @export
* @param {any[]} data
* @param {(item: any) => string} getHtml
* @param {HTMLElement} [container]
* @return {{ iconAtlas: string; iconMapping: any }}
*/
export function domToIconMapping(data, getHtml, container) {
return new Promise((resolve, reject) => {
container = container !== null && container !== void 0 ? container : document.body;
// 获取 iconMappingDom
const iconMappingDom = getDom(data, getHtml);
// 插入页面
container.appendChild(iconMappingDom);
const iconMapping = {};
let rect = iconMappingDom.getBoundingClientRect();
// 遍历iconMappingDom children生成mapping
iconMappingDom.childNodes.forEach((node, i) => {
let nodeRect = node.getBoundingClientRect();
let iconName = 'icon_' + i;
iconMapping[iconName] = {
x: nodeRect.left - rect.left,
y: nodeRect.top - rect.top,
width: nodeRect.width,
height: nodeRect.height,
// mask: false,
};
if (data[i])
data[i]['_icon_'] = iconName;
});
// 获取图片
const svgStr = getSVGString(iconMappingDom);
SVGToImage({
svg: svgStr,
quality: 1,
}).then((dataUrl) => {
// 移除dom
iconMappingDom.remove();
resolve({ iconAtlas: dataUrl, iconMapping });
});
});
}