update
parent
eb4a0119e0
commit
59608c7409
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* {}
|
||||
* @author yutent<yutent.io@gmail.com>
|
||||
* @date 2024/02/05 16:19:45
|
||||
*/
|
||||
|
||||
import { DOMParser } from '@xmldom/xmldom'
|
||||
import svgpath from 'svgpath'
|
||||
|
||||
import { PATH_DECIMAL } from '../config.js'
|
||||
import { normalizePath, pathify } from './path.js'
|
||||
import { generateAmendTrans } from './view_box.js'
|
||||
|
||||
/**
|
||||
* 翻转svgpath
|
||||
* @param {string} path path序列
|
||||
* @return {string} 反转后的path序列
|
||||
*/
|
||||
export function reversal(path) {
|
||||
return svgpath(path).scale(1, -1).round(PATH_DECIMAL).toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换svg为一个path,并且按照目标高度,偏移进行拉伸
|
||||
* @param {string} svg 原始svg
|
||||
* @param {object} options.targetHeight 目标高度
|
||||
* @return {object} path序列和viewbox
|
||||
*/
|
||||
export function normalizeSvg(svg, options) {
|
||||
let svgDocNode = new DOMParser().parseFromString(
|
||||
pathify(svg),
|
||||
'application/xml'
|
||||
)
|
||||
let svgNode = svgDocNode.getElementsByTagName('svg')[0]
|
||||
|
||||
//解决所有的变换,生成一个path
|
||||
let path = normalizePath(svgNode)
|
||||
|
||||
let trans = svgpath(path)
|
||||
|
||||
//根据目标viewbox进行变换
|
||||
let targetHeight = options.targetHeight
|
||||
|
||||
if (targetHeight) {
|
||||
let viewObj = generateAmendTrans(svgNode, targetHeight)
|
||||
viewObj.transforms.forEach(function (viewTrans) {
|
||||
trans[viewTrans[0]].apply(trans, viewTrans[1])
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
viewbox: viewObj.targetViewbox,
|
||||
path: trans.round(PATH_DECIMAL).toString()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
/**
|
||||
* {}
|
||||
* @author yutent<yutent.io@gmail.com>
|
||||
* @date 2024/02/05 16:12:14
|
||||
*/
|
||||
|
||||
import { DOMParser, XMLSerializer } from '@xmldom/xmldom'
|
||||
import svgpath from 'svgpath'
|
||||
|
||||
const shape = {
|
||||
$translatePath(convertObj, attributes) {
|
||||
let data = {}
|
||||
attributes.forEach(function (attribute) {
|
||||
data[attribute.name] = attribute.value
|
||||
})
|
||||
|
||||
return convertObj.template(data)
|
||||
},
|
||||
generatePathNode(doc, node) {
|
||||
let convertObj, attributes, path, tagName
|
||||
|
||||
tagName = String(node.tagName).toLowerCase()
|
||||
//polyline与polygon是同一个东西
|
||||
if (tagName === 'polyline') {
|
||||
tagName = 'polygon'
|
||||
}
|
||||
|
||||
convertObj = shape[tagName]
|
||||
if (!convertObj) {
|
||||
return
|
||||
}
|
||||
|
||||
path = shape.$translatePath(convertObj, node.attributes)
|
||||
if (!path) {
|
||||
return
|
||||
}
|
||||
|
||||
newPathNode = doc.createElement('path')
|
||||
//赋值d属性
|
||||
newPathNode.setAttribute('d', path)
|
||||
//保留其他属性
|
||||
node.attributes.forEach(function (attribute) {
|
||||
if (!convertObj.attrs.includes(attribute.name)) {
|
||||
newPathNode.setAttribute(attribute.name, attribute.value)
|
||||
}
|
||||
})
|
||||
|
||||
return newPathNode
|
||||
},
|
||||
rect: {
|
||||
attrs: ['x', 'y', 'width', 'height', 'rx', 'ry'],
|
||||
template(data) {
|
||||
// 需要做一个保护,如果没有写属性值那么默认为0
|
||||
if (!data.x) {
|
||||
data.x = '0'
|
||||
}
|
||||
|
||||
if (!data.y) {
|
||||
data.y = '0'
|
||||
}
|
||||
|
||||
if (!data.rx && data.ry) {
|
||||
data.rx = data.ry
|
||||
}
|
||||
|
||||
if (!data.ry && data.rx) {
|
||||
data.ry = data.rx
|
||||
}
|
||||
|
||||
if (!data.ry && !data.rx) {
|
||||
data.ry = data.rx = '0'
|
||||
}
|
||||
|
||||
return `M${data.x},${data.y}m${data.rx},0l${data.width - 2 * data.rx},0q${
|
||||
data.rx
|
||||
},0 ${data.rx},${data.ry}l0,${data.height - 2 * data.ry}q0,${
|
||||
data.ry
|
||||
} ${-data.rx},${data.ry}l${
|
||||
-data.width + 2 * data.rx
|
||||
},0q${-data.rx},0 ${-data.rx},${-data.ry}l0,${
|
||||
-data.height + 2 * data.ry
|
||||
}q0,${-data.ry},${data.rx},${-data.ry}Z`
|
||||
}
|
||||
},
|
||||
circle: {
|
||||
attrs: ['cx', 'cy', 'r'],
|
||||
template(data) {
|
||||
return `M${data.cx},${data.cy}m${-data.r},0a${data.r},${data.r},0,1,0,${
|
||||
data.r * 2
|
||||
},0a${data.r},${data.r},0,1,0,-${data.r * 2},0Z`
|
||||
}
|
||||
},
|
||||
line: {
|
||||
attrs: ['x1', 'y1', 'x2', 'y2'],
|
||||
template(data) {
|
||||
return `M${data.x1},${data.y1}L${data.x2},${data.y2}L${data.x1},${data.y1}Z`
|
||||
}
|
||||
},
|
||||
ellipse: {
|
||||
attrs: ['cx', 'cy', 'rx', 'ry'],
|
||||
template(data) {
|
||||
return `M${data.cx - data.rx},${data.cy}a${data.rx},${data.ry},0,1,0,${
|
||||
data.rx * 2
|
||||
},0a${data.rx},${data.ry},0,1,0,-${data.rx * 2},0Z`
|
||||
}
|
||||
},
|
||||
polygon: {
|
||||
attrs: ['points'],
|
||||
template(data) {
|
||||
data.points = data.points.replace(/(^\s*)|(\s*$)/g, '')
|
||||
let points = data.points.split(' ')
|
||||
let p = ''
|
||||
points.forEach(function (point, index) {
|
||||
if (index === 0) {
|
||||
p += 'M' + point
|
||||
} else {
|
||||
p += 'L' + point
|
||||
}
|
||||
})
|
||||
p += 'Z'
|
||||
|
||||
return p
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function generatePath(path, transforms) {
|
||||
let t = svgpath(path).abs()
|
||||
transforms.forEach(it => t.transform(it))
|
||||
return t.toString()
|
||||
}
|
||||
|
||||
export function normalizePath(node, transforms) {
|
||||
let path = ''
|
||||
let newTransForms = transforms ? [...transforms] : []
|
||||
|
||||
if (node.getAttribute && node.getAttribute('transform')) {
|
||||
newTransForms.push(node.getAttribute('transform'))
|
||||
}
|
||||
|
||||
if (!node.hasChildNodes() && node.tagName === 'path') {
|
||||
path = generatePath(node.getAttribute('d'), newTransForms)
|
||||
}
|
||||
|
||||
if (node.hasChildNodes()) {
|
||||
node.childNodes.forEach(function (childNode) {
|
||||
path += normalizePath(childNode, newTransForms)
|
||||
})
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
function transNode(doc, node) {
|
||||
let convertObj, newPathNode
|
||||
|
||||
//基本的图形都是没有子结点的
|
||||
if (!node.hasChildNodes() && node.nodeName !== 'path') {
|
||||
newPathNode = shape.generatePathNode(doc, node)
|
||||
newPathNode && node.parentNode.replaceChild(newPathNode, node)
|
||||
return
|
||||
}
|
||||
|
||||
node.childNodes.forEach(function (child) {
|
||||
transNode(doc, child)
|
||||
})
|
||||
}
|
||||
|
||||
export function pathify(svgString) {
|
||||
let doc = new DOMParser().parseFromString(svgString, 'application/xml')
|
||||
|
||||
transNode(doc, doc)
|
||||
|
||||
return new XMLSerializer().serializeToString(doc)
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
/**
|
||||
* {}
|
||||
* @author yutent<yutent.io@gmail.com>
|
||||
* @date 2024/02/05 16:04:06
|
||||
*/
|
||||
|
||||
function getViewBox(svgNode) {
|
||||
if (!svgNode) {
|
||||
return
|
||||
}
|
||||
|
||||
let width, height
|
||||
let viewbox = svgNode.getAttribute('viewBox').replace(',', ' ').split(' ')
|
||||
|
||||
if (viewbox && viewbox.length === 4) {
|
||||
return viewbox.map(v => +v)
|
||||
}
|
||||
|
||||
width = +svgNode.getAttribute('width')
|
||||
height = +svgNode.getAttribute('height')
|
||||
if (width != 0 && height != 0) {
|
||||
return [0, 0, width, height]
|
||||
}
|
||||
}
|
||||
|
||||
function getViewPort(svgNode) {
|
||||
let width, height, viewbox
|
||||
width = +svgNode.getAttribute('width')
|
||||
height = +svgNode.getAttribute('height')
|
||||
|
||||
viewbox = svgNode.getAttribute('viewBox').replace(',', ' ').split(' ')
|
||||
if (!width) {
|
||||
width = +viewbox[2]
|
||||
}
|
||||
if (!height) {
|
||||
height = +viewbox[3]
|
||||
}
|
||||
|
||||
return [width, height]
|
||||
}
|
||||
|
||||
// 根据 targetHeight 缩放 width
|
||||
// 如: normalizeViewport([1024, 2048], 1024)
|
||||
// #=> [0,0,512, 1024]
|
||||
function getTargetViewbox(viewport, targetHeight) {
|
||||
let width = viewport[0]
|
||||
let height = viewport[1]
|
||||
|
||||
if (height != targetHeight) {
|
||||
width = ~~((targetHeight / height) * width)
|
||||
height = targetHeight
|
||||
}
|
||||
|
||||
return [0, 0, width, height]
|
||||
}
|
||||
|
||||
function normalizeXY(viewbox, targetViewbox) {
|
||||
let x, y, targetX, targetY
|
||||
|
||||
x = viewbox[0]
|
||||
y = viewbox[1]
|
||||
targetX = targetViewbox[0]
|
||||
targetY = targetViewbox[1]
|
||||
//可能会有x,y偏移的情况,所以这边需要做出相应的转换
|
||||
if (x != targetX || y != targetY) {
|
||||
return [['translate', [targetX - x, targetY - y]]]
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
function normalizeWidthHeight(viewbox, targetViewbox) {
|
||||
let width, height, targetWidth, targetHeight, widthScale, heightScale
|
||||
|
||||
width = viewbox[2]
|
||||
height = viewbox[3]
|
||||
targetWidth = targetViewbox[2]
|
||||
targetHeight = targetViewbox[3]
|
||||
|
||||
widthScale = width / targetWidth
|
||||
heightScale = height / targetHeight
|
||||
//比较正常的等比缩放
|
||||
if (widthScale == heightScale) {
|
||||
return [['scale', [1 / widthScale, 1 / widthScale]]]
|
||||
}
|
||||
|
||||
//下面是不等比缩放
|
||||
//不等比缩放后
|
||||
let maxScale = Math.max(widthScale, heightScale)
|
||||
let transforms = [],
|
||||
newWidth,
|
||||
newHeight
|
||||
|
||||
if (widthScale < heightScale) {
|
||||
newWidth = targetWidth * maxScale
|
||||
transforms.push(['translate', [(newWidth - width) / 2, 0]])
|
||||
} else if (widthScale > heightScale) {
|
||||
newHeight = newHeight * maxScale
|
||||
transforms.push(['translate', [0, (newHeight - height) / 2]])
|
||||
}
|
||||
|
||||
transforms.push(['scale', [1 / maxScale, 1 / maxScale]])
|
||||
|
||||
return transforms
|
||||
}
|
||||
|
||||
//返回需要进行的转换数组
|
||||
function normalizeViewBox(viewbox, targetViewbox) {
|
||||
return normalizeXY(viewbox, targetViewbox).concat(
|
||||
normalizeWidthHeight(viewbox, targetViewbox)
|
||||
)
|
||||
}
|
||||
|
||||
export function generateAmendTrans(svgNode, targetHeight) {
|
||||
let viewport = getViewPort(svgNode)
|
||||
let viewbox = getViewBox(svgNode)
|
||||
let targetViewbox = getTargetViewbox(viewport, targetHeight)
|
||||
|
||||
return {
|
||||
transforms: normalizeViewBox(viewbox, targetViewbox),
|
||||
targetViewbox: targetViewbox
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue