update
parent
30c7d671a9
commit
f5321e219a
137
src/elem.js
137
src/elem.js
|
@ -3,16 +3,34 @@
|
|||
* @author yutent<yutent.io@gmail.com>
|
||||
* @date 2024/03/26 10:37:00
|
||||
*/
|
||||
|
||||
import { $, h } from './utils.js'
|
||||
import {} from './lib/constants.js'
|
||||
import { $, uuid, h, preload, hyphen } from './utils.js'
|
||||
|
||||
export class Component {
|
||||
#node
|
||||
type = null
|
||||
type
|
||||
id
|
||||
defs
|
||||
|
||||
constructor(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() {
|
||||
|
@ -27,6 +45,10 @@ export class Component {
|
|||
this.#node.textContent = val
|
||||
}
|
||||
|
||||
get children() {
|
||||
return Array.from(this.#node.children)
|
||||
}
|
||||
|
||||
#create(name, attr, child) {
|
||||
let node = h(name, attr, child)
|
||||
this.#node.appendChild(node)
|
||||
|
@ -53,15 +75,16 @@ export class Component {
|
|||
let out = {}
|
||||
|
||||
if (key) {
|
||||
let attrs = key
|
||||
if (typeof key === 'string') {
|
||||
if (val === void 0) {
|
||||
return node.getAttribute(key)
|
||||
} else {
|
||||
key = { [key]: val }
|
||||
attrs = { [key]: val }
|
||||
}
|
||||
}
|
||||
|
||||
h(node, key)
|
||||
h(node, attrs)
|
||||
return this
|
||||
}
|
||||
|
||||
|
@ -73,8 +96,35 @@ export class Component {
|
|||
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) {
|
||||
let el = this.#create('g')
|
||||
if (args.length) {
|
||||
|
@ -92,25 +142,92 @@ export class Component {
|
|||
return node
|
||||
}
|
||||
|
||||
symbol(vx, vy, vw, vh) {
|
||||
return this.#create('symbol', { viewBox: [vx, vy, vw, vh].join(' ') })
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// 使用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)
|
||||
}
|
||||
|
||||
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 })
|
||||
}
|
||||
|
||||
/**
|
||||
* 拆线, 传入多组坐标
|
||||
*/
|
||||
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) {
|
||||
|
|
|
@ -10,7 +10,14 @@ export const xlink = 'http://www.w3.org/1999/xlink'
|
|||
export const xmlns = 'http://www.w3.org/2000/svg'
|
||||
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 = {
|
||||
'alignment-baseline': 1,
|
||||
|
|
91
src/utils.js
91
src/utils.js
|
@ -4,7 +4,16 @@
|
|||
* @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 = '') {
|
||||
return prefix + Math.random().toString(16).slice(-8)
|
||||
|
@ -47,7 +56,7 @@ export function $$(selector, container) {
|
|||
|
||||
export function h(el, props = null, children) {
|
||||
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) {
|
||||
|
@ -56,17 +65,17 @@ export function h(el, props = null, children) {
|
|||
if (val === void 0) {
|
||||
continue
|
||||
}
|
||||
key = hyphen(key)
|
||||
if (!SPEC_ATTR[key]) {
|
||||
key = hyphen(key)
|
||||
}
|
||||
if (val === null) {
|
||||
el.removeAttribute(key)
|
||||
} else {
|
||||
if (key.slice(0, 6) == 'xlink:') {
|
||||
el.setAttributeNS(xlink, key.slice(6), val)
|
||||
} else if (key.slice(0, 4) == 'xml:') {
|
||||
el.setAttributeNS(xmlns, key.slice(4), val)
|
||||
} else {
|
||||
el.setAttribute(key, val)
|
||||
if (CSS_ATTR[key]) {
|
||||
el.style.cssText += `${key}:${props[key]};`
|
||||
continue
|
||||
}
|
||||
el.setAttribute(key, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -77,11 +86,7 @@ export function h(el, props = null, children) {
|
|||
}
|
||||
|
||||
if (el.tagName.toLowerCase() === 'foreignobject') {
|
||||
let f = h('div', { xmlns: 'http://www.w3.org/1999/xhtml' })
|
||||
let p = h('p', null, children)
|
||||
f.appendChild(p)
|
||||
|
||||
h(el, { xmlns })
|
||||
let f = h('div', null, children)
|
||||
el.appendChild(f)
|
||||
} else {
|
||||
el.appendChild(doc.createTextNode(children))
|
||||
|
@ -106,65 +111,11 @@ export function clone(obj) {
|
|||
return res
|
||||
}
|
||||
|
||||
export function preload(src, f) {
|
||||
export function preload(src, callback) {
|
||||
let img = new Image()
|
||||
|
||||
img.onload = function () {
|
||||
f.call(img)
|
||||
callback(img)
|
||||
}
|
||||
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