mirror of
https://gitee.com/onvia/ccc-tnt-psd2ui
synced 2024-12-26 03:38:24 +00:00
增加 psd2ui 源码
This commit is contained in:
parent
38357d6d80
commit
9174aea6e1
296
psd2ui-tools/README.md
Normal file
296
psd2ui-tools/README.md
Normal file
@ -0,0 +1,296 @@
|
|||||||
|
## 属性
|
||||||
|
|
||||||
|
<a href="#Btn"> @Btn | @btn 按钮</a>
|
||||||
|
|
||||||
|
<a href="#ProgressBar"> @ProgressBar | @progressBar 进度条</a>
|
||||||
|
|
||||||
|
<a href="#Toggle"> @Toggle | @toggle 选项按钮</a>
|
||||||
|
|
||||||
|
<a href="#png9"> @.9 九宫格</a>
|
||||||
|
|
||||||
|
<a href="#ar"> @ar 锚点</a>
|
||||||
|
|
||||||
|
<a href="#size"> @size 尺寸</a>
|
||||||
|
|
||||||
|
<a href="#full"> @full 全屏</a>
|
||||||
|
|
||||||
|
<a href="#scale"> @scale 缩放</a>
|
||||||
|
|
||||||
|
<a href="#ignore"> @ignore | @ig 忽略导出图片和节点</a>
|
||||||
|
|
||||||
|
<a href="#ignorenode"> @ignorenode | @ignode 忽略导出节点</a>
|
||||||
|
|
||||||
|
<a href="#ignoreimg"> @ignoreimg | @igimg 忽略图片</a>
|
||||||
|
|
||||||
|
<a href="#img"> @img 图片选项</a>
|
||||||
|
|
||||||
|
<a href="#flip"> @flip 翻转图像</a>
|
||||||
|
|
||||||
|
<a href="#flipX"> @flipX 翻转图像 (flip 变种)</a>
|
||||||
|
|
||||||
|
<a href="#flipY"> @flipY 翻转图像 (flip 变种)</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 组件
|
||||||
|
|
||||||
|
<a id="Btn"></a>
|
||||||
|
```
|
||||||
|
@Btn || @btn
|
||||||
|
|
||||||
|
作用图层: 所有图层
|
||||||
|
```
|
||||||
|
|
||||||
|
<a id="ProgressBar"></a>
|
||||||
|
```
|
||||||
|
@ProgressBar || @progressBar
|
||||||
|
作用图层: 组图层
|
||||||
|
|
||||||
|
@bar
|
||||||
|
|
||||||
|
bar 为 ProgressBar 的属性,类型为 Sprite
|
||||||
|
作用图层: 图像图层
|
||||||
|
```
|
||||||
|
|
||||||
|
<a id="Toggle"></a>
|
||||||
|
```
|
||||||
|
@Toggle || @toggle
|
||||||
|
作用图层: 组图层
|
||||||
|
|
||||||
|
@check
|
||||||
|
|
||||||
|
check 为 Toggle 的属性,类型为 Sprite
|
||||||
|
作用图层: 图像图层
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Field
|
||||||
|
|
||||||
|
|
||||||
|
<a id="png9"></a>
|
||||||
|
```
|
||||||
|
@.9{l:0,r:0,b:0,t:0}
|
||||||
|
|
||||||
|
九宫格
|
||||||
|
作用图层: 图像图层
|
||||||
|
|
||||||
|
参数:
|
||||||
|
l = left
|
||||||
|
r = right
|
||||||
|
b = bottom
|
||||||
|
t = top
|
||||||
|
ps:
|
||||||
|
l r 只填写其中一项,则为对称
|
||||||
|
b t 同上
|
||||||
|
不填写则默认为 0
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
@ar{x:0,y:0}
|
||||||
|
|
||||||
|
锚点
|
||||||
|
作用图层: 所有图层
|
||||||
|
|
||||||
|
参数:
|
||||||
|
参数都为可选
|
||||||
|
不填写则默认为 0.5
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
<a id="size"></a>
|
||||||
|
```
|
||||||
|
@size{w:100,h:100}
|
||||||
|
|
||||||
|
节点尺寸 非图片尺寸
|
||||||
|
作用图层: 所有图层
|
||||||
|
|
||||||
|
参数:
|
||||||
|
w?: 宽
|
||||||
|
h?: 高
|
||||||
|
只对填写的参数生效,未填写的则为计算到的值
|
||||||
|
无参数不生效
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
<a id="full"></a>
|
||||||
|
```
|
||||||
|
@full
|
||||||
|
|
||||||
|
节点设置为全屏尺寸
|
||||||
|
作用图层: 组图层
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
<a id="scale"></a>
|
||||||
|
```
|
||||||
|
@scale{x:1,y:1}
|
||||||
|
|
||||||
|
节点缩放
|
||||||
|
作用图层: 所有图层
|
||||||
|
|
||||||
|
参数:
|
||||||
|
x?: x 方向
|
||||||
|
y?: y 方向
|
||||||
|
只对填写的参数生效,未填写的则为 1
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
<a id="ignore"></a>
|
||||||
|
```
|
||||||
|
@ignore
|
||||||
|
@ig
|
||||||
|
|
||||||
|
忽略导出图像和节点
|
||||||
|
作用图层: 所有图层
|
||||||
|
```
|
||||||
|
|
||||||
|
<a id="ignorenode"></a>
|
||||||
|
```
|
||||||
|
@ignorenode
|
||||||
|
@ignode
|
||||||
|
|
||||||
|
忽略导出节点
|
||||||
|
作用图层: 所有图层
|
||||||
|
```
|
||||||
|
|
||||||
|
<a id="ignoreimg"></a>
|
||||||
|
```
|
||||||
|
@ignoreimg
|
||||||
|
@igimg
|
||||||
|
|
||||||
|
忽略导出图像
|
||||||
|
作用图层: 图像图层
|
||||||
|
```
|
||||||
|
|
||||||
|
<a id="img"></a>
|
||||||
|
```
|
||||||
|
@img{name:string,id:number,bind:number}
|
||||||
|
|
||||||
|
定制图片
|
||||||
|
作用图层:图像图层
|
||||||
|
|
||||||
|
参数:
|
||||||
|
id: number 可选 当前文档中图片唯一 id
|
||||||
|
name: string 可选 导出的图片名
|
||||||
|
bind: number 可选 绑定 图像 id
|
||||||
|
```
|
||||||
|
|
||||||
|
<a id="flip"></a>
|
||||||
|
```
|
||||||
|
@flip{bind: 0, x: 0, y: 0}
|
||||||
|
|
||||||
|
镜像图像
|
||||||
|
作用图层:图像图层
|
||||||
|
|
||||||
|
参数:
|
||||||
|
bind: number 必选 被绑定的图片 需要用 @img{id:number} 做标记
|
||||||
|
x: 0 | 1, 可选, 1 为 进行 x 方向镜像
|
||||||
|
y: 0 | 1, 可选, 1 为 进行 y 方向镜像
|
||||||
|
x,y 都缺省时,默认 x 方向镜像
|
||||||
|
|
||||||
|
注意:
|
||||||
|
@flip 的图层不会导出图像
|
||||||
|
```
|
||||||
|
|
||||||
|
<a id="flipX"></a>
|
||||||
|
```
|
||||||
|
@flipX{bind: 0}
|
||||||
|
|
||||||
|
flip 的变种 x 方向镜像图像
|
||||||
|
作用图层:图像图层
|
||||||
|
|
||||||
|
参数:
|
||||||
|
bind: number 必选 被绑定的图片 需要用 @img{id:number} 做标记
|
||||||
|
|
||||||
|
注意:
|
||||||
|
@flipX 的图层不会导出图像
|
||||||
|
```
|
||||||
|
|
||||||
|
<a id="flipY"></a>
|
||||||
|
```
|
||||||
|
@flipY{bind: 0}
|
||||||
|
|
||||||
|
flip 的变种 y 方向镜像图像vv
|
||||||
|
作用图层:图像图层
|
||||||
|
|
||||||
|
参数:
|
||||||
|
bind: number 必选 被绑定的图片 需要用 @img{id:number} 做标记
|
||||||
|
|
||||||
|
注意:
|
||||||
|
@flipY 的图层不会导出图像
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 说明
|
||||||
|
多个字段可作用在同一个图层上,按需使用
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 例如
|
||||||
|
```
|
||||||
|
节点名@Btn@size{w:100,h:100}
|
||||||
|
|
||||||
|
节点名@ar{x:1,y:1}@full@img{name:bg}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 美术
|
||||||
|
- 智能图层 支持
|
||||||
|
- 图层样式
|
||||||
|
- 颜色叠加 图层图层不支持,文本图层支持
|
||||||
|
- 描边 文本图层支持
|
||||||
|
|
||||||
|
工具会把 画布外的图像也导出成图片,需要美术将 画布外 不需要导出的图像处理掉
|
||||||
|
需要美术将多个碎图组合的图像合并成智能图层使用
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 程序 配置
|
||||||
|
json 文件,key 为组件名,val 为 预制体参数
|
||||||
|
如下:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"cc.Label": {
|
||||||
|
"__type__": "e4f88adp3hERoJ48DZ2PSAl",
|
||||||
|
"_N$file":{
|
||||||
|
"__uuid__": "803c185c-9442-4b99-af1a-682f877539ab"
|
||||||
|
},
|
||||||
|
"_isSystemFontUsed": false,
|
||||||
|
"isFixNumber": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### 特殊配置
|
||||||
|
```
|
||||||
|
"textOffsetY":{
|
||||||
|
"default": -3,
|
||||||
|
"36": -3
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
textOffsetY label节点 Y 偏移
|
||||||
|
|
||||||
|
以字号为 key ,偏移值 为 val
|
||||||
|
如果没有配置 某些字号,则 使用 default 默认偏移值,如果没有配置 default, 偏移为 0
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### 已知bug
|
||||||
|
使用 强制导出图片选项时,输入为多个 psd 或含有多个 psd 文件的文件夹时,如果在不同 psd 含有相同 md5 的图像,则会在各自目录下生成相同 uuid 的图片
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### CHANGELOG
|
||||||
|
- 增加只导出 图片功能
|
||||||
|
|
||||||
|
- 移除 @mirror 中的参数: {id}
|
||||||
|
- 移除 @flipX & @flipY 中的参数: {id}
|
||||||
|
- 使用 @flip 替换 @mirror
|
||||||
|
- @img 增加 {id, bind} 参数
|
||||||
|
- 增加 @scale
|
9
psd2ui-tools/assets/cc/meta/CCPrefab.meta.v249
Normal file
9
psd2ui-tools/assets/cc/meta/CCPrefab.meta.v249
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.3.2",
|
||||||
|
"uuid": "$PREFB_UUID",
|
||||||
|
"importer": "prefab",
|
||||||
|
"optimizationPolicy": "AUTO",
|
||||||
|
"asyncLoadAssets": false,
|
||||||
|
"readonly": false,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
13
psd2ui-tools/assets/cc/meta/CCPrefab.meta.v342
Normal file
13
psd2ui-tools/assets/cc/meta/CCPrefab.meta.v342
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.1.35",
|
||||||
|
"importer": "prefab",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "$PREFB_UUID",
|
||||||
|
"files": [
|
||||||
|
".json"
|
||||||
|
],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {
|
||||||
|
"syncNodeName": "$NODE_NAME"
|
||||||
|
}
|
||||||
|
}
|
38
psd2ui-tools/assets/cc/meta/CCSpriteFrame.meta.v249
Normal file
38
psd2ui-tools/assets/cc/meta/CCSpriteFrame.meta.v249
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"ver": "2.3.7",
|
||||||
|
"uuid": "$SPRITE_FRAME_UUID",
|
||||||
|
"importer": "texture",
|
||||||
|
"type": "sprite",
|
||||||
|
"wrapMode": "clamp",
|
||||||
|
"filterMode": "bilinear",
|
||||||
|
"premultiplyAlpha": false,
|
||||||
|
"genMipmaps": false,
|
||||||
|
"packable": true,
|
||||||
|
"width": $WIDTH,
|
||||||
|
"height": $HEIGHT,
|
||||||
|
"platformSettings": {},
|
||||||
|
"subMetas": {
|
||||||
|
"$FILE_NAME": {
|
||||||
|
"ver": "1.0.6",
|
||||||
|
"uuid": "$TEXTURE_UUID",
|
||||||
|
"importer": "sprite-frame",
|
||||||
|
"rawTextureUuid": "$SPRITE_FRAME_UUID",
|
||||||
|
"trimType": "auto",
|
||||||
|
"trimThreshold": 1,
|
||||||
|
"rotated": false,
|
||||||
|
"offsetX": 0,
|
||||||
|
"offsetY": 0,
|
||||||
|
"trimX": 0,
|
||||||
|
"trimY": 0,
|
||||||
|
"width": $WIDTH,
|
||||||
|
"height": $HEIGHT,
|
||||||
|
"rawWidth": $WIDTH,
|
||||||
|
"rawHeight": $HEIGHT,
|
||||||
|
"borderTop": $BORDER_TOP,
|
||||||
|
"borderBottom": $BORDER_BOTTOM,
|
||||||
|
"borderLeft": $BORDER_LEFT,
|
||||||
|
"borderRight": $BORDER_RIGHT,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
74
psd2ui-tools/assets/cc/meta/CCSpriteFrame.meta.v342
Normal file
74
psd2ui-tools/assets/cc/meta/CCSpriteFrame.meta.v342
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.0.22",
|
||||||
|
"importer": "image",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "$TEXTURE_UUID",
|
||||||
|
"files": [
|
||||||
|
".png",
|
||||||
|
".json"
|
||||||
|
],
|
||||||
|
"subMetas": {
|
||||||
|
"6c48a": {
|
||||||
|
"importer": "texture",
|
||||||
|
"uuid": "$TEXTURE_UUID@6c48a",
|
||||||
|
"displayName": "$FILE_NAME",
|
||||||
|
"id": "6c48a",
|
||||||
|
"name": "texture",
|
||||||
|
"userData": {
|
||||||
|
"wrapModeS": "clamp-to-edge",
|
||||||
|
"wrapModeT": "clamp-to-edge",
|
||||||
|
"imageUuidOrDatabaseUri": "$TEXTURE_UUID",
|
||||||
|
"minfilter": "linear",
|
||||||
|
"magfilter": "linear",
|
||||||
|
"mipfilter": "none",
|
||||||
|
"anisotropy": 0,
|
||||||
|
"isUuid": true
|
||||||
|
},
|
||||||
|
"ver": "1.0.21",
|
||||||
|
"imported": true,
|
||||||
|
"files": [
|
||||||
|
".json"
|
||||||
|
],
|
||||||
|
"subMetas": {}
|
||||||
|
},
|
||||||
|
"f9941": {
|
||||||
|
"importer": "sprite-frame",
|
||||||
|
"uuid": "$TEXTURE_UUID@f9941",
|
||||||
|
"displayName": "$FILE_NAME",
|
||||||
|
"id": "f9941",
|
||||||
|
"name": "spriteFrame",
|
||||||
|
"userData": {
|
||||||
|
"trimType": "auto",
|
||||||
|
"trimThreshold": 1,
|
||||||
|
"rotated": false,
|
||||||
|
"offsetX": 0,
|
||||||
|
"offsetY": 0,
|
||||||
|
"trimX": 0,
|
||||||
|
"trimY": 0,
|
||||||
|
"width": $WIDTH,
|
||||||
|
"height": $HEIGHT,
|
||||||
|
"rawWidth": $WIDTH,
|
||||||
|
"rawHeight": $HEIGHT,
|
||||||
|
"borderTop": $BORDER_TOP,
|
||||||
|
"borderBottom": $BORDER_BOTTOM,
|
||||||
|
"borderLeft": $BORDER_LEFT,
|
||||||
|
"borderRight": $BORDER_RIGHT,
|
||||||
|
"packable": true,
|
||||||
|
"isUuid": true,
|
||||||
|
"imageUuidOrDatabaseUri": "$TEXTURE_UUID@6c48a",
|
||||||
|
"atlasUuid": ""
|
||||||
|
},
|
||||||
|
"ver": "1.0.9",
|
||||||
|
"imported": true,
|
||||||
|
"files": [
|
||||||
|
".json"
|
||||||
|
],
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"userData": {
|
||||||
|
"type": "sprite-frame",
|
||||||
|
"hasAlpha": true,
|
||||||
|
"redirect": "$TEXTURE_UUID@f9941"
|
||||||
|
}
|
||||||
|
}
|
1
psd2ui-tools/dist/index.js
vendored
Normal file
1
psd2ui-tools/dist/index.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2960
psd2ui-tools/package-lock.json
generated
Normal file
2960
psd2ui-tools/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
43
psd2ui-tools/package.json
Normal file
43
psd2ui-tools/package.json
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"name": "psd2ui",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc -b",
|
||||||
|
"watch": "tsc -w",
|
||||||
|
"rollup": "rollup -c",
|
||||||
|
"test": "node dist/index.js --input ./test/normal-test.psd --cache-remake --output ./out/",
|
||||||
|
"help": "node dist/index.js --h",
|
||||||
|
"test-dir": "node dist/index.js --input E:\\Git\\ccc-framework-3d\\tools\\psd2ui\\test --output E:\\Git\\ccc-framework-3d\\tools\\psd2ui\\out --cache E:\\Git\\ccc-framework-3d\\tools\\psd2ui\\cache\\cache.json",
|
||||||
|
"test-psd": "node dist/index.js --input E:\\Git\\ccc-framework-3d\\tools\\psd2ui\\test\\normal-test.psd --output E:\\Git\\ccc-framework-3d\\tools\\psd2ui\\out --cache E:\\Git\\ccc-framework-3d\\tools\\psd2ui\\cache\\cache.json",
|
||||||
|
"test-cache": "node dist/index.js --input E:\\Git\\ccc-framework-3d\\tools\\psd2ui\\test\\normal-test.psd --output E:\\Git\\ccc-framework-3d\\tools\\psd2ui\\out --cache E:\\Git\\ccc-framework-3d\\tools\\psd2ui\\cache\\cache.json --project-assets E:\\Demo\\CC249JSTest\\assets --cache-remake",
|
||||||
|
"test-dir-no-output": "node dist/index.js --input E:\\Git\\ccc-framework-3d\\tools\\psd2ui\\test --cache E:\\Git\\ccc-framework-3d\\tools\\psd2ui\\cache\\cache.json",
|
||||||
|
"test-psd-no-output": "node dist/index.js --input E:\\Git\\ccc-framework-3d\\tools\\psd2ui\\test\\normal-test.psd --cache E:\\Git\\ccc-framework-3d\\tools\\psd2ui\\cache\\cache.json",
|
||||||
|
"test-init": "node dist/index.js --init --project-assets E:\\Demo\\CC249JSTest\\assets --cache E:\\Git\\ccc-framework-3d\\tools\\psd2ui\\cache\\cache.json",
|
||||||
|
"test-png9": "ts-node src/index.ts --engine-version v342 --pinyin --input ./test/png9.psd --output ./out/",
|
||||||
|
"test-png9-2": "node dist/index.js --engine-version v342 --pinyin --input ./test/png9.psd --output ./out/"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"ag-psd": "^15.0.0",
|
||||||
|
"canvas": "^2.10.2",
|
||||||
|
"fs-extra": "^10.1.0",
|
||||||
|
"minimist": "^1.2.7",
|
||||||
|
"pinyin-pro": "^3.16.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@rollup/plugin-commonjs": "^25.0.3",
|
||||||
|
"@rollup/plugin-json": "^6.0.0",
|
||||||
|
"@rollup/plugin-node-resolve": "^15.1.0",
|
||||||
|
"@rollup/plugin-typescript": "^11.1.2",
|
||||||
|
"@types/fs-extra": "^9.0.13",
|
||||||
|
"@types/node": "^18.11.9",
|
||||||
|
"cross-env": "^7.0.3",
|
||||||
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
|
"rollup-plugin-typescript2": "^0.35.0",
|
||||||
|
"ts-node": "^10.9.1",
|
||||||
|
"tslib": "^2.6.0"
|
||||||
|
}
|
||||||
|
}
|
28
psd2ui-tools/rollup.config.js
Normal file
28
psd2ui-tools/rollup.config.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
|
||||||
|
let path = require("path");
|
||||||
|
const commonjs = require('@rollup/plugin-commonjs')
|
||||||
|
const typescript = require('rollup-plugin-typescript2')
|
||||||
|
let resolve = require('@rollup/plugin-node-resolve').default;
|
||||||
|
let json = require("@rollup/plugin-json").default;
|
||||||
|
let terser = require("rollup-plugin-terser").terser;
|
||||||
|
|
||||||
|
|
||||||
|
const override = { compilerOptions: { module: 'ESNext' } }
|
||||||
|
module.exports = {
|
||||||
|
input: "./src/export.ts",
|
||||||
|
|
||||||
|
output: {
|
||||||
|
file: path.resolve(__dirname, "./dist/index.js"),
|
||||||
|
sourcemap: false,
|
||||||
|
format: "umd",
|
||||||
|
},
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
typescript({ tsconfig: './tsconfig.json', tsconfigOverride: override }),
|
||||||
|
json(),
|
||||||
|
// resolve(),
|
||||||
|
commonjs(),
|
||||||
|
terser()
|
||||||
|
]
|
||||||
|
}
|
6
psd2ui-tools/src/EditorVersion.ts
Normal file
6
psd2ui-tools/src/EditorVersion.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
export enum EditorVersion{
|
||||||
|
all,
|
||||||
|
v249,
|
||||||
|
v342,
|
||||||
|
}
|
171
psd2ui-tools/src/ExportImageMgr.ts
Normal file
171
psd2ui-tools/src/ExportImageMgr.ts
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import 'ag-psd/initialize-canvas'; // only needed for reading image data and thumbnails
|
||||||
|
import * as psd from 'ag-psd';
|
||||||
|
import fs from 'fs-extra';
|
||||||
|
import path from 'path';
|
||||||
|
import { imageMgr } from './assets-manager/ImageMgr';
|
||||||
|
import { fileUtils } from './utils/FileUtils';
|
||||||
|
import { parser } from './Parser';
|
||||||
|
import { PsdDocument } from './psd/PsdDocument';
|
||||||
|
import { PsdLayer } from './psd/PsdLayer';
|
||||||
|
import { PsdGroup } from './psd/PsdGroup';
|
||||||
|
import { PsdText } from './psd/PsdText';
|
||||||
|
import { Color } from './values/Color';
|
||||||
|
|
||||||
|
interface TextObject{
|
||||||
|
text: string;
|
||||||
|
fontSize: number;
|
||||||
|
color: string;
|
||||||
|
outlineWidth?: number;
|
||||||
|
outlineColor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExportImageMgr{
|
||||||
|
|
||||||
|
textObjects: TextObject[] = [];
|
||||||
|
|
||||||
|
test(){
|
||||||
|
const outDir = path.join(__dirname, "..", "out");
|
||||||
|
let psdPath = "./test-img-only/境界奖励-优化.psd";
|
||||||
|
this.parsePsd(psdPath, outDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
async exec(args) {
|
||||||
|
// 检查参数
|
||||||
|
if (!this.checkArgs(args)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断输入是文件夹还是文件
|
||||||
|
let stat = fs.lstatSync(args.input);
|
||||||
|
let isDirectory = stat.isDirectory();
|
||||||
|
if (isDirectory) {
|
||||||
|
if (!args.output) {
|
||||||
|
args.output = path.join(args.input, "psd2ui")
|
||||||
|
}
|
||||||
|
this.parsePsdDir(args.input, args.output);
|
||||||
|
} else {
|
||||||
|
if (!args.output) {
|
||||||
|
let input_dir = path.dirname(args.input);
|
||||||
|
args.output = path.join(input_dir, "psd2ui")
|
||||||
|
}
|
||||||
|
this.parsePsd(args.input, args.output);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查参数
|
||||||
|
checkArgs(args) {
|
||||||
|
|
||||||
|
if (!args.input) {
|
||||||
|
console.error(`请设置 --input`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(args.input)) {
|
||||||
|
console.error(`输入路径不存在: ${args.input}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async parsePsdDir(dir: string, outDir: string) {
|
||||||
|
// 清空目录
|
||||||
|
fs.emptyDirSync(outDir);
|
||||||
|
|
||||||
|
let psds = fileUtils.filterFile(dir, (fileName) => {
|
||||||
|
let extname = path.extname(fileName);
|
||||||
|
if (extname == ".psd") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let i = 0; i < psds.length; i++) {
|
||||||
|
const element = psds[i];
|
||||||
|
await this.parsePsd(element, outDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async parsePsd(psdPath: string, outDir: string) {
|
||||||
|
// 每开始一个新的 psd 清理掉上一个 psd 的图
|
||||||
|
imageMgr.clear();
|
||||||
|
this.textObjects.length = 0;
|
||||||
|
console.log(`=========================================`);
|
||||||
|
|
||||||
|
console.log(`处理 ${psdPath} 文件`);
|
||||||
|
|
||||||
|
let psdName = path.basename(psdPath, ".psd");
|
||||||
|
let buffer = fs.readFileSync(psdPath);
|
||||||
|
const psdFile = psd.readPsd(buffer)
|
||||||
|
let psdRoot = parser.parseLayer(psdFile) as PsdDocument;
|
||||||
|
psdRoot.name = psdName;
|
||||||
|
let prefabDir = path.join(outDir, psdName);
|
||||||
|
let textureDir = path.join(prefabDir, "textures");
|
||||||
|
fs.mkdirsSync(prefabDir); // 创建预制体根目录
|
||||||
|
fs.emptyDirSync(prefabDir);
|
||||||
|
fs.mkdirsSync(textureDir); //创建 图片目录
|
||||||
|
await this.saveImage(textureDir);
|
||||||
|
await this.saveTextFile(psdRoot,prefabDir);
|
||||||
|
console.log(`psd2ui ${psdPath} 处理完成`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
saveImage(out: string) {
|
||||||
|
|
||||||
|
let images = imageMgr.getAllImage();
|
||||||
|
let idx = 0;
|
||||||
|
images.forEach((psdImage, k) => {
|
||||||
|
// 查找镜像
|
||||||
|
let _layer = imageMgr.getSerialNumberImage(psdImage);
|
||||||
|
let name = `${_layer.imgName}_${idx}`
|
||||||
|
console.log(`保存图片 [${_layer.imgName}] 重命名为 [${name}] md5: ${_layer.md5}`);
|
||||||
|
let fullpath = path.join(out, `${name}.png`);
|
||||||
|
fs.writeFileSync(fullpath, _layer.imgBuffer);
|
||||||
|
idx++;
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
saveTextFile(psdRoot: PsdDocument,out: string){
|
||||||
|
this.scanText(psdRoot,psdRoot);
|
||||||
|
let textContent = JSON.stringify(this.textObjects,null,2);
|
||||||
|
let fullpath = path.join(out, `text.txt`);
|
||||||
|
fs.writeFileSync(fullpath, textContent,{encoding: "utf-8"});
|
||||||
|
}
|
||||||
|
|
||||||
|
scanText(layer: PsdLayer, psdRoot: PsdDocument) {
|
||||||
|
if (layer instanceof PsdGroup) {
|
||||||
|
for (let i = 0; i < layer.children.length; i++) {
|
||||||
|
const childLayer = layer.children[i];
|
||||||
|
this.scanText(childLayer, psdRoot);
|
||||||
|
}
|
||||||
|
} else if (layer instanceof PsdText) {
|
||||||
|
let textObj: TextObject = {
|
||||||
|
text: layer.text,
|
||||||
|
fontSize: layer.fontSize,
|
||||||
|
color: `#${layer.color.toHEX()}`
|
||||||
|
};
|
||||||
|
// 有描边
|
||||||
|
if (layer.outline) {
|
||||||
|
textObj.outlineWidth = layer.outline.width;
|
||||||
|
textObj.outlineColor = `#${layer.outline.color.toHEX()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.textObjects.push(textObj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static _instance:ExportImageMgr = null
|
||||||
|
public static getInstance(): ExportImageMgr{
|
||||||
|
if(!this._instance){
|
||||||
|
this._instance = new ExportImageMgr();
|
||||||
|
}
|
||||||
|
return this._instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export let exportImageMgr = ExportImageMgr.getInstance();
|
507
psd2ui-tools/src/Main.ts
Normal file
507
psd2ui-tools/src/Main.ts
Normal file
@ -0,0 +1,507 @@
|
|||||||
|
|
||||||
|
|
||||||
|
//ag-psd 使用 参考 https://github.com/Agamnentzar/ag-psd/blob/HEAD/README_PSD.md
|
||||||
|
|
||||||
|
import 'ag-psd/initialize-canvas'; // only needed for reading image data and thumbnails
|
||||||
|
import * as psd from 'ag-psd';
|
||||||
|
import fs from 'fs-extra';
|
||||||
|
import path from 'path';
|
||||||
|
import { parser } from './Parser';
|
||||||
|
import { PsdDocument } from './psd/PsdDocument';
|
||||||
|
import { PsdLayer } from './psd/PsdLayer';
|
||||||
|
import { LayerType } from './psd/LayerType';
|
||||||
|
import { PsdGroup } from './psd/PsdGroup';
|
||||||
|
import { CCNode } from './engine/cc/CCNode';
|
||||||
|
import { PsdImage } from './psd/PsdImage';
|
||||||
|
import { PsdText } from './psd/PsdText';
|
||||||
|
import { CCSprite } from './engine/cc/CCSprite';
|
||||||
|
import { CCPrefabInfo } from './engine/cc/CCPrefabInfo';
|
||||||
|
import { CCPrefab } from './engine/cc/CCPrefab';
|
||||||
|
import { CCSize } from './engine/cc/values/CCSize';
|
||||||
|
import { CCVec2 } from './engine/cc/values/CCVec2';
|
||||||
|
import { CCComponent } from './engine/cc/CCComponent';
|
||||||
|
import { CCLabel } from './engine/cc/CCLabel';
|
||||||
|
import { CCLabelOutline } from './engine/cc/CCLabelOutline';
|
||||||
|
import { imageCacheMgr } from './assets-manager/ImageCacheMgr';
|
||||||
|
import { EditorVersion } from './EditorVersion';
|
||||||
|
import { Config, config } from './config';
|
||||||
|
import { fileUtils } from './utils/FileUtils';
|
||||||
|
import { imageMgr } from './assets-manager/ImageMgr';
|
||||||
|
import { exportImageMgr } from './ExportImageMgr';
|
||||||
|
import { CCUIOpacity } from './engine/cc/CCUIOpacity';
|
||||||
|
import { CCUITransform } from './engine/cc/CCUITransform';
|
||||||
|
import { CCVec3 } from './engine/cc/values/CCVec3';
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 执行流程
|
||||||
|
* - 首次运行,先读取项目文件夹下所有图片资源,进行 md5 缓存
|
||||||
|
*
|
||||||
|
* - 加载缓存文件
|
||||||
|
* - 处理 psd
|
||||||
|
* - 通过 md5 判断是否已经存在资源,如果存在, 则不再导出,预制体中使用已存在的资源的 uuid
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
console.log(`当前目录: `, __dirname);
|
||||||
|
|
||||||
|
|
||||||
|
export class Main {
|
||||||
|
|
||||||
|
spriteFrameMetaContent: string = "";
|
||||||
|
prefabMetaContent: string = "";
|
||||||
|
psdConfig: Config = null;
|
||||||
|
|
||||||
|
// 强制导出图片
|
||||||
|
isForceImg = false;
|
||||||
|
|
||||||
|
async test() {
|
||||||
|
console.log(`Main-> test`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 首先加载 meta 模板
|
||||||
|
async loadMetaTemplete() {
|
||||||
|
this.spriteFrameMetaContent = fs.readFileSync(path.join(__dirname, `../assets/cc/meta/CCSpriteFrame.meta.${EditorVersion[config.editorVersion]}`), "utf-8");
|
||||||
|
this.prefabMetaContent = fs.readFileSync(path.join(__dirname, `../assets/cc/meta/CCPrefab.meta.${EditorVersion[config.editorVersion]}`), "utf-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载配置
|
||||||
|
async loadPsdConfig(filepath) {
|
||||||
|
if (!fs.existsSync(filepath)) {
|
||||||
|
console.log(`Main-> 配置 ${filepath} 不存在`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let psdConfig = fs.readFileSync(filepath, "utf-8");
|
||||||
|
this.psdConfig = JSON.parse(psdConfig);
|
||||||
|
|
||||||
|
// 合并配置
|
||||||
|
for (const key in this.psdConfig) {
|
||||||
|
if (key in config) {
|
||||||
|
if (typeof this.psdConfig[key] === 'object') {
|
||||||
|
config[key] = Object.assign({}, config[key], this.psdConfig[key]);
|
||||||
|
} else {
|
||||||
|
config[key] = this.psdConfig[key] || config[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async exec(args) {
|
||||||
|
args = mergeAlias(args);
|
||||||
|
if (args.help) {
|
||||||
|
console.log(`help:\n`, config.help);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只导出图片
|
||||||
|
if (args["img-only"]) {
|
||||||
|
exportImageMgr.exec(args);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let writeCache = async () => {
|
||||||
|
// 写入缓存
|
||||||
|
if (args.cache) {
|
||||||
|
fs.mkdirsSync(path.dirname(args.cache));
|
||||||
|
await imageCacheMgr.saveImageMap(args.cache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 设置引擎版本
|
||||||
|
if (args["engine-version"]) {
|
||||||
|
config.editorVersion = EditorVersion[args["engine-version"] as string];
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Main-> 数据版本 ${EditorVersion[config.editorVersion]}`);
|
||||||
|
|
||||||
|
|
||||||
|
if (args.init && (!args["project-assets"] || !args.cache)) {
|
||||||
|
console.log(`psd2ui --init 无法处理,请设置 --project-assets`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建缓存文件
|
||||||
|
if (args.cache && !fs.existsSync(args.cache)) {
|
||||||
|
writeCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在没有缓存文件或者 指定重新缓存的时候,读取项目资源
|
||||||
|
if (args["project-assets"] && (args["cache-remake"] || args.init)) {
|
||||||
|
await imageCacheMgr.loadImages(args["project-assets"]);
|
||||||
|
// 先写入一次
|
||||||
|
writeCache();
|
||||||
|
if (args.init) {
|
||||||
|
console.log(`psd2ui 缓存完成`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查参数
|
||||||
|
if (!this.checkArgs(args)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.cache) {
|
||||||
|
await imageCacheMgr.initWithPath(args.cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载 meta 文件模板
|
||||||
|
await this.loadMetaTemplete();
|
||||||
|
|
||||||
|
if (args.config) {
|
||||||
|
await this.loadPsdConfig(args.config);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isForceImg = !!args["force-img"];
|
||||||
|
|
||||||
|
PsdLayer.isPinyin = args.pinyin;
|
||||||
|
|
||||||
|
// 判断输入是文件夹还是文件
|
||||||
|
let stat = fs.lstatSync(args.input);
|
||||||
|
let isDirectory = stat.isDirectory();
|
||||||
|
if (isDirectory) {
|
||||||
|
if (!args.output) {
|
||||||
|
args.output = path.join(args.input, "psd2ui")
|
||||||
|
}
|
||||||
|
this.parsePsdDir(args.input, args.output);
|
||||||
|
} else {
|
||||||
|
if (!args.output) {
|
||||||
|
let input_dir = path.dirname(args.input);
|
||||||
|
args.output = path.join(input_dir, "psd2ui")
|
||||||
|
}
|
||||||
|
this.parsePsd(args.input, args.output);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入缓存
|
||||||
|
await writeCache();
|
||||||
|
|
||||||
|
console.log(`psd2ui 导出完成`);
|
||||||
|
}
|
||||||
|
// 检查参数
|
||||||
|
checkArgs(args) {
|
||||||
|
|
||||||
|
if (!args.input) {
|
||||||
|
console.error(`请设置 --input`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs.existsSync(args.input)) {
|
||||||
|
console.error(`输入路径不存在: ${args.input}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args["engine-version"]) {
|
||||||
|
let editorVersion = EditorVersion[args["engine-version"] as string];
|
||||||
|
switch (editorVersion) {
|
||||||
|
case EditorVersion.v249:
|
||||||
|
case EditorVersion.v342:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log(`暂未实现该引擎版本 ${args["engine-version"]}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async parsePsdDir(dir: string, outDir: string) {
|
||||||
|
// 清空目录
|
||||||
|
// fs.emptyDirSync(outDir);
|
||||||
|
|
||||||
|
let psds = fileUtils.filterFile(dir, (fileName) => {
|
||||||
|
let extname = path.extname(fileName);
|
||||||
|
if (extname == ".psd") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let i = 0; i < psds.length; i++) {
|
||||||
|
const element = psds[i];
|
||||||
|
await this.parsePsd(element, outDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async parsePsd(psdPath: string, outDir: string) {
|
||||||
|
// 每开始一个新的 psd 清理掉上一个 psd 的图
|
||||||
|
imageMgr.clear();
|
||||||
|
console.log(`=========================================`);
|
||||||
|
|
||||||
|
console.log(`处理 ${psdPath} 文件`);
|
||||||
|
|
||||||
|
let psdName = path.basename(psdPath, ".psd");
|
||||||
|
let buffer = fs.readFileSync(psdPath);
|
||||||
|
const psdFile = psd.readPsd(buffer)
|
||||||
|
let psdRoot = parser.parseLayer(psdFile) as PsdDocument;
|
||||||
|
psdRoot.name = psdName;
|
||||||
|
let prefabDir = path.join(outDir, psdName);
|
||||||
|
let textureDir = path.join(prefabDir, "textures");
|
||||||
|
fs.mkdirsSync(prefabDir); // 创建预制体根目录
|
||||||
|
// fs.emptyDirSync(prefabDir);
|
||||||
|
fs.mkdirsSync(textureDir); //创建 图片目录
|
||||||
|
await this.saveImage(textureDir);
|
||||||
|
await this.buildPrefab(psdRoot);
|
||||||
|
await this.savePrefab(psdRoot, prefabDir);
|
||||||
|
console.log(`psd2ui ${psdPath} 处理完成`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
buildPrefab(psdRoot: PsdDocument) {
|
||||||
|
let prefab = new CCPrefab();
|
||||||
|
psdRoot.pushObject(prefab);
|
||||||
|
let data = this.createCCNode(psdRoot, psdRoot);
|
||||||
|
prefab.data = { __id__: data.idx };
|
||||||
|
// 后期处理
|
||||||
|
this.postUIObject(psdRoot, psdRoot);
|
||||||
|
|
||||||
|
}
|
||||||
|
createCCNode(layer: PsdLayer, psdRoot: PsdDocument) {
|
||||||
|
let node = new CCNode(psdRoot);
|
||||||
|
layer.uiObject = node;
|
||||||
|
node._name = layer.name; //layer.attr?.name || layer.name;
|
||||||
|
node._active = !layer.hidden;
|
||||||
|
node._opacity = layer.opacity;
|
||||||
|
|
||||||
|
if (config.editorVersion >= EditorVersion.v342) {
|
||||||
|
// 3.4.x
|
||||||
|
if (layer.opacity !== 255) {
|
||||||
|
let uiOpacity = new CCUIOpacity();
|
||||||
|
uiOpacity._opacity = layer.opacity;
|
||||||
|
uiOpacity.updateWithLayer(layer);
|
||||||
|
node.addComponent(uiOpacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 劫持尺寸设置,使用 psd 中配置的尺寸,这里不对原数据进行修改
|
||||||
|
let size = new CCSize(layer.size.width, layer.size.height);
|
||||||
|
if (layer.attr?.comps.size) {
|
||||||
|
let _attrSize = layer.attr.comps.size;
|
||||||
|
size.width = _attrSize.w ?? size.width;
|
||||||
|
size.height = _attrSize.h ?? size.height;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对缩放进行处理
|
||||||
|
size.width = Math.round(Math.abs(size.width / layer.scale.x));
|
||||||
|
size.height = Math.round(Math.abs(size.height / layer.scale.y));
|
||||||
|
|
||||||
|
// 配置的位置 Y 偏移
|
||||||
|
let offsetY = 0;
|
||||||
|
if (layer instanceof PsdText) {
|
||||||
|
offsetY = layer.offsetY;
|
||||||
|
}
|
||||||
|
|
||||||
|
node._contentSize = size;
|
||||||
|
// 更新一下位置 // 根据图层名字设置 锚点,位置, 因为没有对原始数据进行修改,所以这里不考虑 缩放
|
||||||
|
layer.updatePositionWithAR();
|
||||||
|
|
||||||
|
// 2.4.9
|
||||||
|
node._trs.setPosition(layer.position.x, layer.position.y + offsetY, 0);
|
||||||
|
node._trs.setRotation(0, 0, 0, 1);
|
||||||
|
node._trs.setScale(layer.scale.x, layer.scale.y, layer.scale.z);
|
||||||
|
node._anchorPoint = new CCVec2(layer.anchorPoint.x, layer.anchorPoint.y);
|
||||||
|
|
||||||
|
|
||||||
|
if (config.editorVersion >= EditorVersion.v342) {
|
||||||
|
// 3.4.x
|
||||||
|
node._lpos = new CCVec3(layer.position.x, layer.position.y + offsetY, 0);
|
||||||
|
node._lrot = new CCVec3(0, 0, 0);
|
||||||
|
node._lscale = new CCVec3(layer.scale.x, layer.scale.y, layer.scale.z);
|
||||||
|
node._euler = new CCVec3();
|
||||||
|
|
||||||
|
// 3.4.x
|
||||||
|
let uiTransform = new CCUITransform();
|
||||||
|
uiTransform._contentSize = size;
|
||||||
|
uiTransform._anchorPoint = node._anchorPoint;
|
||||||
|
uiTransform.updateWithLayer(layer);
|
||||||
|
node.addComponent(uiTransform);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
if (layer instanceof PsdGroup) {
|
||||||
|
for (let i = 0; i < layer.children.length; i++) {
|
||||||
|
const childLayer = layer.children[i];
|
||||||
|
let childNode = this.createCCNode(childLayer, psdRoot);
|
||||||
|
childNode && node.addChild(childNode);
|
||||||
|
}
|
||||||
|
} else if (layer instanceof PsdImage) {
|
||||||
|
let sprite = new CCSprite();
|
||||||
|
|
||||||
|
node.addComponent(sprite);
|
||||||
|
sprite._materials.push({
|
||||||
|
__uuid__: config.SpriteFrame_Material
|
||||||
|
});
|
||||||
|
sprite.updateWithLayer(layer);
|
||||||
|
|
||||||
|
if (layer.isIgnore()) {
|
||||||
|
// 忽略图像
|
||||||
|
} else {
|
||||||
|
// 查找绑定的图像
|
||||||
|
let _layer = imageMgr.getSerialNumberImage(layer);
|
||||||
|
// 使用已缓存的 图片 的 uuid
|
||||||
|
let imageWarp = imageCacheMgr.get(_layer.md5);
|
||||||
|
sprite.setSpriteFrame(imageWarp ? imageWarp.textureUuid : _layer.textureUuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.applyConfig(sprite);
|
||||||
|
} else if (layer instanceof PsdText) {
|
||||||
|
let label = new CCLabel();
|
||||||
|
node.addComponent(label);
|
||||||
|
node._color.set(layer.color);
|
||||||
|
label._color.set(layer.color);
|
||||||
|
label._materials.push({
|
||||||
|
__uuid__: config.Label_Material
|
||||||
|
});
|
||||||
|
label.updateWithLayer(layer);
|
||||||
|
this.applyConfig(label);
|
||||||
|
// 有描边
|
||||||
|
if (layer.outline) {
|
||||||
|
let labelOutline = new CCLabelOutline();
|
||||||
|
node.addComponent(labelOutline);
|
||||||
|
labelOutline.updateWithLayer(layer);
|
||||||
|
this.applyConfig(labelOutline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Button / Toggle / ProgressBar
|
||||||
|
if (layer.attr) {
|
||||||
|
for (const key in layer.attr.comps) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(layer.attr.comps, key) && layer.attr.comps[key]) {
|
||||||
|
let ctor = config.CompMappings[key] as any;
|
||||||
|
if (ctor) {
|
||||||
|
let comp: CCComponent = new ctor();
|
||||||
|
node.addComponent(comp);
|
||||||
|
comp.updateWithLayer(layer);
|
||||||
|
this.applyConfig(comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.createPrefabInfo(layer, psdRoot);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
createPrefabInfo(layer: PsdLayer, psdRoot: PsdDocument) {
|
||||||
|
let node = layer.uiObject as CCNode;
|
||||||
|
let prefabInfo = new CCPrefabInfo();
|
||||||
|
let idx = psdRoot.pushObject(prefabInfo);
|
||||||
|
node._prefab = { __id__: idx };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 后处理
|
||||||
|
postUIObject(layer: PsdLayer, psdRoot: PsdDocument) {
|
||||||
|
}
|
||||||
|
|
||||||
|
saveImage(out: string) {
|
||||||
|
|
||||||
|
let images = imageMgr.getAllImage();
|
||||||
|
images.forEach((psdImage, k) => {
|
||||||
|
// 查找镜像
|
||||||
|
let _layer = imageMgr.getSerialNumberImage(psdImage);
|
||||||
|
|
||||||
|
// 查找已缓存的相同图像
|
||||||
|
let imageWarp = imageCacheMgr.get(_layer.md5);
|
||||||
|
|
||||||
|
// 不是强制导出的话,判断是否已经导出过
|
||||||
|
if (!this.isForceImg) {
|
||||||
|
// 判断是否已经导出过相同 md5 的资源,不再重复导出
|
||||||
|
if (imageWarp?.isOutput) {
|
||||||
|
console.log(`已有相同资源,不再导出 [${psdImage.imgName}] md5: ${psdImage.md5}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(`保存图片 [${_layer.imgName}] md5: ${_layer.md5}`);
|
||||||
|
imageWarp && (imageWarp.isOutput = true);
|
||||||
|
let fullpath = path.join(out, `${_layer.imgName}.png`);
|
||||||
|
fs.writeFileSync(fullpath, _layer.imgBuffer);
|
||||||
|
this.saveImageMeta(_layer, fullpath);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
saveImageMeta(layer: PsdImage, fullpath: string) {
|
||||||
|
let _layer = imageMgr.getSerialNumberImage(layer);
|
||||||
|
let imageWarp = imageCacheMgr.get(_layer.md5);
|
||||||
|
if (!imageWarp) {
|
||||||
|
imageWarp = _layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.4.9 =-> SPRITE_FRAME_UUID
|
||||||
|
let meta = this.spriteFrameMetaContent.replace(/\$SPRITE_FRAME_UUID/g, imageWarp.uuid)
|
||||||
|
|
||||||
|
meta = meta.replace(/\$TEXTURE_UUID/g, imageWarp.textureUuid);
|
||||||
|
meta = meta.replace(/\$FILE_NAME/g, _layer.imgName);
|
||||||
|
meta = meta.replace(/\$WIDTH/g, _layer.textureSize.width as any);
|
||||||
|
meta = meta.replace(/\$HEIGHT/g, _layer.textureSize.height as any);
|
||||||
|
|
||||||
|
let s9 = _layer.s9 || {
|
||||||
|
b: 0, t: 0, l: 0, r: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
meta = meta.replace(/\$BORDER_TOP/g, s9.t as any);
|
||||||
|
meta = meta.replace(/\$BORDER_BOTTOM/g, s9.b as any);
|
||||||
|
meta = meta.replace(/\$BORDER_LEFT/g, s9.l as any);
|
||||||
|
meta = meta.replace(/\$BORDER_RIGHT/g, s9.r as any);
|
||||||
|
|
||||||
|
fs.writeFileSync(fullpath + `.meta`, meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
savePrefab(psdDoc: PsdDocument, out) {
|
||||||
|
let fullpath = path.join(out, `${psdDoc.name}.prefab`);
|
||||||
|
fs.writeFileSync(fullpath, JSON.stringify(psdDoc.objectArray, null, 2));
|
||||||
|
this.savePrefabMeta(psdDoc, fullpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
savePrefabMeta(psdDoc: PsdDocument, fullpath) {
|
||||||
|
let meta = this.prefabMetaContent.replace(/\$PREFB_UUID/g, psdDoc.uuid)
|
||||||
|
fs.writeFileSync(fullpath + `.meta`, meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
applyConfig(comp: CCComponent) {
|
||||||
|
if (!this.psdConfig) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (comp.__type__ in this.psdConfig) {
|
||||||
|
let compConfig = this.psdConfig[comp.__type__];
|
||||||
|
for (const key in compConfig) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(compConfig, key)) {
|
||||||
|
const element = compConfig[key];
|
||||||
|
comp[key] = element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 合并别名 */
|
||||||
|
function mergeAlias(args) {
|
||||||
|
// 如果是 json 对象参数
|
||||||
|
if (args.json) {
|
||||||
|
let base64 = args.json;
|
||||||
|
// 解码 json
|
||||||
|
args = JSON.parse(Buffer.from(base64, "base64").toString());
|
||||||
|
|
||||||
|
// // 编码
|
||||||
|
// let jsonContent = JSON.stringify(args);
|
||||||
|
// let base64 = Buffer.from(jsonContent).toString("base64");
|
||||||
|
}
|
||||||
|
args.help = args.help || args.h;
|
||||||
|
args.input = args.input || args.in;
|
||||||
|
args.output = args.output || args.out;
|
||||||
|
args["engine-version"] = args["engine-version"] || args.ev;
|
||||||
|
args["project-assets"] = args["project-assets"] || args.p;
|
||||||
|
args["cache-remake"] = args["cache-remake"] || args.crm;
|
||||||
|
args["force-img"] = args["force-img"] || args.fimg;
|
||||||
|
args.pinyin = args.pinyin || args.py;
|
||||||
|
args.cache = args.cache || args.c;
|
||||||
|
args.init = args.init || args.i;
|
||||||
|
args.config = args.config;
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
107
psd2ui-tools/src/Parser.ts
Normal file
107
psd2ui-tools/src/Parser.ts
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import { imageCacheMgr } from "./assets-manager/ImageCacheMgr";
|
||||||
|
import { imageMgr } from "./assets-manager/ImageMgr";
|
||||||
|
import { LayerType } from "./psd/LayerType";
|
||||||
|
import { PsdDocument } from "./psd/PsdDocument";
|
||||||
|
import { PsdGroup } from "./psd/PsdGroup";
|
||||||
|
import { PsdImage } from "./psd/PsdImage";
|
||||||
|
import { PsdAttr, PsdLayer } from "./psd/PsdLayer";
|
||||||
|
import { PsdText } from "./psd/PsdText";
|
||||||
|
import { PsdLayerSource } from "./_declare";
|
||||||
|
|
||||||
|
export class Parser {
|
||||||
|
|
||||||
|
/** 解析图层类型 */
|
||||||
|
parseLayerType(source: PsdLayerSource) {
|
||||||
|
if ("children" in source) {
|
||||||
|
if ("width" in source && "height" in source) {
|
||||||
|
// Document
|
||||||
|
return LayerType.Doc;
|
||||||
|
} else {
|
||||||
|
// Group
|
||||||
|
return LayerType.Group;
|
||||||
|
}
|
||||||
|
} else if ("text" in source) {
|
||||||
|
// Text
|
||||||
|
return LayerType.Text;
|
||||||
|
}
|
||||||
|
// else if ('placedLayer' in layer) {
|
||||||
|
// // 智能对象
|
||||||
|
// }
|
||||||
|
return LayerType.Image;
|
||||||
|
}
|
||||||
|
parseLayer(source: any, parent?: PsdGroup, rootDoc?: PsdDocument) {
|
||||||
|
let layer: PsdLayer = null;
|
||||||
|
let layerType = this.parseLayerType(source);
|
||||||
|
switch (layerType) {
|
||||||
|
case LayerType.Doc:
|
||||||
|
case LayerType.Group: {
|
||||||
|
|
||||||
|
let group: PsdGroup = null
|
||||||
|
// Group
|
||||||
|
if (layerType == LayerType.Group) {
|
||||||
|
group = new PsdGroup(source, parent, rootDoc);
|
||||||
|
if(group.attr.comps.ignorenode || group.attr.comps.ignore){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Document
|
||||||
|
group = new PsdDocument(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < source.children.length; i++) {
|
||||||
|
const childSource = source.children[i];
|
||||||
|
let child = this.parseLayer(childSource, group, rootDoc || group as PsdDocument);
|
||||||
|
if (child) {
|
||||||
|
if (!child.attr.comps.ignorenode && !child.attr.comps.ignore) {
|
||||||
|
// 没有进行忽略节点的时候才放入列表
|
||||||
|
group.children.push(child);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error(`图层解析错误`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
layer = group;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LayerType.Image: {
|
||||||
|
//
|
||||||
|
if (!source.canvas) {
|
||||||
|
console.error(`Parser-> 空图层 ${source?.name}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Image
|
||||||
|
let image = layer = new PsdImage(source, parent, rootDoc);
|
||||||
|
imageMgr.add(image);
|
||||||
|
|
||||||
|
// 没有设置忽略且不说镜像的情况下才进行缓存
|
||||||
|
if (!image.isIgnore() && ! image.isBind()) {
|
||||||
|
if (!imageCacheMgr.has(image.md5)) {
|
||||||
|
imageCacheMgr.set(image.md5, {
|
||||||
|
uuid: image.uuid,
|
||||||
|
textureUuid: image.textureUuid,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LayerType.Text: {
|
||||||
|
// Text
|
||||||
|
layer = new PsdText(source, parent, rootDoc);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
layer.layerType = layerType;
|
||||||
|
layer.parseSource();
|
||||||
|
layer.onCtor();
|
||||||
|
return layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const parser = new Parser();
|
6
psd2ui-tools/src/_declare.ts
Normal file
6
psd2ui-tools/src/_declare.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
export interface PsdLayerSource {
|
||||||
|
[k: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
137
psd2ui-tools/src/_decorator.ts
Normal file
137
psd2ui-tools/src/_decorator.ts
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
import { config } from "./config";
|
||||||
|
import { EditorVersion } from "./EditorVersion";
|
||||||
|
import { UIObject } from "./engine/UIObject";
|
||||||
|
|
||||||
|
/** 禁止序列化 */
|
||||||
|
export let nonserialization = (target: any,propertyKey: string)=>{
|
||||||
|
if(!target.__unserialization){
|
||||||
|
target.__unserialization = [];
|
||||||
|
}
|
||||||
|
target.__unserialization.push(propertyKey);
|
||||||
|
|
||||||
|
// if(!target.toJSON){
|
||||||
|
// // JSON.stringify 自动调用
|
||||||
|
// target.toJSON = function(){
|
||||||
|
// let data:Record<any,any> = {};
|
||||||
|
// for (const key in this) {
|
||||||
|
// if (Object.prototype.hasOwnProperty.call(this, key)) {
|
||||||
|
// // @ts-ignore
|
||||||
|
// if(this.__unserialization.indexOf(key) !== -1){
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// // 判断编辑器版本
|
||||||
|
// if(this._version && !this._version[key][EditorVersion[config.editorVersion]]){
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// const value = this[key];
|
||||||
|
// data[key] = value;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return data;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cctype(type: string){
|
||||||
|
return (target: Function) => {
|
||||||
|
Object.defineProperty(target.prototype,"$__type__",{
|
||||||
|
value: type,
|
||||||
|
enumerable: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let _extends = {};
|
||||||
|
let _class_attrs = {};
|
||||||
|
let _target_map_ = {};
|
||||||
|
let __verIdx = 0;
|
||||||
|
|
||||||
|
let _printID = -1;
|
||||||
|
|
||||||
|
function checkTag(target){
|
||||||
|
if(target.constructor.__ver_tag_id__ === undefined || _target_map_[target.constructor.__ver_tag_id__] != target){
|
||||||
|
target.constructor.__ver_tag_id__ = `${__verIdx}`;
|
||||||
|
_target_map_[target.constructor.__ver_tag_id__] = target;
|
||||||
|
__verIdx ++;
|
||||||
|
}
|
||||||
|
return target.constructor.__ver_tag_id__;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _assign(target,source){
|
||||||
|
for (const key in source) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
||||||
|
if(key in target){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
target[key] = source[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function assign(target,... sources){
|
||||||
|
for (let i = 0; i < sources.length; i++) {
|
||||||
|
_assign(target,sources[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ccversion(version: number){
|
||||||
|
|
||||||
|
return (target: any,propertyKey: string)=>{
|
||||||
|
let _class_name_ = target.constructor.name;
|
||||||
|
|
||||||
|
_class_name_ = checkTag(target);
|
||||||
|
|
||||||
|
!_class_attrs[_class_name_] && (_class_attrs[_class_name_] = {});
|
||||||
|
|
||||||
|
let _class_obj = _class_attrs[_class_name_];
|
||||||
|
if(!_class_obj[propertyKey]){
|
||||||
|
_class_obj[propertyKey] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if(EditorVersion.all === version){
|
||||||
|
for (const key in EditorVersion) {
|
||||||
|
_class_obj[propertyKey][EditorVersion[key]] = true;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
_class_obj[propertyKey][EditorVersion[version]] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var base = getSuper(target.constructor);
|
||||||
|
// (base === Object || base === UIObject) && (base = null);
|
||||||
|
if(base){
|
||||||
|
let parent = checkTag(base.prototype);
|
||||||
|
!_extends[_class_name_] && (_extends[_class_name_] = parent);
|
||||||
|
|
||||||
|
|
||||||
|
var _super = getSuper(base);
|
||||||
|
let superIdx = 1;
|
||||||
|
while (_super) {
|
||||||
|
// if(_super === Object || _super === UIObject) {
|
||||||
|
// // _super = null;
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
let super_tag = checkTag(_super.prototype);
|
||||||
|
!_extends[parent] && (_extends[parent] = super_tag);
|
||||||
|
_super = getSuper(_super);
|
||||||
|
superIdx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (parent) {
|
||||||
|
if(parent in _class_attrs){
|
||||||
|
assign(_class_obj,_class_attrs[parent]);
|
||||||
|
}
|
||||||
|
parent = _extends[parent];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!target._version){
|
||||||
|
target._version = {};
|
||||||
|
}
|
||||||
|
target._version[_class_name_] = _class_attrs[_class_name_] = _class_obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSuper (ctor) {
|
||||||
|
var proto = ctor.prototype; // binded function do not have prototype
|
||||||
|
var dunderProto = proto && Object.getPrototypeOf(proto);
|
||||||
|
return dunderProto && dunderProto.constructor;
|
||||||
|
}
|
165
psd2ui-tools/src/assets-manager/ImageCacheMgr.ts
Normal file
165
psd2ui-tools/src/assets-manager/ImageCacheMgr.ts
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
import fs from "fs-extra";
|
||||||
|
import path from "path";
|
||||||
|
import { config } from "../config";
|
||||||
|
import { EditorVersion } from "../EditorVersion";
|
||||||
|
import { fileUtils } from "../utils/FileUtils";
|
||||||
|
|
||||||
|
export interface ImageWarp {
|
||||||
|
path?: string;
|
||||||
|
uuid: string;
|
||||||
|
textureUuid: string;
|
||||||
|
isOutput?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ImageCacheMgr {
|
||||||
|
private _imageMap: Map<string, ImageWarp> = new Map();
|
||||||
|
private _cachePath: string = null;
|
||||||
|
|
||||||
|
initWithPath(_path: string) {
|
||||||
|
if (!fs.existsSync(_path)) {
|
||||||
|
console.log(`ImageCacheMgr-> 文件不存在: ${_path}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._cachePath = _path;
|
||||||
|
let content = fs.readFileSync(_path, "utf-8");
|
||||||
|
this.initWithFile(content);
|
||||||
|
}
|
||||||
|
initWithFile(file: string) {
|
||||||
|
let json = JSON.parse(file);
|
||||||
|
this.initWithJson(json);
|
||||||
|
}
|
||||||
|
initWithJson(json: any) {
|
||||||
|
for (const key in json) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(json, key)) {
|
||||||
|
this._imageMap.set(key, json[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set(md5: string, warp: ImageWarp) {
|
||||||
|
this._imageMap.set(md5, warp);
|
||||||
|
}
|
||||||
|
|
||||||
|
has(md5: string): boolean {
|
||||||
|
return this._imageMap.has(md5);
|
||||||
|
}
|
||||||
|
|
||||||
|
get(md5: string): ImageWarp {
|
||||||
|
return this._imageMap.get(md5);
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveImageMap(_path?: string) {
|
||||||
|
if (!_path) {
|
||||||
|
_path = this._cachePath;
|
||||||
|
}
|
||||||
|
if (!_path) {
|
||||||
|
console.log(`ImageCacheMgr-> 缓存路径 [${_path}] 不存在,无法保存 `);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let obj = Object.create(null);
|
||||||
|
this._imageMap.forEach((v, k) => {
|
||||||
|
obj[k] = v;
|
||||||
|
});
|
||||||
|
let content = JSON.stringify(obj, null, 2);
|
||||||
|
await fileUtils.writeFile(_path, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 获取已存在的图片,生成 md5: uuid 映射,
|
||||||
|
loadImages(dir: string) {
|
||||||
|
if (this._imageMap.size > 0) {
|
||||||
|
console.error(`ImageCacheMgr-> 暂时只能在 启动时加载`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pngs = fileUtils.filterFile(dir, (fileName) => {
|
||||||
|
let extname = path.extname(fileName);
|
||||||
|
if (extname == ".png") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!pngs){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < pngs.length; i++) {
|
||||||
|
const png = pngs[i];
|
||||||
|
let md5 = fileUtils.getMD5(png);
|
||||||
|
console.log(`ImageCacheMgr->缓存 `, png);
|
||||||
|
let imageWarp = this._loadImageMetaWarp(`${png}.meta`);
|
||||||
|
if (imageWarp) {
|
||||||
|
this.set(md5, imageWarp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _loadImageMetaWarp(_path: string) {
|
||||||
|
let content = fs.readFileSync(_path, { encoding: "utf-8" });
|
||||||
|
let imageWarp: ImageWarp = null;
|
||||||
|
switch (config.editorVersion) {
|
||||||
|
case EditorVersion.v249:
|
||||||
|
imageWarp = this._loadImageMeta249(content, _path);
|
||||||
|
break;
|
||||||
|
case EditorVersion.v342:
|
||||||
|
imageWarp = this._loadImageMeta34x(content, _path);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.log(`ImageCacheMgr-> 暂未实现 ${EditorVersion[config.editorVersion]} 版本`);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return imageWarp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _loadImageMeta249(metaContent: any, _path: string) {
|
||||||
|
let filename = path.basename(_path, ".png.meta");
|
||||||
|
let fullpath = path.join(path.dirname(_path), `${filename}.png`);
|
||||||
|
let metaJson = JSON.parse(metaContent);
|
||||||
|
|
||||||
|
if (!metaJson?.subMetas?.[filename]) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let imageWarp: ImageWarp = {
|
||||||
|
path: fullpath,
|
||||||
|
textureUuid: metaJson.subMetas[filename].uuid,
|
||||||
|
uuid: metaJson.uuid,
|
||||||
|
isOutput: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
return imageWarp;
|
||||||
|
}
|
||||||
|
private _loadImageMeta34x(metaContent: any, _path: string) {
|
||||||
|
let filename = path.basename(_path, ".png.meta");
|
||||||
|
let fullpath = path.join(path.dirname(_path), `${filename}.png`);
|
||||||
|
let metaJson = JSON.parse(metaContent);
|
||||||
|
|
||||||
|
if (!metaJson?.subMetas?.["6c48a"]) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let uuid = metaJson.subMetas["6c48a"].uuid.replace("@6c48a", "");
|
||||||
|
let imageWarp: ImageWarp = {
|
||||||
|
path: fullpath,
|
||||||
|
textureUuid: uuid,
|
||||||
|
uuid: uuid,
|
||||||
|
isOutput: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
return imageWarp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static _instance: ImageCacheMgr = null
|
||||||
|
public static getInstance(): ImageCacheMgr {
|
||||||
|
if (!this._instance) {
|
||||||
|
this._instance = new ImageCacheMgr();
|
||||||
|
}
|
||||||
|
return this._instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const imageCacheMgr = ImageCacheMgr.getInstance();
|
59
psd2ui-tools/src/assets-manager/ImageMgr.ts
Normal file
59
psd2ui-tools/src/assets-manager/ImageMgr.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { PsdImage } from "../psd/PsdImage";
|
||||||
|
|
||||||
|
class ImageMgr{
|
||||||
|
// 镜像图像管理
|
||||||
|
private _imageIdKeyMap: Map<number,PsdImage> = new Map();
|
||||||
|
|
||||||
|
// 当前 psd 所有的图片
|
||||||
|
private _imageArray: Map<string,PsdImage> = new Map();
|
||||||
|
|
||||||
|
add(psdImage: PsdImage){
|
||||||
|
// 不忽略导出图片
|
||||||
|
if(!psdImage.isIgnore() && !psdImage.isBind()){
|
||||||
|
if(!this._imageArray.has(psdImage.md5)){
|
||||||
|
this._imageArray.set(psdImage.md5,psdImage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(typeof psdImage.attr.comps.img?.id != "undefined"){
|
||||||
|
let id = psdImage.attr.comps.img.id;
|
||||||
|
if(this._imageIdKeyMap.has(id)){
|
||||||
|
console.warn(`ImageMgr-> ${psdImage.source.name} 已有相同 @img{id:${id}},请检查 psd 图层`);
|
||||||
|
}
|
||||||
|
this._imageIdKeyMap.set(id,psdImage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllImage(){
|
||||||
|
return this._imageArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 尝试获取有编号的图像图层 */
|
||||||
|
getSerialNumberImage(psdImage: PsdImage){
|
||||||
|
let bind = psdImage.attr.comps.flip?.bind ?? psdImage.attr.comps.img?.bind;
|
||||||
|
if(typeof bind != 'undefined'){
|
||||||
|
if(this._imageIdKeyMap.has(bind)){
|
||||||
|
return this._imageIdKeyMap.get(bind)
|
||||||
|
}else{
|
||||||
|
console.warn(`ImageMgr-> ${psdImage.source.name} 未找到绑定的图像 {${bind}},请检查 psd 图层`);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return psdImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear(){
|
||||||
|
this._imageIdKeyMap.clear();
|
||||||
|
this._imageArray.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
private static _instance:ImageMgr = null
|
||||||
|
public static getInstance(): ImageMgr{
|
||||||
|
if(!this._instance){
|
||||||
|
this._instance = new ImageMgr();
|
||||||
|
}
|
||||||
|
return this._instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const imageMgr = ImageMgr.getInstance();
|
60
psd2ui-tools/src/config.ts
Normal file
60
psd2ui-tools/src/config.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { EditorVersion } from "./EditorVersion";
|
||||||
|
import { CCButton } from "./engine/cc/CCButton";
|
||||||
|
import { CCComponent } from "./engine/cc/CCComponent";
|
||||||
|
import { CCProgressBar } from "./engine/cc/CCProgressBar";
|
||||||
|
import { CCToggle } from "./engine/cc/CCToggle";
|
||||||
|
|
||||||
|
export class Config {
|
||||||
|
|
||||||
|
|
||||||
|
readonly help = `
|
||||||
|
--help | 帮助信息
|
||||||
|
--init | 初始化缓存文件 必须设置 --project-assets --cache 两项
|
||||||
|
--force-img | 强制导出图片 即使在有缓存的情况下也要导出
|
||||||
|
--input | 输入目录或者 psd 文件 非 init 时 必选 [dir or psd]
|
||||||
|
--output | 输出目录 可选 缺省时为 --input [dir]
|
||||||
|
--engine-version | 引擎版本 可选 [v249 | v342]
|
||||||
|
--project-assets | 指定项目文件夹 可选 [dir]
|
||||||
|
--cache-remake | 重新创建缓存文件 可选
|
||||||
|
--cache | 缓存文件全路径 可选 [file-full-path]
|
||||||
|
--config | 预制体配置 可选 [file-full-path]
|
||||||
|
--pinyin | 中文转拼音 可选
|
||||||
|
--img-only | 只导出图片 可选
|
||||||
|
--json | json 对象参数 插件工具使用 将所有参数用对象的形式编码成 base64 字符串
|
||||||
|
`
|
||||||
|
|
||||||
|
editorVersion: EditorVersion = EditorVersion.v249;
|
||||||
|
|
||||||
|
DEFAULT_SPRITE_FRAME_MATERIAL = {
|
||||||
|
[EditorVersion.v249]: "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432",
|
||||||
|
[EditorVersion.v342]: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFAULT_LABEL_MATERIAL = {
|
||||||
|
[EditorVersion.v249]: "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432",
|
||||||
|
[EditorVersion.v342]: "",
|
||||||
|
}
|
||||||
|
get SpriteFrame_Material() {
|
||||||
|
return this.DEFAULT_SPRITE_FRAME_MATERIAL[config.editorVersion];
|
||||||
|
}
|
||||||
|
get Label_Material() {
|
||||||
|
return this.DEFAULT_LABEL_MATERIAL[config.editorVersion];
|
||||||
|
}
|
||||||
|
|
||||||
|
CompMappings: Record<string, typeof CCComponent> = {
|
||||||
|
"Btn": CCButton,
|
||||||
|
"ProgressBar": CCProgressBar,
|
||||||
|
"Toggle": CCToggle,
|
||||||
|
}
|
||||||
|
|
||||||
|
// text 文本 Y 偏移
|
||||||
|
textOffsetY = {
|
||||||
|
default: 0,
|
||||||
|
"36": 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
// text 文本 行高偏移,默认为 0 ,行高默认为 字体大小
|
||||||
|
textLineHeightOffset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = new Config();
|
44
psd2ui-tools/src/engine/UIObject.ts
Normal file
44
psd2ui-tools/src/engine/UIObject.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { config } from "../config";
|
||||||
|
import { EditorVersion } from "../EditorVersion";
|
||||||
|
import { utils } from "../utils/Utils";
|
||||||
|
import { nonserialization } from "../_decorator";
|
||||||
|
|
||||||
|
export class UIObject{
|
||||||
|
|
||||||
|
@nonserialization
|
||||||
|
uuid: string = "";
|
||||||
|
|
||||||
|
@nonserialization
|
||||||
|
idx: number = 0;
|
||||||
|
|
||||||
|
constructor(){
|
||||||
|
this.uuid = utils.uuid();
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON(){
|
||||||
|
let data:Record<any,any> = {};
|
||||||
|
for (const key in this) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(this, key)) {
|
||||||
|
// @ts-ignore
|
||||||
|
if(this.__unserialization && this.__unserialization.indexOf(key) !== -1){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
|
let ver_tag = this.constructor.__ver_tag_id__;
|
||||||
|
|
||||||
|
// 判断编辑器版本
|
||||||
|
// @ts-ignore
|
||||||
|
if(this._version && this._version[ver_tag]?.[key]){
|
||||||
|
// @ts-ignore
|
||||||
|
if(!this._version[ver_tag][key][EditorVersion[config.editorVersion]]){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = this[key];
|
||||||
|
data[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
54
psd2ui-tools/src/engine/cc/CCButton.ts
Normal file
54
psd2ui-tools/src/engine/cc/CCButton.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { EditorVersion } from "../../EditorVersion";
|
||||||
|
import { PsdLayer } from "../../psd/PsdLayer";
|
||||||
|
import { cctype, ccversion } from "../../_decorator";
|
||||||
|
import { CCComponent } from "./CCComponent";
|
||||||
|
import { CCIDObject } from "./CCObject";
|
||||||
|
|
||||||
|
@cctype("cc.Button")
|
||||||
|
export class CCButton extends CCComponent{
|
||||||
|
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
duration: number = 0.1;
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
zoomScale: number = 1.2;
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
clickEvents = [];
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_N$interactable: boolean = true;
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_N$enableAutoGrayEffect: boolean = false;
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_N$transition: number = 3;
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
transition: number = 3;
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_N$target: CCIDObject = null;
|
||||||
|
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_interactable = true;
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_transition = 3;
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_duration = 0.1;
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_zoomScale = 1.2;
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_target: CCIDObject = null;
|
||||||
|
|
||||||
|
updateWithLayer(psdLayer: PsdLayer) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
16
psd2ui-tools/src/engine/cc/CCCompPrefabInfo.ts
Normal file
16
psd2ui-tools/src/engine/cc/CCCompPrefabInfo.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { utils } from "../../utils/Utils";
|
||||||
|
import { cctype, nonserialization } from "../../_decorator";
|
||||||
|
import { UIObject } from "../UIObject";
|
||||||
|
import { CCIDObject } from "./CCObject";
|
||||||
|
|
||||||
|
// @cctype("cc.CompPrefabInfo")
|
||||||
|
export class CCCompPrefabInfo extends UIObject{
|
||||||
|
|
||||||
|
__type__: string = "cc.CompPrefabInfo";
|
||||||
|
fileId: string = "";
|
||||||
|
|
||||||
|
constructor(){
|
||||||
|
super();
|
||||||
|
this.fileId = utils.compressUuid(this.uuid);
|
||||||
|
}
|
||||||
|
}
|
22
psd2ui-tools/src/engine/cc/CCComponent.ts
Normal file
22
psd2ui-tools/src/engine/cc/CCComponent.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { EditorVersion } from "../../EditorVersion";
|
||||||
|
import { PsdLayer } from "../../psd/PsdLayer";
|
||||||
|
import { ccversion, nonserialization } from "../../_decorator";
|
||||||
|
import { CCIDObject, CCObject } from "./CCObject";
|
||||||
|
|
||||||
|
export abstract class CCComponent extends CCObject{
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_enabled: boolean = true;
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
node: CCIDObject = null;
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_id: string = "";
|
||||||
|
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
__prefab: CCIDObject = null;
|
||||||
|
|
||||||
|
abstract updateWithLayer(psdLayer: PsdLayer);
|
||||||
|
}
|
112
psd2ui-tools/src/engine/cc/CCLabel.ts
Normal file
112
psd2ui-tools/src/engine/cc/CCLabel.ts
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import { config } from "../../config";
|
||||||
|
import { EditorVersion } from "../../EditorVersion";
|
||||||
|
import { PsdLayer } from "../../psd/PsdLayer";
|
||||||
|
import { PsdText } from "../../psd/PsdText";
|
||||||
|
import { cctype, ccversion } from "../../_decorator";
|
||||||
|
import { CCComponent } from "./CCComponent";
|
||||||
|
import { CCUUIDObject } from "./CCObject";
|
||||||
|
import { CCColor } from "./values/CCColor";
|
||||||
|
|
||||||
|
@cctype("cc.Label")
|
||||||
|
export class CCLabel extends CCComponent{
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_srcBlendFactor: number = 770; // 3.4.x = 2
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_dstBlendFactor: number = 771; // 3.4.x = 4
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_string: string = "";
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_fontSize: number = 0;
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_lineHeight: number = 0;
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_enableWrapText: boolean = true;
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_isSystemFontUsed: boolean = true;
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_spacingX: number = 0;
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_underlineHeight: number = 0;
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_materials: CCUUIDObject[] = [];
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_N$string: string = "";
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_N$file: any = null;
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_batchAsBitmap: boolean = false;
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_styleFlags: number = 0;
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_N$horizontalAlign: number = 1;
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_N$verticalAlign: number = 1;
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_N$fontFamily: string = "Arial";
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_N$overflow: number = 0;
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_N$cacheMode: number = 0;
|
||||||
|
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_visFlags: number = 0;
|
||||||
|
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_customMaterial: any = null;
|
||||||
|
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_color: CCColor = new CCColor(255,255,255,255);
|
||||||
|
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_overflow: number = 0;
|
||||||
|
|
||||||
|
// // 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_cacheMode = 0;
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_horizontalAlign = 1;
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_verticalAlign = 1;
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_actualFontSize = 0;
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_isItalic = false;
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_isBold = false;
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_isUnderline = false;
|
||||||
|
|
||||||
|
updateWithLayer(psdLayer: PsdText) {
|
||||||
|
this._fontSize = psdLayer.fontSize;
|
||||||
|
// this._actualFontSize = this._fontSize;
|
||||||
|
this._string = this._N$string = psdLayer.text;
|
||||||
|
|
||||||
|
this._lineHeight = this._fontSize + config.textLineHeightOffset;
|
||||||
|
|
||||||
|
if(config.editorVersion >= EditorVersion.v342){
|
||||||
|
this._srcBlendFactor = 2;
|
||||||
|
this._dstBlendFactor = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
psd2ui-tools/src/engine/cc/CCLabelOutline.ts
Normal file
22
psd2ui-tools/src/engine/cc/CCLabelOutline.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { EditorVersion } from "../../EditorVersion";
|
||||||
|
import { PsdText } from "../../psd/PsdText";
|
||||||
|
import { cctype, ccversion } from "../../_decorator";
|
||||||
|
import { CCComponent } from "./CCComponent";
|
||||||
|
import { CCUUIDObject } from "./CCObject";
|
||||||
|
import { CCColor } from "./values/CCColor";
|
||||||
|
|
||||||
|
@cctype("cc.LabelOutline")
|
||||||
|
export class CCLabelOutline extends CCComponent{
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_color: CCColor = new CCColor(255,255,255,255);
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_width: number = 1;
|
||||||
|
|
||||||
|
|
||||||
|
updateWithLayer(psdLayer: PsdText) {
|
||||||
|
|
||||||
|
this._width = psdLayer.outline.width;
|
||||||
|
this._color.set(psdLayer.outline.color);
|
||||||
|
}
|
||||||
|
}
|
147
psd2ui-tools/src/engine/cc/CCNode.ts
Normal file
147
psd2ui-tools/src/engine/cc/CCNode.ts
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
import { config } from "../../config";
|
||||||
|
import { EditorVersion } from "../../EditorVersion";
|
||||||
|
import { PsdDocument } from "../../psd/PsdDocument";
|
||||||
|
import { cctype, ccversion, nonserialization } from "../../_decorator";
|
||||||
|
import { CCComponent } from "./CCComponent";
|
||||||
|
import { CCCompPrefabInfo } from "./CCCompPrefabInfo";
|
||||||
|
import { CCIDObject, CCObject } from "./CCObject";
|
||||||
|
import { CCColor } from "./values/CCColor";
|
||||||
|
import { CCSize } from "./values/CCSize";
|
||||||
|
import { CCTypedArray } from "./values/CCTypedArray";
|
||||||
|
import { CCVec2 } from "./values/CCVec2";
|
||||||
|
import { CCVec3 } from "./values/CCVec3";
|
||||||
|
|
||||||
|
@cctype("cc.Node")
|
||||||
|
export class CCNode extends CCObject{
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_parent: CCIDObject = null;
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_children: CCIDObject[] = [];
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_active: boolean = true;
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_components: CCIDObject[] = [];
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_prefab: CCIDObject = null;
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_id: string = "";
|
||||||
|
|
||||||
|
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_opacity: number = 255;
|
||||||
|
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_color: CCColor = new CCColor(255,255,255,255);
|
||||||
|
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_contentSize: CCSize = new CCSize();
|
||||||
|
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_anchorPoint: CCVec2 = new CCVec2(0,0);
|
||||||
|
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_trs: CCTypedArray = new CCTypedArray();
|
||||||
|
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_eulerAngles: CCVec3 = new CCVec3();
|
||||||
|
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_skewX: number = 0;
|
||||||
|
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_skewY: number = 0;
|
||||||
|
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_is3DNode: boolean = false;
|
||||||
|
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_groupIndex: number = 0;
|
||||||
|
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
groupIndex: number = 0;
|
||||||
|
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_renderEnable: boolean = false;
|
||||||
|
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_bfsRenderFlag: boolean = false;
|
||||||
|
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_lpos: CCVec3 = new CCVec3();
|
||||||
|
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_lrot: CCVec3 = new CCVec3();
|
||||||
|
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_lscale: CCVec3 = new CCVec3();
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_euler: CCVec3 = new CCVec3();
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_layer: number = 33554432;
|
||||||
|
|
||||||
|
|
||||||
|
@nonserialization
|
||||||
|
psdDoc: PsdDocument = null;
|
||||||
|
|
||||||
|
@nonserialization
|
||||||
|
components: CCComponent[] = [];
|
||||||
|
|
||||||
|
@nonserialization
|
||||||
|
children: CCNode[] = [];
|
||||||
|
|
||||||
|
constructor(psdDoc: PsdDocument){
|
||||||
|
super();
|
||||||
|
if(psdDoc){
|
||||||
|
this.psdDoc = psdDoc;
|
||||||
|
psdDoc.pushObject(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
addComponent(comp: CCComponent){
|
||||||
|
comp.node = {__id__: this.idx }
|
||||||
|
let compIdx = this.psdDoc.pushObject(comp);
|
||||||
|
this._components.push({ __id__: compIdx});
|
||||||
|
this.components.push(comp);
|
||||||
|
|
||||||
|
if(config.editorVersion >= EditorVersion.v342){
|
||||||
|
this.addCompPrefabInfo(comp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addCompPrefabInfo(comp: CCComponent){
|
||||||
|
let compInfo = new CCCompPrefabInfo();
|
||||||
|
let compIdx = this.psdDoc.pushObject(compInfo);
|
||||||
|
comp.__prefab = {__id__: compIdx }
|
||||||
|
}
|
||||||
|
|
||||||
|
addChild(child: CCNode){
|
||||||
|
this._children.push({ __id__: child.idx});
|
||||||
|
child._parent = { __id__: this.idx };
|
||||||
|
this.children.push(child);
|
||||||
|
}
|
||||||
|
}
|
29
psd2ui-tools/src/engine/cc/CCObject.ts
Normal file
29
psd2ui-tools/src/engine/cc/CCObject.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { EditorVersion } from "../../EditorVersion";
|
||||||
|
import { utils } from "../../utils/Utils";
|
||||||
|
import { ccversion, nonserialization } from "../../_decorator";
|
||||||
|
import { UIObject } from "../UIObject";
|
||||||
|
|
||||||
|
export type CCIDObject = { __id__: number };
|
||||||
|
export type CCUUIDObject = { __uuid__: string, __expectedType__?: string};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export class CCObject extends UIObject{
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
__type__: string;
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_name: string = "";
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_objFlags: number = 0;
|
||||||
|
|
||||||
|
constructor(){
|
||||||
|
super();
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
this.__type__ = this.$__type__
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
26
psd2ui-tools/src/engine/cc/CCPrefab.ts
Normal file
26
psd2ui-tools/src/engine/cc/CCPrefab.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { EditorVersion } from "../../EditorVersion";
|
||||||
|
import { cctype, ccversion } from "../../_decorator";
|
||||||
|
import { CCIDObject, CCObject } from "./CCObject";
|
||||||
|
|
||||||
|
@cctype("cc.Prefab")
|
||||||
|
export class CCPrefab extends CCObject{
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_native: string = "";
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
data: CCIDObject = null;
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
optimizationPolicy: number = 0;
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
asyncLoadAssets: boolean = false;
|
||||||
|
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
readonly: boolean = false;
|
||||||
|
|
||||||
|
// // 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
persistent: boolean = false;
|
||||||
|
|
||||||
|
}
|
30
psd2ui-tools/src/engine/cc/CCPrefabInfo.ts
Normal file
30
psd2ui-tools/src/engine/cc/CCPrefabInfo.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { EditorVersion } from "../../EditorVersion";
|
||||||
|
import { utils } from "../../utils/Utils";
|
||||||
|
import { cctype, ccversion, nonserialization } from "../../_decorator";
|
||||||
|
import { UIObject } from "../UIObject";
|
||||||
|
import { CCIDObject } from "./CCObject";
|
||||||
|
|
||||||
|
// @cctype("cc.PrefabInfo")
|
||||||
|
export class CCPrefabInfo extends UIObject{
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
__type__: string = "cc.PrefabInfo";
|
||||||
|
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
root: CCIDObject = { __id__: 1 };
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
asset: CCIDObject = { __id__: 0};
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
fileId: string = "";
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
sync: boolean = false;
|
||||||
|
|
||||||
|
constructor(){
|
||||||
|
super();
|
||||||
|
this.fileId = utils.compressUuid(this.uuid);
|
||||||
|
}
|
||||||
|
}
|
86
psd2ui-tools/src/engine/cc/CCProgressBar.ts
Normal file
86
psd2ui-tools/src/engine/cc/CCProgressBar.ts
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import { EditorVersion } from "../../EditorVersion";
|
||||||
|
import { PsdGroup } from "../../psd/PsdGroup";
|
||||||
|
import { PsdLayer } from "../../psd/PsdLayer";
|
||||||
|
import { cctype, ccversion } from "../../_decorator";
|
||||||
|
import { CCComponent } from "./CCComponent";
|
||||||
|
import { CCNode } from "./CCNode";
|
||||||
|
import { CCIDObject } from "./CCObject";
|
||||||
|
import { CCSprite } from "./CCSprite";
|
||||||
|
|
||||||
|
@cctype("cc.ProgressBar")
|
||||||
|
export class CCProgressBar extends CCComponent{
|
||||||
|
|
||||||
|
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_N$totalLength: number = 0;
|
||||||
|
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_N$barSprite: CCIDObject = null;
|
||||||
|
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_N$mode: number = 0;
|
||||||
|
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_N$progress: number = 1;
|
||||||
|
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_N$reverse: boolean = false;
|
||||||
|
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_barSprite: CCIDObject = null;
|
||||||
|
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_mode = 0;
|
||||||
|
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_totalLength = 0;
|
||||||
|
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_progress = 1;
|
||||||
|
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_reverse = false;
|
||||||
|
|
||||||
|
|
||||||
|
setBar(sprite: CCSprite){
|
||||||
|
this._barSprite = this._N$barSprite = {
|
||||||
|
__id__: sprite.idx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateWithLayer(psdLayer: PsdGroup) {
|
||||||
|
if(!psdLayer.children){
|
||||||
|
console.error(`CCProgressBar-> 只能作用在 组图层 上`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
outer: for (let i = 0; i < psdLayer.children.length; i++) {
|
||||||
|
const child = psdLayer.children[i];
|
||||||
|
if(child.attr.comps.bar){
|
||||||
|
let node = child.uiObject as CCNode;
|
||||||
|
|
||||||
|
// 暂时只有横向进度条
|
||||||
|
this._totalLength = this._N$totalLength = node._contentSize.width;
|
||||||
|
|
||||||
|
for (let j = 0; j < node.components.length; j++) {
|
||||||
|
const comp = node.components[j];
|
||||||
|
if(comp instanceof CCSprite){
|
||||||
|
this.setBar(comp);
|
||||||
|
break outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
93
psd2ui-tools/src/engine/cc/CCSprite.ts
Normal file
93
psd2ui-tools/src/engine/cc/CCSprite.ts
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import { config } from "../../config";
|
||||||
|
import { EditorVersion } from "../../EditorVersion";
|
||||||
|
import { PsdImage } from "../../psd/PsdImage";
|
||||||
|
import { cctype, ccversion } from "../../_decorator";
|
||||||
|
import { CCComponent } from "./CCComponent";
|
||||||
|
import { CCIDObject, CCUUIDObject } from "./CCObject";
|
||||||
|
import { CCColor } from "./values/CCColor";
|
||||||
|
import { CCVec2 } from "./values/CCVec2";
|
||||||
|
|
||||||
|
@cctype("cc.Sprite")
|
||||||
|
export class CCSprite extends CCComponent {
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_materials: CCUUIDObject[] = [];
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_srcBlendFactor: number = 770; // 3.4.x = 2
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_dstBlendFactor: number = 771; // 3.4.x = 4
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_spriteFrame: CCUUIDObject = null;
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_type: number = 0;
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_sizeMode: number = 1;
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_fillType: number = 0;
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_fillCenter: CCVec2 = new CCVec2();
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_fillStart: number = 0;
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_fillRange: number = 0;
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_isTrimmedMode: boolean = true;
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
_atlas = null;
|
||||||
|
|
||||||
|
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_visFlags: number = 0;
|
||||||
|
|
||||||
|
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_customMaterial: any = null;
|
||||||
|
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_color: CCColor = new CCColor(255,255,255,255);
|
||||||
|
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_useGrayscale: boolean = false;
|
||||||
|
|
||||||
|
use9() {
|
||||||
|
this._type = 1;
|
||||||
|
this._sizeMode = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateWithLayer(psdLayer: PsdImage) {
|
||||||
|
if (psdLayer.s9) {
|
||||||
|
this.use9();
|
||||||
|
}
|
||||||
|
if (Math.abs(psdLayer.scale.x) != 1 || Math.abs(psdLayer.scale.y) != 1) {
|
||||||
|
this._sizeMode = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(config.editorVersion >= EditorVersion.v342){
|
||||||
|
this._srcBlendFactor = 2;
|
||||||
|
this._dstBlendFactor = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setSpriteFrame(uuid: string){
|
||||||
|
if(config.editorVersion >= EditorVersion.v342){
|
||||||
|
this._spriteFrame = {__uuid__: `${uuid}@f9941`,__expectedType__ : "cc.SpriteFrame"};
|
||||||
|
}else{
|
||||||
|
this._spriteFrame = {__uuid__: uuid};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
62
psd2ui-tools/src/engine/cc/CCToggle.ts
Normal file
62
psd2ui-tools/src/engine/cc/CCToggle.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { EditorVersion } from "../../EditorVersion";
|
||||||
|
import { PsdGroup } from "../../psd/PsdGroup";
|
||||||
|
import { PsdLayer } from "../../psd/PsdLayer";
|
||||||
|
import { cctype, ccversion } from "../../_decorator";
|
||||||
|
import { CCButton } from "./CCButton";
|
||||||
|
import { CCComponent } from "./CCComponent";
|
||||||
|
import { CCNode } from "./CCNode";
|
||||||
|
import { CCIDObject } from "./CCObject";
|
||||||
|
import { CCSprite } from "./CCSprite";
|
||||||
|
|
||||||
|
@cctype("cc.Toggle")
|
||||||
|
export class CCToggle extends CCButton{
|
||||||
|
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
_N$isChecked = true;
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
toggleGroup = null;
|
||||||
|
// 2.4.x
|
||||||
|
@ccversion(EditorVersion.v249)
|
||||||
|
checkMark: CCIDObject = null;
|
||||||
|
@ccversion(EditorVersion.all)
|
||||||
|
checkEvents = [];
|
||||||
|
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_isChecked = true;
|
||||||
|
// 3.4.x
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_checkMark: CCIDObject = null;
|
||||||
|
|
||||||
|
setCheckMark(sprite: CCSprite){
|
||||||
|
this._checkMark = this.checkMark = {
|
||||||
|
__id__: sprite.idx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
updateWithLayer(psdLayer: PsdGroup) {
|
||||||
|
if(!psdLayer.children){
|
||||||
|
console.error(`CCToggle-> 只能作用在 组图层 上`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
outer: for (let i = 0; i < psdLayer.children.length; i++) {
|
||||||
|
const child = psdLayer.children[i];
|
||||||
|
if(child.attr.comps.check){
|
||||||
|
let node = child.uiObject as CCNode;
|
||||||
|
for (let j = 0; j < node.components.length; j++) {
|
||||||
|
const comp = node.components[j];
|
||||||
|
if(comp instanceof CCSprite){
|
||||||
|
|
||||||
|
this.setCheckMark(comp);
|
||||||
|
break outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
15
psd2ui-tools/src/engine/cc/CCUIOpacity.ts
Normal file
15
psd2ui-tools/src/engine/cc/CCUIOpacity.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { EditorVersion } from "../../EditorVersion";
|
||||||
|
import { PsdLayer } from "../../psd/PsdLayer";
|
||||||
|
import { cctype, ccversion } from "../../_decorator";
|
||||||
|
import { CCComponent } from "./CCComponent";
|
||||||
|
|
||||||
|
// 3.4.x
|
||||||
|
@cctype("cc.UIOpacity")
|
||||||
|
export class CCUIOpacity extends CCComponent{
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_opacity = 255;
|
||||||
|
|
||||||
|
updateWithLayer(psdLayer: PsdLayer) {
|
||||||
|
}
|
||||||
|
}
|
22
psd2ui-tools/src/engine/cc/CCUITransform.ts
Normal file
22
psd2ui-tools/src/engine/cc/CCUITransform.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { EditorVersion } from "../../EditorVersion";
|
||||||
|
import { PsdLayer } from "../../psd/PsdLayer";
|
||||||
|
import { cctype, ccversion } from "../../_decorator";
|
||||||
|
import { CCComponent } from "./CCComponent";
|
||||||
|
import { CCIDObject, CCObject } from "./CCObject";
|
||||||
|
import { CCSize } from "./values/CCSize";
|
||||||
|
import { CCVec2 } from "./values/CCVec2";
|
||||||
|
|
||||||
|
// 3.4.x
|
||||||
|
|
||||||
|
@cctype("cc.UITransform")
|
||||||
|
export class CCUITransform extends CCComponent{
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_contentSize: CCSize = new CCSize();
|
||||||
|
|
||||||
|
@ccversion(EditorVersion.v342)
|
||||||
|
_anchorPoint: CCVec2 = new CCVec2(0,0);
|
||||||
|
|
||||||
|
updateWithLayer(psdLayer: PsdLayer) {
|
||||||
|
}
|
||||||
|
}
|
7
psd2ui-tools/src/engine/cc/values/CCColor.ts
Normal file
7
psd2ui-tools/src/engine/cc/values/CCColor.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { Color } from "../../../values/Color";
|
||||||
|
import { cctype } from "../../../_decorator";
|
||||||
|
|
||||||
|
export class CCColor extends Color{
|
||||||
|
|
||||||
|
__type__: string = "cc.Color";
|
||||||
|
}
|
8
psd2ui-tools/src/engine/cc/values/CCSize.ts
Normal file
8
psd2ui-tools/src/engine/cc/values/CCSize.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { Size } from "../../../values/Size";
|
||||||
|
import { cctype } from "../../../_decorator";
|
||||||
|
|
||||||
|
@cctype("cc.Size")
|
||||||
|
export class CCSize extends Size{
|
||||||
|
|
||||||
|
__type__: string = "cc.Size";
|
||||||
|
}
|
28
psd2ui-tools/src/engine/cc/values/CCTypedArray.ts
Normal file
28
psd2ui-tools/src/engine/cc/values/CCTypedArray.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { cctype } from "../../../_decorator";
|
||||||
|
import { CCVec3 } from "./CCVec3";
|
||||||
|
|
||||||
|
@cctype("TypedArray")
|
||||||
|
export class CCTypedArray{
|
||||||
|
|
||||||
|
__type__: string = "TypedArray";
|
||||||
|
ctor: string = "Float64Array";
|
||||||
|
array: number [] = [];
|
||||||
|
|
||||||
|
setPosition(x: number,y: number,z: number){
|
||||||
|
this.array[0] = x;
|
||||||
|
this.array[1] = y;
|
||||||
|
this.array[2] = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
setRotation(x: number,y: number,z: number,w: number){
|
||||||
|
this.array[3] = x;
|
||||||
|
this.array[4] = y;
|
||||||
|
this.array[5] = z;
|
||||||
|
this.array[6] = w;
|
||||||
|
}
|
||||||
|
setScale(x: number,y: number,z: number){
|
||||||
|
this.array[7] = x;
|
||||||
|
this.array[8] = y;
|
||||||
|
this.array[9] = z;
|
||||||
|
}
|
||||||
|
}
|
8
psd2ui-tools/src/engine/cc/values/CCVec2.ts
Normal file
8
psd2ui-tools/src/engine/cc/values/CCVec2.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { Vec2 } from "../../../values/Vec2";
|
||||||
|
import { cctype } from "../../../_decorator";
|
||||||
|
|
||||||
|
@cctype("cc.Vec2")
|
||||||
|
export class CCVec2 extends Vec2{
|
||||||
|
|
||||||
|
__type__: string = "cc.Vec2";
|
||||||
|
}
|
8
psd2ui-tools/src/engine/cc/values/CCVec3.ts
Normal file
8
psd2ui-tools/src/engine/cc/values/CCVec3.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { Vec3 } from "../../../values/Vec3";
|
||||||
|
import { cctype } from "../../../_decorator";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export class CCVec3 extends Vec3{
|
||||||
|
__type__: string = "cc.Vec3";
|
||||||
|
}
|
2
psd2ui-tools/src/export.ts
Normal file
2
psd2ui-tools/src/export.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
export * from './index'
|
20
psd2ui-tools/src/index.ts
Normal file
20
psd2ui-tools/src/index.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
import minimist from 'minimist';
|
||||||
|
import { Main } from './Main';
|
||||||
|
import { Texture9Utils } from './utils/Texture9Utils';
|
||||||
|
// ##################
|
||||||
|
|
||||||
|
// 输入
|
||||||
|
|
||||||
|
const oldArgs = process.argv.slice(2);
|
||||||
|
const args = minimist(oldArgs);
|
||||||
|
|
||||||
|
let main = new Main();
|
||||||
|
if (oldArgs.length) {
|
||||||
|
main.exec(args);
|
||||||
|
} else {
|
||||||
|
// 测试
|
||||||
|
main.test();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ##################
|
6
psd2ui-tools/src/psd/LayerType.ts
Normal file
6
psd2ui-tools/src/psd/LayerType.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export enum LayerType{
|
||||||
|
Doc,
|
||||||
|
Group,
|
||||||
|
Text,
|
||||||
|
Image
|
||||||
|
}
|
46
psd2ui-tools/src/psd/PsdDocument.ts
Normal file
46
psd2ui-tools/src/psd/PsdDocument.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { UIObject } from "../engine/UIObject";
|
||||||
|
import { Rect } from "../values/Rect";
|
||||||
|
import { Size } from "../values/Size";
|
||||||
|
import { PsdGroup } from "./PsdGroup";
|
||||||
|
|
||||||
|
export class PsdDocument extends PsdGroup{
|
||||||
|
|
||||||
|
/** 当前文档所有的图片 */
|
||||||
|
images: Map<string,any> = new Map();
|
||||||
|
|
||||||
|
objectMap: Map<string,number> = new Map();
|
||||||
|
|
||||||
|
objectArray: UIObject[] = [];
|
||||||
|
|
||||||
|
constructor(source: any){
|
||||||
|
super(source,null,null);
|
||||||
|
this.size = new Size(source.width,source.height);
|
||||||
|
this.rect = new Rect(0, this.size.width, 0, this.size.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
pushObject(uiObject: UIObject){
|
||||||
|
let idx = this.objectArray.length;
|
||||||
|
uiObject.idx = idx;
|
||||||
|
this.objectMap.set(uiObject.uuid,idx);
|
||||||
|
this.objectArray.push(uiObject);
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
getObjectIdx(uuid: string){
|
||||||
|
let idx = this.objectMap.get(uuid);
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
getObject(uuid: string){
|
||||||
|
let idx = this.objectMap.get(uuid);
|
||||||
|
if(idx < this.objectArray.length){
|
||||||
|
return this.objectArray[idx];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
onCtor(): void {
|
||||||
|
super.onCtor();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
49
psd2ui-tools/src/psd/PsdGroup.ts
Normal file
49
psd2ui-tools/src/psd/PsdGroup.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { Rect } from "../values/Rect";
|
||||||
|
import { PsdLayer } from "./PsdLayer";
|
||||||
|
|
||||||
|
export class PsdGroup extends PsdLayer {
|
||||||
|
declare children: PsdLayer[];
|
||||||
|
declare parent: PsdGroup;
|
||||||
|
|
||||||
|
|
||||||
|
constructor(source: any, parent: PsdLayer, rootDoc: PsdLayer) {
|
||||||
|
super(source, parent, rootDoc);
|
||||||
|
this.children = [];
|
||||||
|
if (rootDoc) {
|
||||||
|
this.rect = new Rect(0, rootDoc.size.width, 0, rootDoc.size.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parseSource(): boolean {
|
||||||
|
super.parseSource();
|
||||||
|
|
||||||
|
if(!this.attr?.comps.full){
|
||||||
|
this.resize();
|
||||||
|
this.computeBasePosition();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
resize() {
|
||||||
|
let left = Number.MAX_SAFE_INTEGER;
|
||||||
|
let right = Number.MIN_SAFE_INTEGER;
|
||||||
|
let top = Number.MAX_SAFE_INTEGER;
|
||||||
|
let bottom = Number.MIN_SAFE_INTEGER;
|
||||||
|
|
||||||
|
for (let i = 0; i < this.children.length; i++) {
|
||||||
|
const element = this.children[i];
|
||||||
|
let _rect = element.rect;
|
||||||
|
left = Math.min(_rect.left, left);
|
||||||
|
right = Math.max(_rect.right, right);
|
||||||
|
top = Math.min(_rect.top, top);
|
||||||
|
bottom = Math.max(_rect.bottom, bottom);
|
||||||
|
}
|
||||||
|
this.rect.left = left;
|
||||||
|
this.rect.right = right;
|
||||||
|
this.rect.top = top;
|
||||||
|
this.rect.bottom = bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
onCtor() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
98
psd2ui-tools/src/psd/PsdImage.ts
Normal file
98
psd2ui-tools/src/psd/PsdImage.ts
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import { PsdGroup } from "./PsdGroup";
|
||||||
|
import { PsdLayer } from "./PsdLayer";
|
||||||
|
import { utils } from "../utils/Utils";
|
||||||
|
import canvas from 'canvas';
|
||||||
|
import { Border, Texture9Utils } from "../utils/Texture9Utils";
|
||||||
|
import { Size } from "../values/Size";
|
||||||
|
import { fileUtils } from "../utils/FileUtils";
|
||||||
|
import { Vec3 } from "../values/Vec3";
|
||||||
|
|
||||||
|
export class PsdImage extends PsdLayer {
|
||||||
|
declare parent: PsdGroup;
|
||||||
|
|
||||||
|
declare textureUuid: string;
|
||||||
|
|
||||||
|
declare md5: string;
|
||||||
|
declare imgBuffer: Buffer;
|
||||||
|
|
||||||
|
declare textureSize: Size;
|
||||||
|
|
||||||
|
declare imgName: string;
|
||||||
|
|
||||||
|
|
||||||
|
declare s9: Border;
|
||||||
|
|
||||||
|
constructor(source: any, parent: PsdLayer, rootDoc: PsdLayer) {
|
||||||
|
super(source, parent, rootDoc);
|
||||||
|
this.textureUuid = utils.uuid();
|
||||||
|
|
||||||
|
// img name
|
||||||
|
this.imgName = this.attr.comps.img?.name || this.name
|
||||||
|
|
||||||
|
// .9
|
||||||
|
if (this.attr.comps['.9']) {
|
||||||
|
let s9 = this.attr.comps['.9'];
|
||||||
|
this.s9 = Texture9Utils.safeBorder(this.source.canvas, s9 as any);
|
||||||
|
let newCanvas = Texture9Utils.split(this.source.canvas, s9 as any);
|
||||||
|
this.source.canvas = newCanvas;
|
||||||
|
}
|
||||||
|
let canvas: canvas.Canvas = this.source.canvas;
|
||||||
|
|
||||||
|
this.imgBuffer = canvas.toBuffer('image/png');
|
||||||
|
this.md5 = fileUtils.getMD5(this.imgBuffer);
|
||||||
|
|
||||||
|
this.textureSize = new Size(canvas.width, canvas.height);
|
||||||
|
this.scale = new Vec3((this.isFilpX() ? -1 : 1) * this.scale.x, (this.isFilpY() ? -1 : 1) * this.scale.y, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
onCtor() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
isIgnore() {
|
||||||
|
//
|
||||||
|
if (this.attr.comps.ignore || this.attr.comps.ignoreimg) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 是否是镜像图片 */
|
||||||
|
isBind() {
|
||||||
|
return typeof this.attr.comps.flip?.bind !== 'undefined'
|
||||||
|
|| typeof this.attr.comps.img?.bind !== 'undefined';
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 是否是 x 方向镜像图片 */
|
||||||
|
isFilpX() {
|
||||||
|
return typeof this.attr.comps.flipX?.bind !== 'undefined';
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 是否是 y 方向镜像图片 */
|
||||||
|
isFilpY() {
|
||||||
|
return typeof this.attr.comps.flipY?.bind !== 'undefined';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据锚点计算坐标
|
||||||
|
updatePositionWithAR() {
|
||||||
|
if (!this.parent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let parent = this.parent;
|
||||||
|
while (parent) {
|
||||||
|
this.position.x -= parent.position.x;
|
||||||
|
this.position.y -= parent.position.y;
|
||||||
|
parent = parent.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this.position.x = this.position.x - this.parent.size.width * this.parent.anchorPoint.x + this.size.width * this.anchorPoint.x;
|
||||||
|
// this.position.y = this.position.y - this.parent.size.height * this.parent.anchorPoint.y + this.size.height * this.anchorPoint.y;
|
||||||
|
// 如果是镜像图片,则特殊处理
|
||||||
|
let arX = (this.isFilpX() ? (1 - this.anchorPoint.x) : this.anchorPoint.x);
|
||||||
|
let arY = (this.isFilpY() ? (1 - this.anchorPoint.y) : this.anchorPoint.y);
|
||||||
|
this.position.x = this.position.x - this.rootDoc.size.width * this.rootDoc.anchorPoint.x + this.size.width * arX;
|
||||||
|
this.position.y = this.position.y - this.rootDoc.size.height * this.rootDoc.anchorPoint.y + this.size.height * arY;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
298
psd2ui-tools/src/psd/PsdLayer.ts
Normal file
298
psd2ui-tools/src/psd/PsdLayer.ts
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
import { PsdLayerSource } from "../_declare";
|
||||||
|
import { LayerType } from "./LayerType";
|
||||||
|
import { Size } from "../values/Size";
|
||||||
|
import { Vec2 } from "../values/Vec2";
|
||||||
|
import { utils } from "../utils/Utils";
|
||||||
|
import { UIObject } from "../engine/UIObject";
|
||||||
|
import { Rect } from "../values/Rect";
|
||||||
|
import { Color } from "../values/Color";
|
||||||
|
import { Vec3 } from "../values/Vec3";
|
||||||
|
import { pinyin } from "pinyin-pro";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 命名规则
|
||||||
|
* "name@Type{prop: 1,prop2: 2}"
|
||||||
|
* Type = btn | bar | (toggle @check) | .9 |
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface PsdAttr {
|
||||||
|
name: string,
|
||||||
|
comps: {
|
||||||
|
Btn?: {};
|
||||||
|
btn?: {};
|
||||||
|
ProgressBar?: {};
|
||||||
|
progressBar?: {};
|
||||||
|
bar?: {};
|
||||||
|
Toggle?: {};
|
||||||
|
toggle?: {};
|
||||||
|
check?: {};
|
||||||
|
".9"?: { l?: number, r?: number, b?: number, t?: number };
|
||||||
|
ar?: { x?: number, y?: number };
|
||||||
|
// 忽略导出节点和图片
|
||||||
|
ignore?: {};
|
||||||
|
ig?: {};
|
||||||
|
// 忽略导出节点
|
||||||
|
ignorenode?: {};
|
||||||
|
ignode?: {};
|
||||||
|
// 忽略导出图片
|
||||||
|
ignoreimg?: {};
|
||||||
|
igimg?: {};
|
||||||
|
|
||||||
|
full?: {};
|
||||||
|
size?: { w?: number, h?: number };
|
||||||
|
scale?: { x?: number, y?: number };
|
||||||
|
img?: { id?: number, name?: string, bind?: number }
|
||||||
|
flip?: { bind: number, x?: number, y?: number }
|
||||||
|
flipX?: { bind: number }
|
||||||
|
flipY?: { bind: number }
|
||||||
|
// position?:{x?: number,y?: number};
|
||||||
|
// pos?:{x?: number,y?: number};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class PsdLayer {
|
||||||
|
|
||||||
|
static isPinyin = false;
|
||||||
|
|
||||||
|
declare uuid: string;
|
||||||
|
declare rootDoc: PsdLayer;
|
||||||
|
declare name: string;
|
||||||
|
declare source: PsdLayerSource;
|
||||||
|
declare parent: PsdLayer;
|
||||||
|
declare position: Vec2;
|
||||||
|
declare size: Size;
|
||||||
|
declare rect: Rect;
|
||||||
|
declare anchorPoint: Vec2;
|
||||||
|
declare hidden: boolean;
|
||||||
|
declare opacity: number;
|
||||||
|
declare layerType: LayerType;
|
||||||
|
declare uiObject: UIObject;
|
||||||
|
declare attr: PsdAttr; // 解析名字获得各项属性
|
||||||
|
declare color: Color;
|
||||||
|
declare scale: Vec3;
|
||||||
|
|
||||||
|
constructor(source: any, parent: PsdLayer, rootDoc: PsdLayer) {
|
||||||
|
this.uuid = utils.uuid();
|
||||||
|
this.source = source;
|
||||||
|
this.parent = parent;
|
||||||
|
this.rootDoc = rootDoc;
|
||||||
|
this.name = source.name;
|
||||||
|
this.position = new Vec2();
|
||||||
|
this.size = new Size();
|
||||||
|
this.rect = new Rect(source);
|
||||||
|
// this.anchorPoint = new Vec2();
|
||||||
|
this.anchorPoint = new Vec2(0.5, 0.5);
|
||||||
|
this.hidden = false;
|
||||||
|
this.opacity = 255;
|
||||||
|
this.color = new Color(255, 255, 255, 255);
|
||||||
|
console.log(`PsdLayer->解析到图层 `, this.name);
|
||||||
|
|
||||||
|
this.attr = this.parseNameRule(this.name);
|
||||||
|
// // 更新名字
|
||||||
|
this.name = this.chineseToPinyin(this.attr?.name || this.name);
|
||||||
|
|
||||||
|
// 使用配置的缩放系数
|
||||||
|
let _scale = this.attr?.comps.scale;
|
||||||
|
this.scale = new Vec3(_scale?.x ?? 1, _scale?.y ?? 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract onCtor();
|
||||||
|
|
||||||
|
|
||||||
|
parseNameRule(name: string) {
|
||||||
|
if (!name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
name = name.trim();
|
||||||
|
let fragments = name.split("@");
|
||||||
|
if (fragments.length === 0) {
|
||||||
|
console.error(`PsdLayer-> 名字解析错误`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let obj: PsdAttr = {
|
||||||
|
name: fragments[0]?.replace(/\.|>|\/|\ /g, "_") ?? "unknow",
|
||||||
|
comps: {},
|
||||||
|
}
|
||||||
|
for (let i = 1; i < fragments.length; i++) {
|
||||||
|
const fragment = fragments[i].trim();
|
||||||
|
let attr = {};
|
||||||
|
let startIdx = fragment.indexOf("{");
|
||||||
|
let comp = fragment;
|
||||||
|
if (startIdx != -1) {
|
||||||
|
|
||||||
|
let endIdx = fragment.indexOf("}");
|
||||||
|
if (endIdx == -1) {
|
||||||
|
console.log(`PsdLayer->${name} 属性 解析错误`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let attrStr = fragment.substring(startIdx + 1, endIdx);
|
||||||
|
comp = fragment.substr(0, startIdx);
|
||||||
|
attrStr = attrStr.trim();
|
||||||
|
let attrs = attrStr.split(",");
|
||||||
|
attrs.forEach((str) => {
|
||||||
|
str = str.trim();
|
||||||
|
let strs = str.split(":");
|
||||||
|
if (!strs.length) {
|
||||||
|
console.log(`PsdLayer->${name} 属性 解析错误`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
strs.map((v) => {
|
||||||
|
return v.trim();
|
||||||
|
});
|
||||||
|
attr[strs[0]] = utils.isNumber(strs[1]) ? parseFloat(strs[1]) : strs[1];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
comp = comp.trim();
|
||||||
|
comp = comp.replace(":", ""); // 防呆,删除 key 中的冒号,
|
||||||
|
obj.comps[comp] = attr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取别名的值
|
||||||
|
obj.comps.ignore = obj.comps.ignore || obj.comps.ig;
|
||||||
|
obj.comps.ignorenode = obj.comps.ignorenode || obj.comps.ignode;
|
||||||
|
obj.comps.ignoreimg = obj.comps.ignoreimg || obj.comps.igimg;
|
||||||
|
obj.comps.Btn = obj.comps.Btn || obj.comps.btn;
|
||||||
|
obj.comps.ProgressBar = obj.comps.ProgressBar || obj.comps.progressBar;
|
||||||
|
obj.comps.Toggle = obj.comps.Toggle || obj.comps.toggle;
|
||||||
|
|
||||||
|
// 图片名中文转拼音
|
||||||
|
if (obj.comps.img) {
|
||||||
|
if (obj.comps.img.name) {
|
||||||
|
obj.comps.img.name = this.chineseToPinyin(obj.comps.img.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将mirror filpX filpY 进行合并
|
||||||
|
if (obj.comps.flip || obj.comps.flipX || obj.comps.flipY) {
|
||||||
|
obj.comps.flip = Object.assign({}, obj.comps.flip, obj.comps.flipX, obj.comps.flipY);
|
||||||
|
|
||||||
|
if (obj.comps.flipX) {
|
||||||
|
obj.comps.flip.x = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.comps.flipY) {
|
||||||
|
obj.comps.flip.y = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// x,y 都缺省时,默认 x 方向镜像
|
||||||
|
if (typeof obj.comps.flip.bind !== 'undefined') {
|
||||||
|
if (!obj.comps.flip.y) {
|
||||||
|
obj.comps.flip.x = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只有作为镜像图片使用的时候才反向赋值
|
||||||
|
// 反向赋值,防止使用的时候值错误
|
||||||
|
if (obj.comps.flip.x) {
|
||||||
|
obj.comps.flipX = Object.assign({}, obj.comps.flipX, obj.comps.flip);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.comps.flip.y) {
|
||||||
|
obj.comps.flipY = Object.assign({}, obj.comps.flipY, obj.comps.flip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 检查冲突
|
||||||
|
if (obj.comps.full && obj.comps.size) {
|
||||||
|
console.warn(`PsdLayer->${obj.name} 同时存在 @full 和 @size`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 解析数据 */
|
||||||
|
parseSource() {
|
||||||
|
let _source = this.source;
|
||||||
|
|
||||||
|
|
||||||
|
// psd文档
|
||||||
|
if (!this.parent) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.hidden = _source.hidden;
|
||||||
|
this.opacity = Math.round(_source.opacity * 255);
|
||||||
|
|
||||||
|
// 获取锚点
|
||||||
|
let ar = this.attr.comps.ar;
|
||||||
|
if (ar) {
|
||||||
|
this.anchorPoint.x = ar.x ?? this.anchorPoint.x;
|
||||||
|
this.anchorPoint.y = ar.y ?? this.anchorPoint.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.computeBasePosition();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/** 解析 effect */
|
||||||
|
parseEffects() {
|
||||||
|
// 颜色叠加 暂时搞不定
|
||||||
|
// if(this.source.effects?.solidFill){
|
||||||
|
// let solidFills = this.source.effects?.solidFill;
|
||||||
|
// for (let i = 0; i < solidFills.length; i++) {
|
||||||
|
// const solidFill = solidFills[i];
|
||||||
|
// if(solidFill.enabled){
|
||||||
|
// let color = solidFill.color;
|
||||||
|
// this.color = new Color(color.r,color.g,color.b,solidFill.opacity * 255);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 中文转拼音 */
|
||||||
|
chineseToPinyin(text: string) {
|
||||||
|
if (!text || !PsdLayer.isPinyin) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
let names = pinyin(text, {
|
||||||
|
toneType: "none",
|
||||||
|
type: "array"
|
||||||
|
});
|
||||||
|
|
||||||
|
names = names.map((text) => {
|
||||||
|
return text.slice(0, 1).toUpperCase() + text.slice(1).toLowerCase();
|
||||||
|
});
|
||||||
|
return names.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算初始坐标 左下角 0,0 为锚点
|
||||||
|
computeBasePosition() {
|
||||||
|
if (!this.rootDoc) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let _rect = this.rect;
|
||||||
|
let width = (_rect.right - _rect.left);
|
||||||
|
let height = (_rect.bottom - _rect.top);
|
||||||
|
|
||||||
|
this.size.width = width;
|
||||||
|
this.size.height = height;
|
||||||
|
|
||||||
|
// 位置 左下角为锚点
|
||||||
|
let x = _rect.left;
|
||||||
|
let y = (this.rootDoc.size.height - _rect.bottom);
|
||||||
|
|
||||||
|
this.position.x = x;
|
||||||
|
this.position.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据锚点计算坐标
|
||||||
|
updatePositionWithAR() {
|
||||||
|
if (!this.parent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let parent = this.parent;
|
||||||
|
while (parent) {
|
||||||
|
this.position.x -= parent.position.x;
|
||||||
|
this.position.y -= parent.position.y;
|
||||||
|
parent = parent.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this.position.x = this.position.x - this.parent.size.width * this.parent.anchorPoint.x + this.size.width * this.anchorPoint.x;
|
||||||
|
// this.position.y = this.position.y - this.parent.size.height * this.parent.anchorPoint.y + this.size.height * this.anchorPoint.y;
|
||||||
|
this.position.x = this.position.x - this.rootDoc.size.width * this.rootDoc.anchorPoint.x + this.size.width * this.anchorPoint.x;
|
||||||
|
this.position.y = this.position.y - this.rootDoc.size.height * this.rootDoc.anchorPoint.y + this.size.height * this.anchorPoint.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
67
psd2ui-tools/src/psd/PsdText.ts
Normal file
67
psd2ui-tools/src/psd/PsdText.ts
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
|
||||||
|
import { config } from "../config";
|
||||||
|
import { Color } from "../values/Color";
|
||||||
|
import { Vec2 } from "../values/Vec2";
|
||||||
|
import { PsdGroup } from "./PsdGroup";
|
||||||
|
import { PsdLayer } from "./PsdLayer";
|
||||||
|
|
||||||
|
export class PsdText extends PsdLayer{
|
||||||
|
declare parent: PsdGroup;
|
||||||
|
declare text: string;
|
||||||
|
declare fontSize: number;
|
||||||
|
declare font: string;
|
||||||
|
declare outline: { width: number, color: Color }; // 描边
|
||||||
|
declare offsetY: number;
|
||||||
|
|
||||||
|
|
||||||
|
parseSource(): boolean {
|
||||||
|
super.parseSource();
|
||||||
|
let textSource = this.source.text;
|
||||||
|
let style = textSource.style;
|
||||||
|
if(style){
|
||||||
|
let fillColor = style.fillColor;
|
||||||
|
if(fillColor){
|
||||||
|
this.color = new Color(fillColor.r,fillColor.g,fillColor.b,fillColor.a * 255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.text = textSource.text;
|
||||||
|
this.fontSize = style.fontSize;
|
||||||
|
|
||||||
|
this.offsetY = config.textOffsetY[this.fontSize] || config.textOffsetY["default"] || 0;
|
||||||
|
|
||||||
|
this.parseSolidFill();
|
||||||
|
this.parseStroke();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
onCtor(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 描边 */
|
||||||
|
parseStroke(){
|
||||||
|
if(this.source.effects?.stroke){
|
||||||
|
let stroke = this.source.effects?.stroke[0];
|
||||||
|
// 外描边
|
||||||
|
if(stroke?.enabled && stroke?.position === "outside"){
|
||||||
|
let color = stroke.color;
|
||||||
|
this.outline = {
|
||||||
|
width: stroke.size.value,
|
||||||
|
color: new Color(color.r,color.g,color.b,stroke.opacity * 255)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** 解析 颜色叠加 */
|
||||||
|
parseSolidFill() {
|
||||||
|
if(this.source.effects?.solidFill){
|
||||||
|
let solidFills = this.source.effects?.solidFill;
|
||||||
|
for (let i = 0; i < solidFills.length; i++) {
|
||||||
|
const solidFill = solidFills[i];
|
||||||
|
if(solidFill.enabled){
|
||||||
|
let color = solidFill.color;
|
||||||
|
this.color = new Color(color.r,color.g,color.b,solidFill.opacity * 255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
114
psd2ui-tools/src/utils/FileUtils.ts
Normal file
114
psd2ui-tools/src/utils/FileUtils.ts
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
|
||||||
|
import fs from 'fs-extra';
|
||||||
|
import path from 'path';
|
||||||
|
import crypto from "crypto";
|
||||||
|
|
||||||
|
|
||||||
|
class FileUtils {
|
||||||
|
|
||||||
|
// 深度遍历
|
||||||
|
DFS(root: string, callback?: (options: {isDirectory: boolean,fullPath: string, fileName: string,depth: number}) => void,depth = 0) {
|
||||||
|
let exists = fs.existsSync(root);
|
||||||
|
if (!exists) {
|
||||||
|
console.log(`FileUtils-> ${root} is not exists`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let files = fs.readdirSync(root);
|
||||||
|
let _cacheDepth = depth;
|
||||||
|
depth ++;
|
||||||
|
files.forEach((file) => {
|
||||||
|
let fullPath = path.join(root, file);
|
||||||
|
let stat = fs.lstatSync(fullPath);
|
||||||
|
let isDirectory = stat.isDirectory();
|
||||||
|
callback?.({isDirectory,fullPath,fileName: file,depth: _cacheDepth});
|
||||||
|
if (!isDirectory) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this.DFS(fullPath,callback,depth);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
filterFile(root: string, filter?: (fileName: string) => boolean) {
|
||||||
|
let exists = fs.existsSync(root);
|
||||||
|
if (!exists) {
|
||||||
|
console.log(`FileUtils-> ${root} is not exists`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var res: string[] = [];
|
||||||
|
let files = fs.readdirSync(root);
|
||||||
|
files.forEach((file) => {
|
||||||
|
let pathName = path.join(root, file);
|
||||||
|
let stat = fs.lstatSync(pathName);
|
||||||
|
let isDirectory = stat.isDirectory();
|
||||||
|
// 只对文件进行判断
|
||||||
|
if(!isDirectory){
|
||||||
|
let isPass = filter(file);
|
||||||
|
if(!isPass){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isDirectory) {
|
||||||
|
res.push(pathName);
|
||||||
|
} else {
|
||||||
|
res = res.concat(this.filterFile(pathName,filter));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
getFolderFiles(dir: string,type: "folder" | "file"){
|
||||||
|
let exists = fs.existsSync(dir);
|
||||||
|
if (!exists) {
|
||||||
|
console.log(`FileUtils-> ${dir} is not exists`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let res: {fullPath: string,basename: string}[] = [];
|
||||||
|
let files = fs.readdirSync(dir);
|
||||||
|
files.forEach((file) => {
|
||||||
|
let fullPath = path.join(dir, file);
|
||||||
|
let stat = fs.lstatSync(fullPath);
|
||||||
|
let isDirectory = stat.isDirectory();
|
||||||
|
if (isDirectory) {
|
||||||
|
if(type === 'folder'){
|
||||||
|
res.push({fullPath,basename: file});
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if(type === 'file'){
|
||||||
|
res.push({fullPath,basename: file});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async writeFile(fullPath: string, data: any) {
|
||||||
|
if(typeof data !== 'string'){
|
||||||
|
try {
|
||||||
|
data = JSON.stringify(data,null,2);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(`FileUtils->writeFile `,error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(`写入文件 ${fullPath}`);
|
||||||
|
|
||||||
|
let dir = path.dirname(fullPath);
|
||||||
|
await fs.mkdirp(dir);
|
||||||
|
await fs.writeFile(fullPath, data);
|
||||||
|
|
||||||
|
console.log(`写入完成 ${fullPath} `);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取文件的 md5 */
|
||||||
|
getMD5(buffer: Buffer | string){
|
||||||
|
if(typeof buffer === 'string'){
|
||||||
|
buffer = fs.readFileSync(buffer);
|
||||||
|
}
|
||||||
|
let md5 = crypto.createHash("md5").update(buffer).digest("hex");
|
||||||
|
return md5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export let fileUtils = new FileUtils();
|
60
psd2ui-tools/src/utils/Texture9Utils.ts
Normal file
60
psd2ui-tools/src/utils/Texture9Utils.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
|
||||||
|
import canvas from 'canvas';
|
||||||
|
|
||||||
|
export interface Border {
|
||||||
|
l?: number;
|
||||||
|
r?: number;
|
||||||
|
t?: number;
|
||||||
|
b?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Texture9Utils {
|
||||||
|
|
||||||
|
static safeBorder(_canvas: canvas.Canvas, border: Border) {
|
||||||
|
|
||||||
|
border.l = (border.l ?? border.r) || 0;
|
||||||
|
border.r = (border.r ?? border.l) || 0;
|
||||||
|
border.t = (border.t ?? border.b) || 0;
|
||||||
|
border.b = (border.b ?? border.t) || 0;
|
||||||
|
return border;
|
||||||
|
}
|
||||||
|
static split(_canvas: canvas.Canvas, border: Border): canvas.Canvas {
|
||||||
|
this.safeBorder(_canvas, border);
|
||||||
|
let cw = _canvas.width;
|
||||||
|
let ch = _canvas.height;
|
||||||
|
let space = 4;
|
||||||
|
let left = border.l || cw;
|
||||||
|
let right = border.r || cw;
|
||||||
|
let top = border.t || ch;
|
||||||
|
let bottom = border.b || ch;
|
||||||
|
if (border.b == 0 && border.t == 0 && border.l == 0 && border.r == 0) {
|
||||||
|
return _canvas;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (border.l + border.r > cw + space) {
|
||||||
|
console.log(`Texture9Utils-> 设置的九宫格 left, right 数据不合理,请重新设置`);
|
||||||
|
return _canvas;
|
||||||
|
}
|
||||||
|
if (border.b + border.t > ch + space) {
|
||||||
|
console.log(`Texture9Utils-> 设置的九宫格 bottom, top 数据不合理,请重新设置`);
|
||||||
|
return _canvas;
|
||||||
|
}
|
||||||
|
|
||||||
|
let newCanvas = canvas.createCanvas(Math.min(cw, border.l + border.r + space) || cw, Math.min(ch, border.b + border.t + space) || ch);
|
||||||
|
let ctx = newCanvas.getContext("2d");
|
||||||
|
|
||||||
|
// 左上
|
||||||
|
ctx.drawImage(_canvas, 0, 0, left + space, top + space, 0, 0, left + space, top + space);
|
||||||
|
|
||||||
|
// 左下
|
||||||
|
ctx.drawImage(_canvas, 0, ch - bottom, left + space, bottom, 0, top + space, left + space, bottom);
|
||||||
|
|
||||||
|
// 右上
|
||||||
|
ctx.drawImage(_canvas, cw - left, 0, right, top + space, left + space, 0, right, top + space);
|
||||||
|
|
||||||
|
// 右下
|
||||||
|
ctx.drawImage(_canvas, cw - left, ch - bottom, right, bottom, left + space, top + space, right, bottom);
|
||||||
|
|
||||||
|
return newCanvas;
|
||||||
|
}
|
||||||
|
}
|
98
psd2ui-tools/src/utils/Utils.ts
Normal file
98
psd2ui-tools/src/utils/Utils.ts
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import canvas from 'canvas';
|
||||||
|
import { PsdLayer } from "../psd/PsdLayer";
|
||||||
|
import fs from 'fs-extra';
|
||||||
|
import path from 'path';
|
||||||
|
import { PsdDocument } from '../psd/PsdDocument';
|
||||||
|
import { PsdImage } from '../psd/PsdImage';
|
||||||
|
|
||||||
|
// ------------decode-uuid
|
||||||
|
const BASE64_KEYS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
||||||
|
const values = new Array(123); // max char code in base64Keys
|
||||||
|
for (let i = 0; i < 123; ++i) { values[i] = 64; } // fill with placeholder('=') index
|
||||||
|
for (let i = 0; i < 64; ++i) { values[BASE64_KEYS.charCodeAt(i)] = i; }
|
||||||
|
|
||||||
|
// decoded value indexed by base64 char code
|
||||||
|
const BASE64_VALUES = values;
|
||||||
|
|
||||||
|
const HexChars = '0123456789abcdef'.split('');
|
||||||
|
|
||||||
|
const _t = ['', '', '', ''];
|
||||||
|
const UuidTemplate = _t.concat(_t, '-', _t, '-', _t, '-', _t, '-', _t, _t, _t);
|
||||||
|
const Indices = UuidTemplate.map((x, i) => x === '-' ? NaN : i).filter(isFinite);
|
||||||
|
|
||||||
|
|
||||||
|
let HexMap = {}
|
||||||
|
{
|
||||||
|
for (let i = 0; i < HexChars.length; i++) {
|
||||||
|
let char = HexChars[i]
|
||||||
|
HexMap[char] = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Utils {
|
||||||
|
|
||||||
|
|
||||||
|
uuid() {
|
||||||
|
var d = new Date().getTime();
|
||||||
|
if (globalThis.performance && typeof globalThis.performance.now === "function") {
|
||||||
|
d += performance.now(); //use high-precision timer if available
|
||||||
|
}
|
||||||
|
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||||
|
var r = (d + Math.random() * 16) % 16 | 0;
|
||||||
|
d = Math.floor(d / 16);
|
||||||
|
return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);
|
||||||
|
});
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
decodeUuid(base64) {
|
||||||
|
const strs = base64.split('@');
|
||||||
|
const uuid = strs[0];
|
||||||
|
if (uuid.length !== 22) {
|
||||||
|
return base64;
|
||||||
|
}
|
||||||
|
UuidTemplate[0] = base64[0];
|
||||||
|
UuidTemplate[1] = base64[1];
|
||||||
|
for (let i = 2, j = 2; i < 22; i += 2) {
|
||||||
|
const lhs = BASE64_VALUES[base64.charCodeAt(i)];
|
||||||
|
const rhs = BASE64_VALUES[base64.charCodeAt(i + 1)];
|
||||||
|
UuidTemplate[Indices[j++]] = HexChars[lhs >> 2];
|
||||||
|
UuidTemplate[Indices[j++]] = HexChars[((lhs & 3) << 2) | rhs >> 4];
|
||||||
|
UuidTemplate[Indices[j++]] = HexChars[rhs & 0xF];
|
||||||
|
}
|
||||||
|
return base64.replace(uuid, UuidTemplate.join(''));
|
||||||
|
}
|
||||||
|
// 压缩uuid
|
||||||
|
compressUuid(fullUuid) {
|
||||||
|
const strs = fullUuid.split('@');
|
||||||
|
const uuid: string = strs[0];
|
||||||
|
if (uuid.length !== 36) {
|
||||||
|
return fullUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
let zipUuid = []
|
||||||
|
zipUuid[0] = uuid[0];
|
||||||
|
zipUuid[1] = uuid[1];
|
||||||
|
let cleanUuid = uuid.replace('-', '').replace('-', '').replace('-', '').replace('-', '')
|
||||||
|
|
||||||
|
for (let i = 2, j = 2; i < 32; i += 3) {
|
||||||
|
|
||||||
|
const left = HexMap[String.fromCharCode(cleanUuid.charCodeAt(i))];
|
||||||
|
const mid = HexMap[String.fromCharCode(cleanUuid.charCodeAt(i + 1))];
|
||||||
|
const right = HexMap[String.fromCharCode(cleanUuid.charCodeAt(i + 2))];
|
||||||
|
|
||||||
|
zipUuid[j++] = BASE64_KEYS[(left << 2) + (mid >> 2)]
|
||||||
|
zipUuid[j++] = BASE64_KEYS[((mid & 3) << 4) + right]
|
||||||
|
}
|
||||||
|
return fullUuid.replace(uuid, zipUuid.join(''));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
isNumber(val){
|
||||||
|
return (!isNaN(parseFloat(val)) && isFinite(val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const utils = new Utils();
|
98
psd2ui-tools/src/utils/UuidUtils.ts
Normal file
98
psd2ui-tools/src/utils/UuidUtils.ts
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
var Uuid = require('node-uuid');
|
||||||
|
|
||||||
|
var Base64KeyChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
|
||||||
|
var AsciiTo64 = new Array(128);
|
||||||
|
for (var i = 0; i < 128; ++i) { AsciiTo64[i] = 0; }
|
||||||
|
for (i = 0; i < 64; ++i) { AsciiTo64[Base64KeyChars.charCodeAt(i)] = i; }
|
||||||
|
|
||||||
|
var Reg_Dash = /-/g;
|
||||||
|
var Reg_Uuid = /^[0-9a-fA-F-]{36}$/;
|
||||||
|
var Reg_NormalizedUuid = /^[0-9a-fA-F]{32}$/;
|
||||||
|
var Reg_CompressedUuid = /^[0-9a-zA-Z+/]{22,23}$/;
|
||||||
|
|
||||||
|
export class UuidUtils {
|
||||||
|
|
||||||
|
// 加了这个标记后,字符串就不可能会是 uuid 了。
|
||||||
|
static NonUuidMark: '.'
|
||||||
|
|
||||||
|
// 压缩后的 uuid 可以减小保存时的尺寸,但不能做为文件名(因为无法区分大小写并且包含非法字符)。
|
||||||
|
// 默认将 uuid 的后面 27 位压缩成 18 位,前 5 位保留下来,方便调试。
|
||||||
|
// fc991dd7-0033-4b80-9d41-c8a86a702e59 -> fc9913XADNLgJ1ByKhqcC5Z
|
||||||
|
// 如果启用 min 则将 uuid 的后面 30 位压缩成 20 位,前 2 位保留不变。
|
||||||
|
// fc991dd7-0033-4b80-9d41-c8a86a702e59 -> fcmR3XADNLgJ1ByKhqcC5Z
|
||||||
|
/*
|
||||||
|
* @param {Boolean} [min=false]
|
||||||
|
*/
|
||||||
|
static compressUuid (uuid, min) {
|
||||||
|
if (Reg_Uuid.test(uuid)) {
|
||||||
|
uuid = uuid.replace(Reg_Dash, '');
|
||||||
|
}
|
||||||
|
else if (!Reg_NormalizedUuid.test(uuid)) {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
var reserved = (min === true) ? 2 : 5;
|
||||||
|
return UuidUtils.compressHex(uuid, reserved);
|
||||||
|
}
|
||||||
|
|
||||||
|
static compressHex (hexString, reservedHeadLength) {
|
||||||
|
var length = hexString.length;
|
||||||
|
var i;
|
||||||
|
if (typeof reservedHeadLength !== 'undefined') {
|
||||||
|
i = reservedHeadLength;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
i = length % 3;
|
||||||
|
}
|
||||||
|
var head = hexString.slice(0, i);
|
||||||
|
var base64Chars = [];
|
||||||
|
while (i < length) {
|
||||||
|
var hexVal1 = parseInt(hexString[i], 16);
|
||||||
|
var hexVal2 = parseInt(hexString[i + 1], 16);
|
||||||
|
var hexVal3 = parseInt(hexString[i + 2], 16);
|
||||||
|
base64Chars.push(Base64KeyChars[(hexVal1 << 2) | (hexVal2 >> 2)]);
|
||||||
|
base64Chars.push(Base64KeyChars[((hexVal2 & 3) << 4) | hexVal3]);
|
||||||
|
i += 3;
|
||||||
|
}
|
||||||
|
return head + base64Chars.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
static decompressUuid (str) {
|
||||||
|
if (str.length === 23) {
|
||||||
|
// decode base64
|
||||||
|
var hexChars = [];
|
||||||
|
for (var i = 5; i < 23; i += 2) {
|
||||||
|
var lhs = AsciiTo64[str.charCodeAt(i)];
|
||||||
|
var rhs = AsciiTo64[str.charCodeAt(i + 1)];
|
||||||
|
hexChars.push((lhs >> 2).toString(16));
|
||||||
|
hexChars.push((((lhs & 3) << 2) | rhs >> 4).toString(16));
|
||||||
|
hexChars.push((rhs & 0xF).toString(16));
|
||||||
|
}
|
||||||
|
//
|
||||||
|
str = str.slice(0, 5) + hexChars.join('');
|
||||||
|
}
|
||||||
|
else if (str.length === 22) {
|
||||||
|
// decode base64
|
||||||
|
var hexChars = [];
|
||||||
|
for (var i = 2; i < 22; i += 2) {
|
||||||
|
var lhs = AsciiTo64[str.charCodeAt(i)];
|
||||||
|
var rhs = AsciiTo64[str.charCodeAt(i + 1)];
|
||||||
|
hexChars.push((lhs >> 2).toString(16));
|
||||||
|
hexChars.push((((lhs & 3) << 2) | rhs >> 4).toString(16));
|
||||||
|
hexChars.push((rhs & 0xF).toString(16));
|
||||||
|
}
|
||||||
|
//
|
||||||
|
str = str.slice(0, 2) + hexChars.join('');
|
||||||
|
}
|
||||||
|
return [str.slice(0, 8), str.slice(8, 12), str.slice(12, 16), str.slice(16, 20), str.slice(20)].join('-');
|
||||||
|
}
|
||||||
|
|
||||||
|
static isUuid (str) {
|
||||||
|
return Reg_CompressedUuid.test(str) || Reg_NormalizedUuid.test(str) || Reg_Uuid.test(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uuid () {
|
||||||
|
var uuid = Uuid.v4();
|
||||||
|
return UuidUtils.compressUuid(uuid, true);
|
||||||
|
}
|
||||||
|
};
|
40
psd2ui-tools/src/values/Color.ts
Normal file
40
psd2ui-tools/src/values/Color.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
export class Color{
|
||||||
|
declare r: number;
|
||||||
|
declare g: number;
|
||||||
|
declare b: number;
|
||||||
|
declare a: number;
|
||||||
|
constructor(r: number,g: number,b: number,a: number){
|
||||||
|
this.r = Math.ceil(r || 0);
|
||||||
|
this.g = Math.ceil(g || 0);
|
||||||
|
this.b = Math.ceil(b || 0);
|
||||||
|
this.a = Math.ceil(a || 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
set(color: Color){
|
||||||
|
this.r = Math.ceil(color.r || 0);
|
||||||
|
this.g = Math.ceil(color.g || 0);
|
||||||
|
this.b = Math.ceil(color.b || 0);
|
||||||
|
this.a = Math.ceil(color.a || 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public toHEX (fmt: '#rgb' | '#rrggbb' | '#rrggbbaa' = '#rrggbb') {
|
||||||
|
const prefix = '0';
|
||||||
|
// #rrggbb
|
||||||
|
const hex = [
|
||||||
|
(this.r < 16 ? prefix : '') + (this.r).toString(16),
|
||||||
|
(this.g < 16 ? prefix : '') + (this.g).toString(16),
|
||||||
|
(this.b < 16 ? prefix : '') + (this.b).toString(16),
|
||||||
|
];
|
||||||
|
const i = -1;
|
||||||
|
if (fmt === '#rgb') {
|
||||||
|
hex[0] = hex[0][0];
|
||||||
|
hex[1] = hex[1][0];
|
||||||
|
hex[2] = hex[2][0];
|
||||||
|
} else if (fmt === '#rrggbbaa') {
|
||||||
|
hex.push((this.a < 16 ? prefix : '') + (this.a).toString(16));
|
||||||
|
}
|
||||||
|
return hex.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
23
psd2ui-tools/src/values/Rect.ts
Normal file
23
psd2ui-tools/src/values/Rect.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
export class Rect{
|
||||||
|
declare left: number;
|
||||||
|
declare right: number;
|
||||||
|
declare top: number;
|
||||||
|
declare bottom: number;
|
||||||
|
constructor(left: number | Rect = 0,right = 0,top = 0,bottom = 0) {
|
||||||
|
if(typeof left == 'object'){
|
||||||
|
this.set(left);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.left = left || 0;
|
||||||
|
this.right = right || 0;
|
||||||
|
this.top = top || 0;
|
||||||
|
this.bottom = bottom || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
set(rect: Rect){
|
||||||
|
this.left = rect.left;
|
||||||
|
this.right = rect.right;
|
||||||
|
this.top = rect.top;
|
||||||
|
this.bottom = rect.bottom;
|
||||||
|
}
|
||||||
|
}
|
8
psd2ui-tools/src/values/Size.ts
Normal file
8
psd2ui-tools/src/values/Size.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export class Size{
|
||||||
|
declare width: number;
|
||||||
|
declare height: number;
|
||||||
|
constructor(width: number = 0,height: number = 0) {
|
||||||
|
this.width = width || 0;
|
||||||
|
this.height = height || 0;
|
||||||
|
}
|
||||||
|
}
|
8
psd2ui-tools/src/values/Vec2.ts
Normal file
8
psd2ui-tools/src/values/Vec2.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export class Vec2{
|
||||||
|
declare x: number;
|
||||||
|
declare y: number;
|
||||||
|
constructor(x: number = 0,y: number = 0) {
|
||||||
|
this.x = x || 0;
|
||||||
|
this.y = y || 0;
|
||||||
|
}
|
||||||
|
}
|
10
psd2ui-tools/src/values/Vec3.ts
Normal file
10
psd2ui-tools/src/values/Vec3.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export class Vec3{
|
||||||
|
declare x: number;
|
||||||
|
declare y: number;
|
||||||
|
declare z: number;
|
||||||
|
constructor(x: number = 0,y: number = 0,z: number = 0) {
|
||||||
|
this.x = x || 0;
|
||||||
|
this.y = y || 0;
|
||||||
|
this.z = z || 0;
|
||||||
|
}
|
||||||
|
}
|
15
psd2ui-tools/test/_test.config.json
Normal file
15
psd2ui-tools/test/_test.config.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"cc.Label": {
|
||||||
|
"__type__": "e4f88adp3hERoJ48DZ2PSAl",
|
||||||
|
"_N$file":{
|
||||||
|
"__uuid__": "803c185c-9442-4b99-af1a-682f877539ab"
|
||||||
|
},
|
||||||
|
"_isSystemFontUsed": false,
|
||||||
|
"isFixNumber": true
|
||||||
|
},
|
||||||
|
"textOffsetY":{
|
||||||
|
"default": -3,
|
||||||
|
"36": -3
|
||||||
|
},
|
||||||
|
"textLineHeightOffset": 4
|
||||||
|
}
|
BIN
psd2ui-tools/test/demo.psd
Normal file
BIN
psd2ui-tools/test/demo.psd
Normal file
Binary file not shown.
BIN
psd2ui-tools/test/png9.psd
Normal file
BIN
psd2ui-tools/test/png9.psd
Normal file
Binary file not shown.
104
psd2ui-tools/tsconfig.json
Normal file
104
psd2ui-tools/tsconfig.json
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||||
|
|
||||||
|
/* Projects */
|
||||||
|
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||||
|
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||||
|
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||||
|
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||||
|
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||||
|
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||||
|
|
||||||
|
/* Language and Environment */
|
||||||
|
"target": "ES2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||||
|
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||||
|
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||||
|
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||||
|
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||||
|
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||||
|
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||||
|
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||||
|
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||||
|
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||||
|
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||||
|
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||||
|
|
||||||
|
/* Modules */
|
||||||
|
"module": "commonjs", /* Specify what module code is generated. */
|
||||||
|
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||||
|
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||||
|
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||||
|
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||||
|
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||||
|
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||||
|
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||||
|
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||||
|
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||||
|
"resolveJsonModule": true, /* Enable importing .json files. */
|
||||||
|
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||||
|
|
||||||
|
/* JavaScript Support */
|
||||||
|
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||||
|
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||||
|
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||||
|
|
||||||
|
/* Emit */
|
||||||
|
"declaration": false, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||||
|
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||||
|
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||||
|
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||||
|
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||||
|
"outDir": "./dist", /* Specify an output folder for all emitted files. */
|
||||||
|
// "removeComments": true, /* Disable emitting comments. */
|
||||||
|
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||||
|
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||||
|
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
||||||
|
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||||
|
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||||
|
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||||
|
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||||
|
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||||
|
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||||
|
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||||
|
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||||
|
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||||
|
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||||
|
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||||
|
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||||
|
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||||
|
|
||||||
|
/* Interop Constraints */
|
||||||
|
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||||
|
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||||
|
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||||
|
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||||
|
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||||
|
|
||||||
|
/* Type Checking */
|
||||||
|
"strict": false, /* Enable all strict type-checking options. */
|
||||||
|
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||||
|
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||||
|
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||||
|
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||||
|
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||||
|
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||||
|
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||||
|
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||||
|
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||||
|
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||||
|
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||||
|
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||||
|
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||||
|
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||||
|
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||||
|
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||||
|
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||||
|
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||||
|
|
||||||
|
/* Completeness */
|
||||||
|
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||||
|
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||||
|
},
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user