update
parent
30c7d671a9
commit
f5321e219a
137
src/elem.js
137
src/elem.js
|
@ -3,16 +3,34 @@
|
||||||
* @author yutent<yutent.io@gmail.com>
|
* @author yutent<yutent.io@gmail.com>
|
||||||
* @date 2024/03/26 10:37:00
|
* @date 2024/03/26 10:37:00
|
||||||
*/
|
*/
|
||||||
|
import {} from './lib/constants.js'
|
||||||
import { $, h } from './utils.js'
|
import { $, uuid, h, preload, hyphen } from './utils.js'
|
||||||
|
|
||||||
export class Component {
|
export class Component {
|
||||||
#node
|
#node
|
||||||
type = null
|
type
|
||||||
|
id
|
||||||
|
defs
|
||||||
|
|
||||||
constructor(el) {
|
constructor(el) {
|
||||||
this.#node = el
|
this.#node = el
|
||||||
this.type = el.tagName.toLowerCase()
|
|
||||||
|
let _type = el.tagName.toLowerCase()
|
||||||
|
|
||||||
|
if (_type !== 'defs') {
|
||||||
|
this.id = uuid(_type)
|
||||||
|
el.id = this.id
|
||||||
|
}
|
||||||
|
|
||||||
|
this.type = _type
|
||||||
|
|
||||||
|
if (_type === 'svg') {
|
||||||
|
let defs = this.select('defs')
|
||||||
|
if (!defs) {
|
||||||
|
defs = this.#create('defs')
|
||||||
|
}
|
||||||
|
this.defs = defs
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get node() {
|
get node() {
|
||||||
|
@ -27,6 +45,10 @@ export class Component {
|
||||||
this.#node.textContent = val
|
this.#node.textContent = val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get children() {
|
||||||
|
return Array.from(this.#node.children)
|
||||||
|
}
|
||||||
|
|
||||||
#create(name, attr, child) {
|
#create(name, attr, child) {
|
||||||
let node = h(name, attr, child)
|
let node = h(name, attr, child)
|
||||||
this.#node.appendChild(node)
|
this.#node.appendChild(node)
|
||||||
|
@ -53,15 +75,16 @@ export class Component {
|
||||||
let out = {}
|
let out = {}
|
||||||
|
|
||||||
if (key) {
|
if (key) {
|
||||||
|
let attrs = key
|
||||||
if (typeof key === 'string') {
|
if (typeof key === 'string') {
|
||||||
if (val === void 0) {
|
if (val === void 0) {
|
||||||
return node.getAttribute(key)
|
return node.getAttribute(key)
|
||||||
} else {
|
} else {
|
||||||
key = { [key]: val }
|
attrs = { [key]: val }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h(node, key)
|
h(node, attrs)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,8 +96,35 @@ export class Component {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
move(x, y) {
|
||||||
|
this.attr({ x, y })
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
size(width, height) {
|
||||||
|
this.attr({ width, height })
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
transform(transform) {
|
||||||
|
this.attr({ transform })
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------ */
|
/* ------------------------------ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 引入defs中的组件
|
||||||
|
*/
|
||||||
|
use(id) {
|
||||||
|
if (id instanceof Component) {
|
||||||
|
id = id.id
|
||||||
|
} else if (id.startsWith('#')) {
|
||||||
|
id = id.slice(1)
|
||||||
|
}
|
||||||
|
return this.#create('use', { 'xlink:href': '#' + id })
|
||||||
|
}
|
||||||
|
|
||||||
g(...args) {
|
g(...args) {
|
||||||
let el = this.#create('g')
|
let el = this.#create('g')
|
||||||
if (args.length) {
|
if (args.length) {
|
||||||
|
@ -92,25 +142,92 @@ export class Component {
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
symbol(vx, vy, vw, vh) {
|
||||||
|
return this.#create('symbol', { viewBox: [vx, vy, vw, vh].join(' ') })
|
||||||
|
}
|
||||||
|
|
||||||
rect(x, y, width, height) {
|
rect(x, y, width, height) {
|
||||||
return this.#create('rect', { x, y, width, height })
|
return this.#create('rect', { x, y, width, height })
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 多边形, 传入多个坐标点
|
||||||
|
*/
|
||||||
|
polygon(...points) {
|
||||||
|
if (points.length) {
|
||||||
|
points = points.flatMap(p => p.join(',')).join(' ')
|
||||||
|
return this.#create('polygon', { points })
|
||||||
|
}
|
||||||
|
throw new Error('polygon() argument is undefined')
|
||||||
|
}
|
||||||
|
|
||||||
text(x = 0, y = 0, text = '') {
|
text(text = '', x = 0, y = 0) {
|
||||||
return this.#create('text', { x, y }, text)
|
return this.#create('text', { x, y }, text)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用foreignObject实现文本自动换行等排版
|
// 使用foreignObject实现文本自动换行等排版
|
||||||
autoText(x = 0, y = 0, width, height, text = '') {
|
autoText(text = '', x = 0, y = 0, width, height) {
|
||||||
return this.#create('foreignObject', { x, y, width, height }, text)
|
return this.#create('foreignObject', { x, y, width, height }, text)
|
||||||
}
|
}
|
||||||
|
|
||||||
line(x1, y1, x2, y2, attr = {}) {
|
line(x1, y1, x2, y2, attr = { stroke: '#000', strokeWidth: 1 }) {
|
||||||
return this.#create('line', { x1, y1, x2, y2, ...attr })
|
return this.#create('line', { x1, y1, x2, y2, ...attr })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拆线, 传入多组坐标
|
||||||
|
*/
|
||||||
polyline(...points) {
|
polyline(...points) {
|
||||||
return this.#create('polyline', { points })
|
if (points.length) {
|
||||||
|
points = points.flatMap(p => p.join(',')).join(' ')
|
||||||
|
return this.#create('polyline', { points })
|
||||||
|
}
|
||||||
|
throw new Error('polyline() argument is undefined')
|
||||||
|
}
|
||||||
|
|
||||||
|
circle(cx = 0, cy = 0, r = 1) {
|
||||||
|
return this.#create('circle', { cx, cy, r })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 椭圆
|
||||||
|
* @param cx, cy 圆心坐标
|
||||||
|
* @param rx, ry X、Y轴半径
|
||||||
|
*/
|
||||||
|
ellipse(cx, cy, rx, ry) {
|
||||||
|
return this.#create('ellipse', { cx, cy, rx, ry })
|
||||||
|
}
|
||||||
|
|
||||||
|
image(href, { x, y, width, height } = {}) {
|
||||||
|
let el = this.#create('image', {
|
||||||
|
preserveAspectRatio: 'none',
|
||||||
|
href,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
})
|
||||||
|
|
||||||
|
if (width === void 0 || height === void 0) {
|
||||||
|
preload(href, function (img) {
|
||||||
|
h(el.node, {
|
||||||
|
width: img.naturalWidth,
|
||||||
|
height: img.naturalHeight
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return el
|
||||||
|
}
|
||||||
|
|
||||||
|
pattern(x, y, width, height, viewBox = []) {
|
||||||
|
let props = { patternUnits: 'userSpaceOnUse', x, y, width, height }
|
||||||
|
|
||||||
|
if (viewBox.length) {
|
||||||
|
props.viewBox = viewBox.join(' ')
|
||||||
|
} else {
|
||||||
|
props.viewBox = [x, y, width, height].join(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.#create('pattern', props)
|
||||||
}
|
}
|
||||||
|
|
||||||
path(d) {
|
path(d) {
|
||||||
|
|
|
@ -10,7 +10,14 @@ export const xlink = 'http://www.w3.org/1999/xlink'
|
||||||
export const xmlns = 'http://www.w3.org/2000/svg'
|
export const xmlns = 'http://www.w3.org/2000/svg'
|
||||||
export const xhtmlns = 'http://www.w3.org/1999/xhtml'
|
export const xhtmlns = 'http://www.w3.org/1999/xhtml'
|
||||||
|
|
||||||
export const HTML_TAGS = ['div', 'span', 'p']
|
export const HTML_TAGS = { div: 1, span: 1, p: 1 }
|
||||||
|
export const SPEC_ATTR = {
|
||||||
|
viewBox: 1,
|
||||||
|
preserveAspectRatio: 1,
|
||||||
|
patternUnits: 1,
|
||||||
|
patternTransform: 1,
|
||||||
|
patternContentUnits: 1
|
||||||
|
}
|
||||||
|
|
||||||
export const CSS_ATTR = {
|
export const CSS_ATTR = {
|
||||||
'alignment-baseline': 1,
|
'alignment-baseline': 1,
|
||||||
|
|
91
src/utils.js
91
src/utils.js
|
@ -4,7 +4,16 @@
|
||||||
* @date 2024/03/06 09:55:31
|
* @date 2024/03/06 09:55:31
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { xlink, xmlns, xhtmlns, HTML_TAGS, doc, win } from './lib/constants.js'
|
import {
|
||||||
|
xlink,
|
||||||
|
xmlns,
|
||||||
|
xhtmlns,
|
||||||
|
HTML_TAGS,
|
||||||
|
SPEC_ATTR,
|
||||||
|
CSS_ATTR,
|
||||||
|
doc,
|
||||||
|
win
|
||||||
|
} from './lib/constants.js'
|
||||||
|
|
||||||
export function uuid(prefix = '') {
|
export function uuid(prefix = '') {
|
||||||
return prefix + Math.random().toString(16).slice(-8)
|
return prefix + Math.random().toString(16).slice(-8)
|
||||||
|
@ -47,7 +56,7 @@ export function $$(selector, container) {
|
||||||
|
|
||||||
export function h(el, props = null, children) {
|
export function h(el, props = null, children) {
|
||||||
if (typeof el === 'string') {
|
if (typeof el === 'string') {
|
||||||
el = doc.createElementNS(HTML_TAGS.includes(el) ? xhtmlns : xmlns, el)
|
el = doc.createElementNS(HTML_TAGS[el] ? xhtmlns : xmlns, el)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props) {
|
if (props) {
|
||||||
|
@ -56,17 +65,17 @@ export function h(el, props = null, children) {
|
||||||
if (val === void 0) {
|
if (val === void 0) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
key = hyphen(key)
|
if (!SPEC_ATTR[key]) {
|
||||||
|
key = hyphen(key)
|
||||||
|
}
|
||||||
if (val === null) {
|
if (val === null) {
|
||||||
el.removeAttribute(key)
|
el.removeAttribute(key)
|
||||||
} else {
|
} else {
|
||||||
if (key.slice(0, 6) == 'xlink:') {
|
if (CSS_ATTR[key]) {
|
||||||
el.setAttributeNS(xlink, key.slice(6), val)
|
el.style.cssText += `${key}:${props[key]};`
|
||||||
} else if (key.slice(0, 4) == 'xml:') {
|
continue
|
||||||
el.setAttributeNS(xmlns, key.slice(4), val)
|
|
||||||
} else {
|
|
||||||
el.setAttribute(key, val)
|
|
||||||
}
|
}
|
||||||
|
el.setAttribute(key, val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,11 +86,7 @@ export function h(el, props = null, children) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (el.tagName.toLowerCase() === 'foreignobject') {
|
if (el.tagName.toLowerCase() === 'foreignobject') {
|
||||||
let f = h('div', { xmlns: 'http://www.w3.org/1999/xhtml' })
|
let f = h('div', null, children)
|
||||||
let p = h('p', null, children)
|
|
||||||
f.appendChild(p)
|
|
||||||
|
|
||||||
h(el, { xmlns })
|
|
||||||
el.appendChild(f)
|
el.appendChild(f)
|
||||||
} else {
|
} else {
|
||||||
el.appendChild(doc.createTextNode(children))
|
el.appendChild(doc.createTextNode(children))
|
||||||
|
@ -106,65 +111,11 @@ export function clone(obj) {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
export function preload(src, f) {
|
export function preload(src, callback) {
|
||||||
let img = new Image()
|
let img = new Image()
|
||||||
|
|
||||||
img.onload = function () {
|
img.onload = function () {
|
||||||
f.call(img)
|
callback(img)
|
||||||
}
|
}
|
||||||
img.src = src
|
img.src = src
|
||||||
}
|
}
|
||||||
|
|
||||||
function repush(arr, item) {
|
|
||||||
let l = arr.length - 1 // 要减1, 最后如果本身在最后, 不用变
|
|
||||||
for (let i = 0; i < l; i++) {
|
|
||||||
if (arr[i] === item) {
|
|
||||||
;[arr[i], arr[l]] = [arr[l], arr[i]]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function cacher(fn, scope, postprocessor) {
|
|
||||||
function newf(...args) {
|
|
||||||
let key = args.join('\u2400'),
|
|
||||||
cache = (newf.cache = newf.cache || {}),
|
|
||||||
count = (newf.count = newf.count || [])
|
|
||||||
if (cache.hasOwnProperty(key)) {
|
|
||||||
repush(count, key)
|
|
||||||
return postprocessor ? postprocessor(cache[key]) : cache[key]
|
|
||||||
}
|
|
||||||
count.length >= 1e3 && delete cache[count.shift()]
|
|
||||||
count.push(key)
|
|
||||||
cache[key] = fn.apply(scope, args)
|
|
||||||
return postprocessor ? postprocessor(cache[key]) : cache[key]
|
|
||||||
}
|
|
||||||
return newf
|
|
||||||
}
|
|
||||||
|
|
||||||
export function jsonFiller(root, o) {
|
|
||||||
for (let i = 0, ii = root.length; i < ii; i++) {
|
|
||||||
let item = {
|
|
||||||
type: root[i].type,
|
|
||||||
attr: root[i].attr()
|
|
||||||
},
|
|
||||||
children = root[i].children()
|
|
||||||
o.push(item)
|
|
||||||
if (children.length) {
|
|
||||||
jsonFiller(children, (item.childNodes = []))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function extend(origin, target) {
|
|
||||||
let methods = Object.getOwnPropertyNames(target).filter(
|
|
||||||
n => n !== 'constructor'
|
|
||||||
)
|
|
||||||
for (let k of methods) {
|
|
||||||
Object.defineProperty(origin, k, { value: target[k] })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function url(id) {
|
|
||||||
return "url('#" + id + "')"
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue