想做图标,但是很麻烦

This commit is contained in:
枫谷剑仙 2022-08-03 21:34:44 +08:00
parent 1425b173d8
commit f0ca5b6496
5 changed files with 191 additions and 61 deletions

View File

@ -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;
}

View File

@ -0,0 +1,62 @@
<svg id="图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 123 75">
<style>
.back {
fill: #FFFFD4;
}
.front {
fill: #F3DC69;
}
</style>
<defs>
<filter id="inset-shadow" x="-50%" y="-50%" width="200%" height="200%" name="inset-shadow">
<feComponentTransfer in="SourceAlpha">
<feFuncA tableValues="1 0" type="table"/>
</feComponentTransfer>
<feGaussianBlur stdDeviation="1" result="blur"/>
<feFlood flood-color="white" />
<feComposite in2="blur" operator="in"/>
<feOffset dx="0" dy="3"/>
<feComposite in2="SourceAlpha" operator="in" result="offsetblur-bottom"/>
<feFlood flood-color="black" />
<feComposite in2="blur" operator="in"/>
<feOffset dx="0" dy="-3"/>
<feComposite in2="SourceAlpha" operator="in" result="offsetblur-top"/>
<feMerge>
<feMergeNode in="SourceGraphic"/>
<feMergeNode in="offsetblur-bottom"/>
<feMergeNode in="offsetblur-top"/>
</feMerge>
</filter>
<filter id="out-shadow" x="-50%" y="-50%" width="200%" height="200%" name="inset-shadow">
<feComponentTransfer in="SourceAlpha">
<feFuncA tableValues="0 1" type="table"/>
</feComponentTransfer>
<feGaussianBlur stdDeviation="1" result="blur"/>
<feOffset dx="0" dy="3"/>
<feComposite in2="SourceAlpha" operator="out" result="offsetblur"/>
<feMerge>
<feMergeNode in="SourceGraphic"/>
<feMergeNode in="offsetblur"/>
</feMerge>
</filter>
</defs>
<symbol id="awoken-count-bg" viewBox="0 0 36 40">
<g>
<circle class="back" filter="url(#out-shadow)" cx="18" cy="16" r="16"/>
<circle class="front" filter="url(#inset-shadow)" cx="18" cy="16" r="13"/>
</g>
</symbol>
<image width="123" height="40" xlink:href="../../../CTFX/Desktop/%E6%9C%AA%E5%91%BD%E5%90%8D-3.png"/>
<g id="bg-enable-be-assist">
<circle class="back" filter="url(#out-shadow)" cx="23" cy="49" r="16"/>
<circle class="front" filter="url(#inset-shadow)" cx="23" cy="49" r="13"/>
</g>
<g id="bg-latent">
<circle fill="white" filter="url(#out-shadow)" cx="60" cy="49" r="16"/>
<circle fill="#378DE8" filter="url(#inset-shadow)" cx="60" cy="49" r="13"/>
</g>
<g id="bg-unable-be-assist">
<circle fill="white" filter="url(#out-shadow)" cx="103" cy="49" r="16"/>
<circle fill="#096E11" filter="url(#inset-shadow)" cx="103" cy="49" r="13"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -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);
const tasks = [
{directory: './awokens', idPre: 'awoken', svgFilename: '../icon-awoken.svg'},
{directory: './types', idPre: 'type', svgFilename: '../icon-type.svg'},
];
tasks.forEach(main);

View File

@ -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"
}
}

View File

@ -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':