From 00889573db43f793a66243420aa2a472a46e6267 Mon Sep 17 00:00:00 2001 From: yutent Date: Tue, 26 Mar 2024 18:30:07 +0800 Subject: [PATCH] update --- src/elem.js | 109 +++++++++++++++++++++++++++ src/index.js | 23 ++++++ src/lib/color.js | 135 ++++++++++++++++++++++++++++++++++ src/lib/constants.js | 77 ++++++++++++++++++++ src/utils.js | 170 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 514 insertions(+) create mode 100644 src/elem.js create mode 100644 src/index.js create mode 100644 src/lib/color.js create mode 100644 src/lib/constants.js create mode 100644 src/utils.js diff --git a/src/elem.js b/src/elem.js new file mode 100644 index 0000000..d2fd633 --- /dev/null +++ b/src/elem.js @@ -0,0 +1,109 @@ +/** + * {} + * @author yutent + * @date 2024/03/26 10:37:00 + */ + +import { $, h } from './utils.js' + +export class Component { + #node + type = null + + constructor(el) { + this.#node = el + this.type = el.tagName.toLowerCase() + } + + get node() { + return this.#node + } + + get textContent() { + return this.#node.textContent + } + + set textContent(val) { + this.#node.textContent = val + } + + #create(name, attr, child) { + let node = h(name, attr, child) + this.#node.appendChild(node) + return new Component(node) + } + + append(...elems) { + for (let el of elems) { + this.#node.appendChild(el instanceof Component ? el.node : el) + } + return this + } + + select(selector) { + let node = $(selector, this.#node) + if (node) { + return new Component(node) + } + return null + } + + attr(key, val) { + let node = this.node + let out = {} + + if (key) { + if (typeof key === 'string') { + if (val === void 0) { + return node.getAttribute(key) + } else { + key = { [key]: val } + } + } + + h(node, key) + return this + } + + // 无任何参数时, 返回节点所有的属性 + for (let it of Array.from(node.attributes)) { + out[it.nodeName] = it.nodeValue + } + + return out + } + + g(...args) { + let el = this.#create('g') + if (args.length) { + el.append(...args) + } + return el + } + + style(content) { + let node = this.select('style') + if (node === null) { + node = this.#create('style') + } + node.textContent += content + return node + } + + rect(x, y, width, height) { + return this.#create('rect', { x, y, width, height }) + } + + text(x = 0, y = 0, text = '') { + return this.#create('text', { x, y }, text) + } + + // 使用foreignObject实现文本自动换行等排版 + autoText(x = 0, y = 0, width, height, text = '') { + return this.#create('foreignObject', { x, y, width, height }, text) + } + + path(d) { + return this.#create('path', { d }) + } +} diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..7a6ccf8 --- /dev/null +++ b/src/index.js @@ -0,0 +1,23 @@ +/** + * {} + * @author yutent + * @date 2024/03/26 10:07:25 + */ + +import { $, h, type } from './utils.js' +import { xmlns, doc, win } from './lib/constants.js' + +import { Component } from './elem.js' + +export function createSvg(el) { + if (el) { + if (type(el) === 'string') { + el = $(el) + } + h(el, { xmlns }) + } else { + el = h('svg', { xmlns }) + } + + return new Component(el) +} diff --git a/src/lib/color.js b/src/lib/color.js new file mode 100644 index 0000000..fee91f6 --- /dev/null +++ b/src/lib/color.js @@ -0,0 +1,135 @@ +/** + * {颜色格式转换} + * @author yutent + * @date 2024/03/26 10:07:01 + */ + +// H: 色相, S: 饱和度, B/V: 亮度 +export function hsb2rgb(hsb) { + let h = hsb.h + let s = Math.round((hsb.s * 255) / 100) + let v = Math.round((hsb.b * 255) / 100) + let r = 0 + let g = 0 + let b = 0 + + if (s === 0) { + r = g = b = v + } else { + let t1 = v + let t2 = ((255 - s) * v) / 255 + let t3 = ((t1 - t2) * (h % 60)) / 60 + + // + if (h === 360) { + h = 0 + } + + if (h < 60) { + r = t1 + g = t2 + t3 + b = t2 + } else if (h < 120) { + r = t1 - t3 + g = t1 + b = t2 + } else if (h < 180) { + r = t2 + g = t1 + b = t2 + t3 + } else if (h < 240) { + r = t2 + g = t1 - t3 + b = t1 + } else if (h < 300) { + r = t2 + t3 + g = t2 + b = t1 + } else if (h < 360) { + r = t1 + g = t2 + b = t1 - t3 + } + } + r = Math.round(r) + g = Math.round(g) + b = Math.round(b) + + return { r, g, b } +} + +export function rgb2hex({ r, g, b }, a) { + let hex = [r, g, b].map(it => it.toString(16).padStart(2, '0')).join('') + if (a !== void 0) { + hex += (~~((a / 100) * 255)).toString(16) + } + return hex +} + +export function hex2rgb(hex) { + let r, g, b, a + + hex = hex.replace(/^#/, '') + + switch (hex.length) { + case 3: + case 4: + r = hex[0].repeat(2) + g = hex[1].repeat(2) + b = hex[2].repeat(2) + a = (hex[3] || 'f').repeat(2) + + break + + case 6: + case 8: + r = hex.slice(0, 2) + g = hex.slice(2, 4) + b = hex.slice(4, 6) + a = hex.slice(6, 8) || 'ff' + break + } + + r = parseInt(r, 16) + g = parseInt(g, 16) + b = parseInt(b, 16) + a = ~~((parseInt(a, 16) * 100) / 255) + + return { r, g, b, a } +} + +export function rgb2hsb({ r, g, b }) { + let hsb = { h: 0, s: 0, b: 0 } + let max = Math.max(r, g, b) + let min = Math.min(r, g, b) + let delta = max - min + + hsb.b = max + hsb.s = max === 0 ? 0 : (delta * 255) / max + + if (hsb.s === 0) { + hsb.h = -1 + } else { + if (r === max) { + hsb.h = (g - b) / delta + } else if (g === max) { + hsb.h = 2 + (b - r) / delta + } else { + hsb.h = 4 + (r - g) / delta + } + } + hsb.h *= 60 + + if (hsb.h < 0) { + hsb.h += 360 + } + + hsb.s *= 100 / 255 + hsb.b *= 100 / 255 + + return hsb +} + +export function hex2hsb(hex) { + return rgb2hsb(hex2rgb(hex)) +} diff --git a/src/lib/constants.js b/src/lib/constants.js new file mode 100644 index 0000000..1340994 --- /dev/null +++ b/src/lib/constants.js @@ -0,0 +1,77 @@ +/** + * {一些常量} + * @author yutent + * @date 2024/03/06 16:25:01 + */ + +export const doc = document +export const win = window +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 CSS_ATTR = { + 'alignment-baseline': 1, + 'baseline-shift': 1, + clip: 1, + 'clip-path': 1, + 'clip-rule': 1, + color: 1, + 'color-interpolation': 1, + 'color-interpolation-filters': 1, + 'color-profile': 1, + 'color-rendering': 1, + cursor: 1, + direction: 1, + display: 1, + 'dominant-baseline': 1, + 'enable-background': 1, + fill: 1, + 'fill-opacity': 1, + 'fill-rule': 1, + filter: 1, + 'flood-color': 1, + 'flood-opacity': 1, + font: 1, + 'font-family': 1, + 'font-size': 1, + 'font-size-adjust': 1, + 'font-stretch': 1, + 'font-style': 1, + 'font-variant': 1, + 'font-weight': 1, + 'glyph-orientation-horizontal': 1, + 'glyph-orientation-vertical': 1, + 'image-rendering': 1, + kerning: 1, + 'letter-spacing': 1, + 'lighting-color': 1, + marker: 1, + 'marker-end': 1, + 'marker-mid': 1, + 'marker-start': 1, + mask: 1, + opacity: 1, + overflow: 1, + 'pointer-events': 1, + 'shape-rendering': 1, + 'stop-color': 1, + 'stop-opacity': 1, + stroke: 1, + 'stroke-dasharray': 1, + 'stroke-dashoffset': 1, + 'stroke-linecap': 1, + 'stroke-linejoin': 1, + 'stroke-miterlimit': 1, + 'stroke-opacity': 1, + 'stroke-width': 1, + 'text-anchor': 1, + 'text-decoration': 1, + 'text-rendering': 1, + 'unicode-bidi': 1, + visibility: 1, + 'word-spacing': 1, + 'writing-mode': 1 +} diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 0000000..13cd930 --- /dev/null +++ b/src/utils.js @@ -0,0 +1,170 @@ +/** + * {} + * @author yutent + * @date 2024/03/06 09:55:31 + */ + +import { xlink, xmlns, xhtmlns, HTML_TAGS, doc, win } from './lib/constants.js' + +export function uuid(prefix = '') { + return prefix + Math.random().toString(16).slice(-8) +} + +export function noop() {} + +//驼峰转换为连字符线风格 +export function hyphen(target) { + return target.replace(/([a-z\d])([A-Z]+)/g, '$1-$2').toLowerCase() +} + +//连字符转换为驼峰风格 +export function camelize(target) { + //提前判断,提高效率 + if (target.indexOf('-') < 0) { + return target + } + return target.replace(/\-([a-z])/g, (m, s) => s.toUpperCase()) +} + +export function type(o) { + if (o === null) { + return null + } + return Object.prototype.toString.call(o).slice(8, -1).toLowerCase() +} + +export function $(selector, container, multi) { + let fn = multi ? 'querySelectorAll' : 'querySelector' + if (container) { + return container[fn](selector) + } + return document.body[fn](selector) +} + +export function $$(selector, container) { + return $(selector, container, true) +} + +export function h(el, props = null, children) { + if (typeof el === 'string') { + el = doc.createElementNS(HTML_TAGS.includes(el) ? xhtmlns : xmlns, el) + } + + if (props) { + for (let key in props) { + let val = props[key] + if (val === void 0) { + continue + } + 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 (children) { + if (Array.isArray(children)) { + children = children.join('') + } + + 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 }) + el.appendChild(f) + } else { + el.appendChild(doc.createTextNode(children)) + } + + el.normalize() + } + return el +} + +window.h = h + +export function clone(obj) { + if (typeof obj == 'function' || Object(obj) !== obj) { + return obj + } + let res = new obj.constructor() + for (let key in obj) + if (obj.hasOwnProperty(key)) { + res[key] = clone(obj[key]) + } + return res +} + +export function preload(src, f) { + let img = new Image() + + img.onload = function () { + f.call(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 + "')" +}