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