From f0ca5b6496acbfebe87c67c0bfacdf6741075947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=AB=E8=B0=B7=E5=89=91=E4=BB=99?= Date: Wed, 3 Aug 2022 21:34:44 +0800 Subject: [PATCH] =?UTF-8?q?=E6=83=B3=E5=81=9A=E5=9B=BE=E6=A0=87=EF=BC=8C?= =?UTF-8?q?=E4=BD=86=E6=98=AF=E5=BE=88=E9=BA=BB=E7=83=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- css/svg-icon.css | 15 ++++ images/icon-awoken-count.svg | 62 +++++++++++++ images/project file/build symble svgs.js | 107 ++++++++++++++--------- package.json | 2 +- script-custom_elements.js | 66 ++++++++++---- 5 files changed, 191 insertions(+), 61 deletions(-) create mode 100644 images/icon-awoken-count.svg diff --git a/css/svg-icon.css b/css/svg-icon.css index 6435dbf3..4a829bb5 100644 --- a/css/svg-icon.css +++ b/css/svg-icon.css @@ -7,4 +7,19 @@ svg { width: 100%; height: 100%; +} + .back { + fill: white; +} + .front { + fill: #096E11; +} +pad-icon[type="awoken-count"][number="full"][special] .back { + fill: #FFFFD4; +} +pad-icon[type="awoken-count"][number="full"][special] .front { + fill: #F3DC69; +} +pad-icon[type="awoken-count"][latent] .front { + fill: #378DE8; } \ No newline at end of file diff --git a/images/icon-awoken-count.svg b/images/icon-awoken-count.svg new file mode 100644 index 00000000..e9cb0c99 --- /dev/null +++ b/images/icon-awoken-count.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/project file/build symble svgs.js b/images/project file/build symble svgs.js index 591b742e..7e329fc9 100644 --- a/images/project file/build symble svgs.js +++ b/images/project file/build symble svgs.js @@ -1,19 +1,26 @@ const fs = require('fs'); const path = require('path'); const mime = require('mime'); //需要安装 -const sizeOf = require('image-size'); //需要安装 const { DOMImplementation, XMLSerializer } = require('@xmldom/xmldom'); //需要安装 -const xmlFormatter = require('xml-formatter'); +const xmlFormatter = require('xml-formatter'); //需要安装 +const sharp = require('sharp'); //需要安装 -const directory = './awokens'; -const files = fs.readdirSync(directory); +const svgNS = 'http://www.w3.org/2000/svg'; class Icon { constructor(file, dir) { this.fileName = file; this.directory = dir; this.buffer = fs.readFileSync(this.path()); - this.size = sizeOf(this.buffer); + this.sharp = sharp(this.buffer); + this.init(); + } + async init() { + this.buffer = await this.sharp.metadata(); + const {data, info} = await this.sharp.webp({ nearLossless : true}) //近似无损,而非绝对无损 + .toBuffer({ resolveWithObject: true }); + this.webpBuffer = data; + this.webpInfo = info; } path() { return path.join(this.directory, this.fileName); @@ -21,45 +28,63 @@ class Icon { base64() { return `data:${mime.getType(this.fileName)};base64,${this.buffer.toString('base64')}`; } + webpBase64() { + return `data:${mime.getType('webp')};base64,${this.webpBuffer.toString('base64')}`; + } } -const iconArr = []; -for (const file of files) -{ - const icon = new Icon(file, directory); - iconArr.push(icon); -} -iconArr.sort((a,b)=>{ - function nameNum(fileName){return parseInt(/^\d+/.exec(fileName)[0])} - return (nameNum(a.fileName) - nameNum(b.fileName)) || //先判断数字 - (a.fileName.length - b.fileName.length); //然后判断文件名长度 -}); -const svgNS = 'http://www.w3.org/2000/svg'; + //const dt = new DOMImplementation().createDocumentType('svg:svg', '-//W3C//DTD SVG 1.1//EN', 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'); //const svg = new DOMImplementation().createDocument(svgNS, 'svg', dt); -const svg = new DOMImplementation().createDocument(svgNS, 'svg'); -for (const icon of iconArr) -{ - console.log('正在处理 %s', icon.fileName); - const symbol = svg.createElement('symbol'); - const parseName = path.parse(icon.fileName); - const regRes = /^(\d+)(.*)$/ig.exec(parseName.name); - let aid = regRes ? `${parseInt(regRes[1])}${regRes[2]}` : parseName.name; - symbol.setAttribute('id', `awoken-${aid}`); - symbol.setAttribute('viewBox', `0 0 32 32`); - svg.documentElement.appendChild(symbol); - const image = svg.createElement('image'); - image.setAttribute('width', icon.size.width); - image.setAttribute('height', icon.size.height); - image.setAttribute('href', icon.base64()); - symbol.appendChild(image); +async function main({directory, idPre, svgFilename}) { + const files = fs.readdirSync(directory); + + const iconArr = []; + for (const file of files) + { + const icon = new Icon(file, directory); + await icon.init(); + iconArr.push(icon); + } + iconArr.sort((a,b)=>{ + function nameNum(fileName){return parseInt(/^\d+/.exec(fileName)[0] || 0)} + return (nameNum(a.fileName) - nameNum(b.fileName)) || //先判断数字 + (a.fileName.length - b.fileName.length); //然后判断文件名长度 + }); + const svgDoc = new DOMImplementation().createDocument(svgNS, 'svg'); + + for (const icon of iconArr) + { + console.log('正在处理 %s %s', directory, icon.fileName); + const symbol = svgDoc.createElement('symbol'); + const parseName = path.parse(icon.fileName); + + const regRes = /^(\d+)(.*)$/ig.exec(parseName.name); + let aid = regRes ? `${parseInt(regRes[1])}${regRes[2]}` : parseName.name; + symbol.setAttribute('id', `${idPre}-${aid}`); + symbol.setAttribute('viewBox', `0 0 32 32`); + svgDoc.documentElement.appendChild(symbol); + const image = svgDoc.createElement('image'); + image.setAttribute('width', icon.webpInfo.width); + image.setAttribute('height', icon.webpInfo.height); + image.setAttribute('href', icon.webpBase64()); + + symbol.appendChild(image); + } + const serialized = new XMLSerializer().serializeToString(svgDoc); + const formattedXml = xmlFormatter(serialized, { + indentation: '\t', + filter: (node) => node.type !== 'Comment', + collapseContent: true, + lineSeparator: '\n' + }); + fs.writeFileSync(svgFilename, formattedXml); } -const serialized = new XMLSerializer().serializeToString(svg); -const formattedXml = xmlFormatter(serialized, { - indentation: '\t', - filter: (node) => node.type !== 'Comment', - collapseContent: true, - lineSeparator: '\n' -}); -fs.writeFileSync('../icon-awoken.svg', formattedXml); \ No newline at end of file + +const tasks = [ + {directory: './awokens', idPre: 'awoken', svgFilename: '../icon-awoken.svg'}, + {directory: './types', idPre: 'type', svgFilename: '../icon-type.svg'}, +]; + +tasks.forEach(main); \ No newline at end of file diff --git a/package.json b/package.json index 05979ac4..d80ae3f2 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "dependencies": { "@xmldom/xmldom": "^0.8.2", - "image-size": "^1.0.2", "mime": "^3.0.0", "opencc-js": "^1.0.4", + "sharp": "^0.30.7", "xml-formatter": "^2.6.1" } } diff --git a/script-custom_elements.js b/script-custom_elements.js index 9cddf35a..82e4fc23 100644 --- a/script-custom_elements.js +++ b/script-custom_elements.js @@ -114,22 +114,26 @@ class PadIcon extends HTMLElement { // Specify observed attributes so that // attributeChangedCallback will work static get observedAttributes() { - return ['iid','type','lang']; + return [ + 'type', //图标类型 + 'number', //编号或数字,必须是数字 + 'lang', //英语、中文特殊图标的设定 + 'icon-name',//子图标名称 + 'icon-value',//子图标的值 + 'full',//觉醒打满 + 'special',//是否是特殊颜色 + ]; } - #iid = 0; + #number = 0; #type = "awoken"; - get iid() { this.#iid; } - /** - * @param {string | number} x - */ - set iid(x) { - this.setAttribute('iid', x); - this.#iid = x; + get number() { this.#number; } + set number(x) { + const number = Number(x); + if (Number.isNaN(number)) throw new Error('传入的 number 不是数字!'); + this.setAttribute('number', number); + this.#number = number; } get type() { this.#type; } - /** - * @param {string} x - */ set type(x) { this.setAttribute('type', x); this.#type = x; @@ -154,24 +158,48 @@ class PadIcon extends HTMLElement { this.update(); } attributeChangedCallback(name, oldValue, newValue) { //自定义标签属性改变 - if (name == 'iid') this.#iid = parseInt(newValue); + if (name == 'number') { + const number = Number(newValue); + this.#number = Number.isNaN(number) ? 0 : number; + } if (name == 'type') this.#type = newValue; this.update(); } update() { - let iid = this.#iid || 0; + let number = this.#number; const type = this.#type; const lang = this.getAttribute('lang') || currentLanguage.i18n; const shadow = this.shadowRoot; - const use = shadow.querySelector('use'); + const svg = shadow.querySelector('svg'); + const use = svg.querySelector('use'); switch (type) { case 'awoken': { - if (/^(?:en|ko)/.test(lang) && [40,46,47,48].includes(iid)) iid += '-en'; //英文不一样的觉醒 - if (/^(?:zh)/.test(lang) && [46,47].includes(iid)) iid += '-zh'; //中文不一样的觉醒 - use.setAttribute("href",`images/icon-awoken.svg#awoken-${iid}`); + if (/^(?:en|ko)/.test(lang) && [40,46,47,48].includes(number)) number += '-en'; //英文不一样的觉醒 + if (/^(?:zh)/.test(lang) && [46,47].includes(number)) number += '-zh'; //中文不一样的觉醒 + use.setAttribute("href",`images/icon-awoken.svg#awoken-${number}`); + break; + } + case 'type': { + if (/^(?:en|ko)/.test(lang) && [9,12].includes(number)) number += '-en'; //英文不一样的类型 + use.setAttribute("href",`images/icon-type.svg#type-${number}`); + break; + } + case 'awoken-count': { + use.setAttribute("href",`images/icon-awoken-count.svg#awoken-count-bg`); + const text = svg.appendChild(document.createElementNS(svgNS, 'text')); + const iconName = this.getAttribute('icon-name'); + svg.setAttribute("icon-name", iconName); + //awoken,latent,8-latent + //full,enable-assist-full,latent-full,8-latent,8-latent-full + + const full = this.getAttribute('full') != null; + const special = this.getAttribute('special') != null; + text.textContent = full ? '★' : number; + text.setAttribute("x", "50%"); + text.setAttribute("y", "50%"); + text.setAttribute("class", "number"); break; } - case 'type': case 'latent': case 'badge': case 'attr':