Commit e8f81782 authored by Master Q's avatar Master Q

测试看看

parent 2a0a7d35
......@@ -9,6 +9,7 @@
// ["@babel/plugin-transform-runtime", {
// // "corejs": 3
// }],
["@babel/plugin-proposal-decorators", { "legacy": true }],
"@babel/plugin-proposal-optional-chaining",
"./babel-plugins/babel-plugins-test.js"
]
......
......@@ -52,7 +52,10 @@
<script src="output.js"></script>
<script type="module">
window.addEventListener("load", function () {
const canvas = document.getElementById('stage')
canvas.width = document.body.clientWidth * (window.devicePixelRatio || 1)
canvas.height = document.body.clientHeight * (window.devicePixelRatio || 1)
new Main(canvas)
})
</script>
......
This diff is collapsed.
declare module SvgaParser {
/**
* 加载方法
* @param url 资源路径
* @param success
* @param failure
*/
export function loadSvga(url: string, success: (videoItem: VideoEntity) => void, failure?: (err: string) => void): void;
/**
* 导出只是当作类型接口用
*/
export interface VideoEntity {
/**
* SVGA 文件版本
*/
version: string;
/**
* 影片尺寸
*/
videoSize: {
width: number;
height: number;
};
/**
* 帧率,60,30等每秒
*/
FPS: number;
/**
* 总帧数
*/
frames: number;
/**
* base64图片数据记录
*/
images: {
[key: string]: string
};
/**
* 图片是否已被缓存,缓存全局,注意名字覆盖
*/
hasBeenCached: boolean;
/**
* sprite对象数据
*/
sprites: SpriteEntity[];
}
interface SpriteEntity {
/**
* 标识
*/
matteKey: string;
/**
* 图片key值
*/
imageKey: string;
/**
* 帧数据数组
*/
frames: FrameEntity[];
}
/**
* 还有很多其他数据,暂不需要,比如矢量路径和遮罩路径暂时都无
*/
interface FrameEntity {
/**
* 透明度
*/
alpha: number;
/**
* 2维矩阵数据
*/
transform: {
a: number,
b: number,
c: number,
d: number,
tx: number,
ty: number,
};
}
}
declare module "svga-parser" { export = SvgaParser; }
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
// Type definitions for howler.js v2.1.1
// Project: https://github.com/goldfire/howler.js
// Definitions by: Pedro Casaubon <https://github.com/xperiments>
// Alexander Leon <https://github.com/alien35>
// Nicholas Higgins <https://github.com/nicholashza>
// Carlos Urango <https://github.com/cjurango>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
interface HowlerGlobal {
mute(muted: boolean): this;
volume(): number;
volume(volume: number): this;
codecs(ext: string): boolean;
unload(): this;
usingWebAudio: boolean;
html5PoolSize: number;
noAudio: boolean;
autoUnlock: boolean;
autoSuspend: boolean;
ctx: AudioContext;
masterGain: GainNode;
stereo(pan: number): this;
pos(x: number, y: number, z: number): this | void;
orientation(x: number, y: number, z: number, xUp: number, yUp: number, zUp: number): this | void;
}
declare let Howler: HowlerGlobal;
interface IHowlSoundSpriteDefinition {
[name: string]: [number, number] | [number, number, boolean]
}
interface IHowlProperties {
src: string | string[];
volume?: number;
html5?: boolean;
loop?: boolean;
preload?: boolean;
autoplay?: boolean;
mute?: boolean;
sprite?: IHowlSoundSpriteDefinition;
rate?: number;
pool?: number;
format?: string[] | string;
xhrWithCredentials?: boolean;
onload?: () => void;
onloaderror?: (soundId: number, error: any) => void;
onplay?: (soundId: number) => void;
onplayerror?: (soundId: number, error: any) => void;
onend?: (soundId: number) => void;
onpause?: (soundId: number) => void;
onstop?: (soundId: number) => void;
onmute?: (soundId: number) => void;
onvolume?: (soundId: number) => void;
onrate?: (soundId: number) => void;
onseek?: (soundId: number) => void;
onfade?: (soundId: number) => void;
onunlock?: (soundId: number) => void;
}
interface Howl {
play(spriteOrId?: string | number): number; // .play() is not chainable; the other methods are
pause(id?: number): this;
stop(id?: number): this;
mute(): boolean;
mute(muted: boolean, id?: number): this;
volume(): number;
volume(idOrSetVolume: number): this | number;
volume(volume: number, id: number): this;
fade(from: number, to: number, duration: number, id?: number): this;
rate(): number;
rate(idOrSetRate: number): this | number;
rate(rate: number, id: number): this;
seek(seek?: number, id?: number): this | number;
loop(id?: number): boolean;
loop(loop: boolean, id?: number): this;
playing(id?: number): boolean;
duration(id?: number): number;
state(): 'unloaded' | 'loading' | 'loaded';
load(): this;
unload(): void;
on(event: 'load', callback: () => void, id?: number): this;
on(event: 'loaderror', callback: (soundId: number, error: any) => void, id?: number): this;
on(event: 'play', callback: (soundId: number) => void, id?: number): this;
on(event: 'playerror', callback: (soundId: number, error: any) => void, id?: number): this;
on(event: 'end', callback: (soundId: number) => void, id?: number): this;
on(event: 'pause', callback: (soundId: number) => void, id?: number): this;
on(event: 'stop', callback: (soundId: number) => void, id?: number): this;
on(event: 'mute', callback: (soundId: number) => void, id?: number): this;
on(event: 'volume', callback: (soundId: number) => void, id?: number): this;
on(event: 'rate', callback: (soundId: number) => void, id?: number): this;
on(event: 'seek', callback: (soundId: number) => void, id?: number): this;
on(event: 'fade', callback: (soundId: number) => void, id?: number): this;
on(event: string, callback: Function, id?: number): this;
on(event: 'unlock', callback: (soundId: number) => void, id?: number): this;
once(event: 'load', callback: () => void, id?: number): this;
once(event: 'loaderror', callback: (soundId: number, error: any) => void, id?: number): this;
once(event: 'play', callback: (soundId: number) => void, id?: number): this;
once(event: 'playerror', callback: (soundId: number, error: any) => void, id?: number): this;
once(event: 'end', callback: (soundId: number) => void, id?: number): this;
once(event: 'pause', callback: (soundId: number) => void, id?: number): this;
once(event: 'stop', callback: (soundId: number) => void, id?: number): this;
once(event: 'mute', callback: (soundId: number) => void, id?: number): this;
once(event: 'volume', callback: (soundId: number) => void, id?: number): this;
once(event: 'rate', callback: (soundId: number) => void, id?: number): this;
once(event: 'seek', callback: (soundId: number) => void, id?: number): this;
once(event: 'fade', callback: (soundId: number) => void, id?: number): this;
once(event: string, callback: Function, id?: number): this;
once(event: 'unlock', callback: (soundId: number) => void, id?: number): this;
off(event: string, callback?: Function, id?: number): this;
off(): this;
stereo(pan: number, id?: number): this | void;
pos(x: number, y: number, z: number, id?: number): this | void;
orientation(x: number, y: number, z: number, xUp: number, yUp: number, zUp: number): this | void;
pannerAttr(o: {
coneInnerAngle?: number,
coneOuterAngle?: number, coneOuterGain?: number,
distanceModel: 'inverse' | 'linear', maxDistance: number,
panningModel: 'HRTF' | 'equalpower', refDistance: number, rolloffFactor: number
}, id?: number): this;
}
interface HowlStatic {
new(properties: IHowlProperties): Howl;
}
declare let Howl: HowlStatic;
declare module "howler" {
export let Howler: HowlerGlobal;
export let Howl: HowlStatic;
}
/*
* lib.d.ts
* Created by 还有醋v on 2022/5/27.
* Copyright © 2022 haiyoucuv. All rights reserved.
*/
declare const __ENV__: "development" | "production";
declare const __version__: string;
declare const __buildDate__: string;
/**
* shader文件类型
*/
declare module '*.vert' {
const source: string;
export default source;
}
declare module '*.frag' {
const source: string;
export default source;
}
declare module '*.vs' {
const source: string;
export default source;
}
declare module '*.fs' {
const source: string;
export default source;
}
declare module '*.glsl' {
const source: string;
export default source;
}
This diff is collapsed.
{
"groups": [],
"path": "./resource/"
}
\ No newline at end of file
var fs = require("fs");
var del = require('del');
function delDir(path, isSelf) {
let files = [];
if (fs.existsSync(path)) {
files = fs.readdirSync(path);
files.forEach((file, index) => {
let curPath = path + "/" + file;
if (fs.statSync(curPath).isDirectory()) {
delDir(curPath); //递归删除文件夹
} else {
fs.unlinkSync(curPath); //删除文件
}
});
if (!isSelf) fs.rmdirSync(path);
}
}
var paths = './released/';//设置删除路径
// delDir(paths, true);//删除文件夹
del(paths).then(() => {
fs.mkdirSync(paths);
}).catch(()=>{
fs.mkdirSync(paths);
})
\ No newline at end of file
......@@ -5,6 +5,36 @@ const config = require('../webpack.dev.js')
const compiler = webpack(config)
const server = new webpackDevServer(config.devServer, compiler)
const path = require("path");
const runScript = require("./runScript");
const fs = require("fs");
// const skinUrl = "./resource/skin.json";
// fs.watchFile(skinUrl, { persistent: true, interval: 2000 }, (curr, prev) => {
// const skinDataAll = fs.readFileSync(skinUrl)
// var endFile = `export const SkinJson = ${skinDataAll}`
// fs.writeFileSync("./src/SkinJson.ts", endFile);
// console.log("SkinJson.ts文件已更新")
// })
// 修改resource文件夹任意内容自动刷新资源
fs.watch('./resource/', { recursive: true }, (event, filename) => {
if (
filename === ".DS_Store"
|| filename === "res.json"
|| filename === "skin.json"
) return;
runScript(path.resolve('./scripts/flushRes.js'), (err) => {
console.log(err);
});
});
runScript(path.resolve('./scripts/flushRes.js'), (err) => {
console.log(err);
});
const argv = process.argv;
const runServer = async () => {
......
//生成res.json
//遍历资源文件夹,生成
var fs = require('fs');
var path = require("path");
var readPath = "./resource/"
var files = fs.readdirSync(readPath);
var obj = { groups: [] };//每项包括keys合name
files.forEach(function (file) {
//路径
let fPath = path.join(readPath, file);
//只处理文件夹
if (fs.statSync(fPath).isDirectory()) {
//继续读每个子文件夹,json和png名字有相同的,只留json,
var sonFiles = fs.readdirSync(fPath);
//没有文件
if (!sonFiles.length) return
//取出所有json
var jsons = sonFiles.filter((f) => { return f.substr(-5) == ".json" })
//去掉json所带png的图片
sonFiles = sonFiles.filter((f) => { return jsons.indexOf(f.substring(0, f.length - 4) + ".json") == -1 })
//去掉mac上的缓存文件
sonFiles = sonFiles.filter((f) => { return f != '.DS_Store' })
var group = {
keys: "",
name: file
}
for (var i = 0; i < sonFiles.length; i++) {
if (i != 0) group.keys += ",";
group.keys += sonFiles[i]
}
obj.groups.push(group)
}
})
obj.path="./resource/"
console.log("资源更新完成")
//生成json
fs.writeFileSync(readPath + "res.json", JSON.stringify(obj, "", "\t"));
//TS也更新
var endPath = './src/';
var endFile = `export const ResJson = ${JSON.stringify(obj, "", "\t")}`
fs.writeFileSync(endPath + "ResJson.ts", endFile);
\ No newline at end of file
var fs = require("fs");
// fs.writeFileSync(
// "./released/output.js",
// fs.readFileSync("./output.js")
// )
var endPath = './released/';
fs.writeFileSync(endPath + "output.js",
// 'import * as FYGE from "fyge-tbmini";\n' +
// 'import * as FYGE from "fyge";\n' +//以后改成这个
// 'import * as SvgaParser from "svga-parser";\n' +
'/* eslint-disable */\n' +
fs.readFileSync("./output.js"));
console.log("js生成")
/*
* runScript.js
* Created by 还有醋v on 2021/5/8.
* Copyright © 2021 haiyoucuv. All rights reserved.
*/
const childProcess = require('child_process');
module.exports = function runScript(scriptPath, callback) {
let invoked = false;
const process = childProcess.fork(scriptPath);
process.on('error', function (err) {
if (invoked) return;
invoked = true;
callback(err);
});
process.on('exit', function (code) {
if (invoked) return;
invoked = true;
const err = code === 0 ? null : new Error('exit code ' + code);
callback(err);
});
}
var fs = require('fs');
var path = require('path');
const co = require('co');
const OSS = require('ali-oss');
const chalk = require('chalk');
const ProgressBar = require('progress');
class TuiaAutoUpload {
constructor(props, type) {
this.type = type;
const defaultOptions = {
dir: undefined,
originDir: undefined
}
this.options = Object.assign({}, defaultOptions, props);
if (!this.options.dir || !this.options.originDir) {
console.log(chalk.red('缺少参数,初始化失败'))
return;
}
this.init();
}
init() {
var _this = this;
this.client = new OSS({
region: 'oss-cn-hangzhou',
accessKeyId: 'LTAI4Fw25WcfcGv7FvcHoiHK',
accessKeySecret: 'NZk1NtT9J5HFaAolNbtQdzTzLLvLYm',
bucket: _this.type === 'prod' ? 'duiba' : 'daily-duiba'
});
this.bar = new ProgressBar(chalk.yellow(` 文件上传中 [:bar] :current/${this.files().length} :percent :elapseds`), {
complete: '●',
incomplete: '○',
width: 20,
total: this.files().length,
callback: () => {
console.log(chalk.green('\n All complete.'));
console.log(chalk.blue(`\n 本次队列文件共${this.files().length}个,已存在文件${this.existFiles}个,上传文件${this.uploadFiles}个,上传失败文件${this.errorFiles}个\n`));
}
})
return this;
}
files() {
var _this = this;
if (this._files) return this._files;
this._files = [];
/**
* 文件遍历方法
* @param filePath 需要遍历的文件路径
*/
function fileDisplay(filePath) {
//根据文件路径读取文件,返回文件列表
var files = fs.readdirSync(filePath);
files.forEach(function (filename) {
//获取当前文件的绝对路径
var filedir = path.join(filePath, filename);
//根据文件路径获取文件信息,返回一个fs.Stats对象
var stats = fs.statSync(filedir);
var isFile = stats.isFile();//是文件
var isDir = stats.isDirectory();//是文件夹
if (isFile) {
var sep = '/';
if ('win32' == process.platform)
sep = '\\';
var newDirArr = filedir.split(sep);
newDirArr.shift();
_this._files.push(newDirArr.join('/'));
}
if (isDir) {
fileDisplay(filedir);//递归,如果是文件夹,就继续遍历该文件夹下面的文件
}
});
}
//调用文件遍历方法
fileDisplay(this.options.dir);
return this._files;
}
start() {
this.files().map((file, index) => {
let _this = this;
const path1 = path.join(path.resolve(__dirname, '..'), 'released', file);
let originFile;
this.existFiles = 0;
this.uploadFiles = 0;
this.errorFiles = 0;
co(function* () {
const originPath = `${_this.options.originDir}${file}`;
try {
originFile = yield _this.client.head(originPath);
} catch (error) {
originFile = error;
}
if (_this.type === 'prod') {
if (originFile.status === 404) {
yield _this.client.put(originPath, path1);
_this.uploadFiles += 1;
} else {
_this.existFiles += 1;
}
} else if (_this.type === 'dev') {
if (originFile.status === 404 || originFile.status === 200) {
_this.existFiles += 1;
}
yield _this.client.put(originPath, path1, {
headers: {
'Cache-Control': 'no-cache'
}
})
_this.uploadFiles += 1;
}
_this.bar.tick();
}).catch(function (err) {
_this.errorFiles += 1;
console.log(err);
});
});
}
}
const configFileName = 'project.json';
if (!fs.existsSync(configFileName)) {
throw new Error(`${configFileName}不存在.`)
}
let config = fs.readFileSync('project.json');
config = JSON.parse(config + '');
if (!config.type) {
throw new Error(`${configFileName}的type不存在.`)
}
if (!config.name) {
throw new Error(`${configFileName}的name不存在.`)
}
const now = new Date();
const version = Math.round(now.getTime() / 1000);
console.log(`版本号:
${version}`)
const autoupload = new TuiaAutoUpload({
dir: './released/',
// dir: path.join(__dirname, './released/'),
originDir: `/db_games/${config.type}/${config.name}/${version}/`
}, "prod")
autoupload.start()
var iconv = require('iconv-lite');
var readPath = "./released/resource/";
//读取json文件
var data = iconv.decode(fs.readFileSync(readPath + "res.json"), "utf-8");//GBK
//反序列化
data = JSON.parse(data);
data.path = `https://yun.duiba.com.cn/db_games/${config.type}/${config.name}/${version}/resource/`
//写入目标文件夹,可配置,每个项目必须修改,或者直接和project的保持一致(淘宝项目文件固定后)
var endPath = './src/';
var endFile = `export const ResJson = ${JSON.stringify(data, "", "\t")}`
endFile = endFile.replace(
`"path": "${data.path}"`,
`// eslint-disable-next-line\n\t"path": "${data.path}"`
);
fs.writeFileSync(endPath + "ResJson.ts", endFile);
//根据参数吧,有就是web。需要生成皮肤和js,没有就不执行后续
let arg = process.argv.splice(2);
if (!arg[0]) return;
//有版本号了,模板也生成吧
require("./createHtml")(`${config.type}/${config.name}/${version}`);
var uploadSingleJs = require("./uploadSingleJs")
var exec = require('child_process').exec;
//打包js
exec("webpack --config webpack.prod.js", { encoding: 'utf8' }, (e) => {
if (e) {
console.log('webpack error:', e)
return
}
uploadSingleJs(`${config.type}/${config.name}/${version}`)
//再打印一次
console.log(`版本号:
${version}`)
})
\ No newline at end of file
const co = require('co');
const OSS = require('ali-oss');
var fs = require('fs');
let arg = process.argv.splice(2);
//只打包js时,自执行上传
if(arg[0])uploadSingleJs();
function uploadSingleJs(url) {
if (!url) {//不传的时候
const configFileName = 'project.json';
if (!fs.existsSync(configFileName)) {
throw new Error(`${configFileName}不存在.`)
}
let config = fs.readFileSync('project.json');
config = JSON.parse(config + '');
if (!config.type) {
throw new Error(`${configFileName}的type不存在.`)
}
if (!config.name) {
throw new Error(`${configFileName}的name不存在.`)
}
const now = new Date();
const version = Math.round(now.getTime() / 1000);
console.log(`版本号:
${version}`)
url = `${config.type}/${config.name}/${version}`;
require("./createHtml")(`${config.type}/${config.name}/${version}`);
}
//单js文件上传
co(function* () {
const originPath = `/db_games/${url}/output.js`;
var client = new OSS({
region: 'oss-cn-hangzhou',
accessKeyId: 'LTAI4Fw25WcfcGv7FvcHoiHK',
accessKeySecret: 'NZk1NtT9J5HFaAolNbtQdzTzLLvLYm',
bucket: 'duiba'
})
var originFile;
try {
originFile = yield client.head(originPath);
} catch (error) {
originFile = error;
}
if (originFile.status === 404)
yield client.put(originPath, "./output.js");
})
}
module.exports = uploadSingleJs
\ No newline at end of file
import Dream from ".."
import { UseAni } from "../../modules/UseDecorator/useAni"
import {
EventsMap
} from '../types'
type DreamFC<T extends Record<string, any> = {}, R extends any = FYGE.Container> = (props: {
ref?: (c: R) => void,
inlineProps?: Record<string, any>
children?: FYGE.Container[], // 这样写的话,外面也能提示了 -。-
} & T) => FYGE.Container
type OriginalElementWidthEvents<extraProps={}, T = FYGE.Container> = DreamFC<EventsMap & extraProps, T>
type DrawData = {
'rect': [number, number, number, number],
'circle': [number, number, number]
}
export const FPShapeOfRectV2: DreamFC<Partial<{
type: keyof DrawData,
drawData: any,
fillColor: string,
alpha: number
}>> = function ({
type = 'rect',
fillColor = '#000000',
alpha = 1,
drawData = [0, 0, 750, 1624]
}) {
const graphicsIns = new FYGE.Shape()
graphicsIns.beginFill(fillColor)
if (type === 'rect') {
graphicsIns.drawRect(drawData[0], drawData[1], drawData[2], drawData[3])
} else if (type === 'circle') {
graphicsIns.drawCircle(drawData[0], drawData[1], drawData[2])
}
graphicsIns.endFill()
graphicsIns.alpha = alpha
return graphicsIns
}
function ScaleAni(t: number) {
return function(c: FYGE.Container) {
return new Promise(async (r) => {
FYGE.Tween.removeTweens(c)
c.scale.set(0, 0)
await new Promise(resolve => setTimeout(resolve, t))
FYGE.Tween.get(c, {
loop: true
})
.to({scaleX: 1, scaleY: 1}, 500, FYGE.Ease.quadInOut)
.to({scaleX: 0, scaleY: 0}, 500, FYGE.Ease.quadInOut)
.wait(1000)
})
}
}
export const DotAni: OriginalElementWidthEvents<{
delay: number,
radius: number
}> = function({
delay,
radius
}) {
const Cls: any = UseAni({
showCall: ScaleAni(delay)
})(function() {
const shapIns = new FYGE.Shape()
shapIns.beginFill('#43b99f')
shapIns.drawCircle(0, 0, radius)
shapIns.endFill()
return shapIns
})
return <Cls></Cls>
}
export class DreamDotAni extends Dream.RenderContainer {
render() {
return (
<FYGE.Container>
<FPShapeOfRectV2 type="rect" alpha={0.6} fillColor="#00000"></FPShapeOfRectV2>
{
Array.from({
length: 3
}, (_, index) => {
return (
<DotAni inlineProps={{
x: 270 + 100 * index,
y: 780
}} delay={index * 150} radius={40}></DotAni>
)
})
}
</FYGE.Container>
)
}
}
export const Container: OriginalElementWidthEvents = function() {
return (
<FYGE.Container>
</FYGE.Container>
)
}
export class DreamSpriteV2 extends Dream.RenderContainer<{
src: string | FYGE.Texture
}> {
notifyDisplayChange(w: number, h: number) {
let t = this.wrapperCont.parent
if (t.isInDisplay) {
t.sw = w
t.sh = h
}
while(t) {
t.dispatchEvent('updateDisplay')
t = t.parent
}
}
wrapperCont: FYGE.Container
render() {
const {
src
} = this.props
let _sp: FYGE.Sprite
if (typeof src === 'string') {
_sp = new FYGE.Sprite()
// 记得改一下 可能是加载过的
FYGE.GlobalLoader.loadImage((s, image) => {
if (s && image) {
const texture = FYGE.Texture.fromImage(image!)
_sp.texture = texture
this.notifyDisplayChange(texture.width, texture.height)
}
}, src)
} else {
_sp = new FYGE.Sprite(src)
}
this.wrapperCont = _sp
return _sp
}
}
\ No newline at end of file
import { RenderContainer } from "./renderContainer"
import { EventsMap } from "./types"
import { isFunction, nextTick } from "./utils"
type GetConstructType<T extends abstract new(...args: any)=>any> =
T extends abstract new (...args: infer P) => any
? P
: never
type ChildType = RenderContainer | FYGE.Container
type ChildrenTypeArray = Array<RenderContainer | FYGE.Container>
export function addChildFromParent(parent: FYGE.Container, children: ChildType[] | ChildType) {
if (Array.isArray(children)) {
children.forEach((child) => {
if (Array.isArray(child)) {
addChildFromParent(parent, child)
} else {
if (typeof child === 'number') {
// @ts-ignore
child = child + ''
}
if (typeof child === 'string') {
const t = new FYGE.TextField()
t.text = child
t.fillColor = '#000000'
// @ts-ignore
child = t
}
if (child instanceof RenderContainer) {
const node = child.render()
node! && parent.addChild(node)
} else {
// 可能createElement 会return null or undefined
child && parent.addChild(child)
}
}
})
} else {
if (children instanceof RenderContainer) {
const node = children.render()
node! && parent.addChild(node)
} else {
parent.addChild(children)
}
}
}
export function createElement<K extends new(p?: any) => ChildType, T extends ChildType>(ConstructType: K, props: Record<string, any> & {
inlineProps: Record<string, any>,
children: any[],
ref?: (c: ChildType) => void,
} & EventsMap, ...children: ChildrenTypeArray) {
if (typeof ConstructType === 'string') {
throw TypeError('Dream create cannot effect for string element type')
}
// TODO Object.freeze
props = props || Object.create(null)
// delete props.children
props.children = children
// Object.freeze(props)
// type tk = GetConstructType<K>
let eleins = new ConstructType(props)
if (
!(eleins instanceof RenderContainer || eleins instanceof FYGE.Container)
) {
throw TypeError(`class ${ConstructType} must extends RenderContainer or FYGE.Container...`)
}
const ref = props.ref
if (typeof ref === 'function') {
nextTick(function() {
ref(eleins)
})
// 别往下传播了
delete props.ref
}
if (eleins instanceof FYGE.Container) {
const inlineProps = props.inlineProps
if (inlineProps) {
// 简单点直接赋值
for (let k in inlineProps) {
// @ts-ignore 里面是 getter 和 setter 写的
(eleins[k] = inlineProps[k])
}
}
if (isFunction(props.onClick)) {
eleins.addEventListener(FYGE.MouseEvent.CLICK, props.onClick!)
}
if (isFunction(props.onClickCapture)) {
eleins.addEventListener(FYGE.MouseEvent.CLICK, props.onClickCapture!, eleins, true)
}
if (isFunction(props.onEnterFrame)) {
eleins.addEventListener(FYGE.Event.ENTER_FRAME, props.onEnterFrame!)
}
if (isFunction(props.onAddedToStage)) {
eleins.addEventListener(FYGE.Event.ADDED_TO_STAGE, props.onAddedToStage!)
}
// _noap 不会加到后面
!props._noap && addChildFromParent(eleins, children)
} else if (eleins instanceof RenderContainer) {
// 因为是 倒序的 nextTick 这里就先 didRendered 注册一下, 先让 render 里面的 ref 跑完
nextTick(function() {
(eleins as RenderContainer).didRendered()
}, eleins)
const node = eleins.render()
// Promise.resolve().then(() => {
// // @ts-ignore
// eleins.didRendered()
// })
// node && (eleins = node)
return node
}
return eleins
}
\ No newline at end of file
import { addChildFromParent, createElement } from "./development";
import { RenderContainer } from "./renderContainer";
export default {
createElement: createElement,
RenderContainer: RenderContainer,
VirtualRender: addChildFromParent
}
\ No newline at end of file
type PropsWithRef<P extends Record<string, any>> = P & { ref?: (e: any) => any };
export class RenderContainer<P extends Record<string, any> = any> {
readonly props: Readonly<P> & Readonly<{ children?: FYGE.Container[] }>;
constructor(props: Readonly<P> | P | Readonly<{ref?: (e: RenderContainer) => any} & P>) {
this.props = props
}
didRendered() {
}
render(): any {
}
}
\ No newline at end of file
type EventCallBack<T extends any = any> = (e: T) => any
export type EventsMap = {
onClick?: EventCallBack<FYGE.MouseEvent>
onClickCapture?: EventCallBack<FYGE.MouseEvent>
onEnterFrame?: EventCallBack<FYGE.Event>
onAddedToStage?: EventCallBack<FYGE.Event>
}
\ No newline at end of file
export function isFunction(p: any) {
return typeof p === 'function'
}
const inBrowser = !!document // 简单 的 判断 是否在浏览器中
// nextTick 简单原理
// 借助 MutationObserver 来进行处理 下一事件循环
// Mo 会在所有 同步代码执行之后 才会执行回调
const hasMutationObserverBug = false // 这里就简单这样处理吧
export const nextTick = (function () {
var callbacks: Function[] = []
var pending = false
var timerFunc: Function
function nextTickHandler () {
pending = false
// 之所以要slice复制一份出来是因为有的cb执行过程中又会往callbacks中加入内容
// 比如$nextTick的回调函数里又有$nextTick
// 这些是应该放入到下一个轮次的nextTick去执行的,
// 所以拷贝一份当前的,遍历执行完当前的即可,避免无休止的执行下去
var copies = callbacks.slice(0)
callbacks = []
// console.log(copies)
// for (var i = 0; i < copies.length; i++) {
// copies[i]()
// }
// 这里专门处理了一下
for (var i = copies.length - 1; i >= 0; i--) {
copies[i]()
}
}
if (typeof Promise !== 'undefined') {
timerFunc = function() {
Promise.resolve().then(() => {
nextTickHandler()
})
}
} else if (typeof MutationObserver !== 'undefined' && !hasMutationObserverBug) {
/* istanbul ignore if */
// ios9.3以上的WebView的MutationObserver有bug,
//所以在hasMutationObserverBug中存放了是否是这种情况
var counter = 1
// 创建一个MutationObserver,observer监听到dom改动之后后执行回调nextTickHandler
var observer = new MutationObserver(nextTickHandler)
var textNode = document.createTextNode(counter + '')
// 调用MutationObserver的接口,观测文本节点的字符内容
observer.observe(textNode, {
characterData: true
})
// 每次执行timerFunc都会让文本节点的内容在0/1之间切换,
// 不用true/false可能是有的浏览器对于文本节点设置内容为true/false有bug?
// 切换之后将新值赋值到那个我们MutationObserver观测的文本节点上去
timerFunc = function () {
counter = (counter + 1) % 2
textNode.data = counter + ''
}
} else {
// webpack attempts to inject a shim for setImmediate
// if it is used as a global, so we have to work around that to
// avoid bundling unnecessary code.
// webpack默认会在代码中插入setImmediate的垫片
// 没有MutationObserver就优先用setImmediate,不行再用setTimeout
const context = inBrowser
? window
: typeof global !== 'undefined' ? global : {
setImmediate
}
timerFunc = context.setImmediate || setTimeout
}
return function (cb: Function, ctx?: any) {
var func = ctx
? function () { cb.call(ctx) }
: cb
callbacks.push(func)
// 如果pending为true, 就其实表明本轮事件循环中已经执行过timerFunc(nextTickHandler, 0)
if (pending) return
pending = true
timerFunc(nextTickHandler, 0)
}
})()
\ No newline at end of file
export const ResJson = {
"groups": [],
"path": "./resource/"
}
\ No newline at end of file
console.log(123)
\ No newline at end of file
import { destroyLayers, layers } from "./modules/layers";
import { SceneController } from "./modules/layers/ctrls";
import { RES } from "./modules/RES";
import { ResJson } from "./ResJson";
import { HomeScene } from "./scenes/home";
import Tween = FYGE.Tween;
import EventDispatcher = FYGE.EventDispatcher;
import Stage = FYGE.Stage;
import RENDERER_TYPE = FYGE.RENDERER_TYPE
import Event = FYGE.Event
import getEnv = FYGE.getEnv;
export class Main {
//主舞台
stage: Stage;
private requestID: number;
private _pause: boolean;
private canvas: HTMLCanvasElement;
constructor(
canvas: HTMLCanvasElement,
desWidth = 750,
desHeight = 1624,
divWidth?: number,
divHeight?: number,
renderType = RENDERER_TYPE.WEBGL,
stageCenter = true,
fixedHeight = false,
resolution?: any,
) {
let sysInfo;
// 淘宝小程序环境就用canvas初始化
if (!window) { // 自行处理吧,这么判断也不保险,万一淘宝小程序加进了window
FYGE.initedByCanvas(canvas); // 里面会设置env为tb,这个很重要
// @ts-ignore 存在my就初始化
sysInfo = my.getSystemInfoSync();
}
divWidth = divWidth || sysInfo?.windowWidth || document.body.clientWidth;
divHeight = divHeight || sysInfo?.windowHeight || document.body.clientHeight;
resolution = resolution || sysInfo?.pixelRatio || window.devicePixelRatio || 1;
// 建舞台
const stage = this.stage = new Stage(
canvas,
desWidth, // 设计宽度,按设计搞给的就行
desHeight, // 设计高度
divWidth, // 显示宽度,全屏就是屏幕宽度
divHeight, // 显示高度,全屏就是屏幕高度
renderType, // 渲染模式canvas
stageCenter, // 视窗居中裁切
fixedHeight, // 不定高,定宽适配
resolution, // 分辨率
);
this.canvas = canvas; // 赋值下,为了下面的destroy的cancelAnimationFrame
// stage初始化
stage.addEventListener(Event.INIT_STAGE, this.onAddToStage, this);
//循环
this.loop();
if (document) {
this.initWebEvent()
}
}
private loop = () => {
if (!this._pause) {
Tween.flush();
this.stage.flush();
}
// @ts-ignore
getEnv() == "tb" ? this.requestID = this.canvas.requestAnimationFrame(this.loop) :
this.requestID = window.requestAnimationFrame(this.loop);
}
onAddToStage() {
layers.init(this.stage)
SceneController.init(layers.sceneLayer)
RES.loadConfig(ResJson)
SceneController.changeScene(HomeScene)
}
initWebEvent(){
const mouseEvent = this.stage.onMouseEvent.bind(this.stage);
this.canvas.addEventListener("touchstart", mouseEvent, false);
this.canvas.addEventListener('touchmove', mouseEvent, false);
this.canvas.addEventListener('touchend', mouseEvent, false);
}
run() {
this._pause = false;
// @ts-ignore Tween计时清零
Tween._lastTime = null;
}
/**
* 在小程序隐藏时调用onHide
*/
pause() {
// this._pause = true;//先不暂停了
}
//在小程序页面卸载时调用onUnload,多次销毁后会有问题,再检查
destroy() {
// Tween都移除,注意吧,可能原先的也被移除,对于多page时注意,会把其他页面的也去掉
Tween.removeAllTweens();
// 停掉计时器
// @ts-ignore 为了兼容多page的canvas
FYGE.getEnv() == "tb" ? this.canvas.cancelAnimationFrame(this.requestID) :
window.cancelAnimationFrame(this.requestID);
// 层级销毁
destroyLayers();
// 销毁控制器
// destroyAllCtrls();
// 舞台销毁
this.stage.destroy();
// 全局事件置空
// GDispatcher.removeAllEventListener();
// 淘宝环境网络数据记录清
// destroyTbNetData();
// // 网络数据记录清空
// destroyWebNetData();
}
}
\ No newline at end of file
This diff is collapsed.
import Dream from "../../Dream"
import { Container } from "../../Dream/UI"
type UseAniConfig = {
showCall?: (c: FYGE.Container, ...args: any[]) => Promise<any>
hideCall?: (c: FYGE.Container) => Promise<any>
}
export function UseAni(cfg: UseAniConfig) {
return function(Node: any) {
return class extends Dream.RenderContainer<{
getAniIns: (ins: any) => any
}> {
aniCont: FYGE.Container
didRendered(): void {
this.showAni()
}
async showAni() {
this.aniCont.visible = true
if (cfg.showCall) {
await cfg.showCall(this.aniCont)
}
}
async hideAni() {
if (cfg.hideCall) {
await cfg.hideCall(this.aniCont)
}
this.aniCont.visible = false
}
render() {
const {
getAniIns,
...othersProps
} = this.props
return (
<Container ref={el => {
this.aniCont = el
}}>
<Node {...othersProps}></Node>
</Container>
)
}
}
}
}
\ No newline at end of file
export type ResolveCallback<T> = (value: T | PromiseLike<T>) => void
export type RejectCallback = (reason?: any) => void
export type CancelCallback = () => void
export type ImperativePromise<T> = {
promise: Promise<T>
resolve: ResolveCallback<T>
reject: RejectCallback
cancel: CancelCallback
}
/**
* 创建指令shi Promise
* @param promiseArg
* @returns
*/
export function createImperativePromise<T>(promiseArg?: Promise<T> | null | undefined): ImperativePromise<T> {
let resolve: ResolveCallback<T> | null = null
let reject: RejectCallback | null = null
const wrappedPromise = new Promise<T>((_resolve, _reject) => {
resolve = _resolve
reject = _reject
})
promiseArg && promiseArg.then(
val => {
resolve && resolve(val)
},
error => {
reject && reject(error)
}
)
return {
promise: wrappedPromise,
resolve: (value: T | PromiseLike<T>) => {
resolve && resolve(value)
},
reject: (reason?: any) => {
reject && reject(reason)
},
cancel: () => {
resolve = null
reject = null
}
}
}
\ No newline at end of file
import Dream from "../../Dream";
import { RenderContainer } from "../../Dream/renderContainer"
type SignleContainer = FYGE.Container
type TagType = SignleContainer | RenderContainer | (FC)
type FC<P = {}> = FunctionComponent<P>;
type PropsWithChildren<P> = P & { children?: any[] };
interface FunctionComponent<P = {}> {
(props: PropsWithChildren<P>): SignleContainer;
}
// class SceneCompent extends Dream.RenderContainer {
// }
function SceneWrapper(SceneNode: any) {
return class extends Dream.RenderContainer{
sceneContainer: FYGE.Container
wrapperContainer: FYGE.Container
show() {
// 做动画
this.wrapperContainer.visible = true
}
hide() {
this.wrapperContainer.visible = false
}
render() {
return (
<FYGE.Container ref={(el: any) => this.wrapperContainer = el}>
<FYGE.Container ref={(el: FYGE.Container) => this.sceneContainer = el}>
<SceneNode {...this.props}></SceneNode>
</FYGE.Container>
</FYGE.Container>
)
}
}
}
export const SceneController = (function() {
let controller: FYGE.Container
const SceneMap = new Map()
return {
init(cont: FYGE.Container) {
if (!controller) {
controller = cont
}
},
changeScene(SceneNode: any, props?: Record<string, any>) {
if (!controller) {
throw new Error('没有场景controller')
}
const _ = SceneMap.get(SceneNode)
if (_) {
// props ?
_.show()
return
}
const S = SceneWrapper(SceneNode)
const cont = new FYGE.Container()
controller.addChild(cont)
Dream.VirtualRender(cont, <S
{...props}
ref={(el: { show: () => void; }) => {
SceneMap.set(SceneNode, el)
el.show()
}}
></S>)
}
}
})()
\ No newline at end of file
/**
* 添加进舞台的所有层级
* 仿白鹭的那套
*/
class Layers extends FYGE.Container {
[x: string]: any
private _bottomLayer: FYGE.Container;
private _sceneLayer: FYGE.Container;
private _commonUILayer: FYGE.Container;
private _popupLayer: FYGE.Container;
private _toastLayer: FYGE.Container;
private _topLayer: FYGE.Container;
private _shareLayer: FYGE.Container;
init(stage: FYGE.Stage) {
stage.addChild(this);
const arr = [
"_bottomLayer",
"_sceneLayer",
"_commonUILayer",
"_popupLayer",
"_toastLayer",
"_topLayer",
"_shareLayer"
];
for (let i = 0; i < arr.length; i++) {
this[arr[i]] = new FYGE.Container();
//有些时候,定宽的时候,部分layer置顶,部分居中,再处理
//为了都置顶和置左,stage的方式永远居中视窗,要么改stage永远左上为00
// this[arr[i]].y = this.stageOffsetY;
//如果定宽这里没必要,肯定是0
// this[arr[i]].x = this.stageOffsetX;//去掉,定高时就居中了
this.addChild(this[arr[i]]);
}
//都以顶部适配
// this.sceneLayer.y = this.stageOffsetY;
// this.popupLayer.y = this.stageOffsetY;
//都以底部适配
// this.sceneLayer.y = -this.stageOffsetY;
// this.popupLayer.y = -this.stageOffsetY;
//这个因为psd弹框不规范
// this.popupLayer.y -= 420 / 2;
this.shareLayer.y = -this.stageOffsetY;
//初始化场景层级
// SceneCtrl.instance.init(this.sceneLayer);
// //初始化场景层级
// CommonUICtrl.instance.init(this.commonUILayer);
// //初始化弹框层级
// PanelCtrl.instance.init(this.popupLayer);
}
/**
* 底图所在层级,比如统一的背景
*/
get bottomLayer() { return this._bottomLayer }
/**
* 场景
*/
get sceneLayer() { return this._sceneLayer }
/**
* UI
*/
get commonUILayer() { return this._commonUILayer }
/**
* 弹框
*/
get popupLayer() { return this._popupLayer }
/**
* toast所在层级
*/
get toastLayer() { return this._toastLayer }
/**
* 顶层,比如统一标题栏等
*/
get topLayer() { return this._topLayer }
/**
* 分享引导层
*/
get shareLayer() { return this._shareLayer }
/**
* 舞台信息都放在layers里吧
* 舞台可见高度,初始化后才能使用
*/
get stageHeight() {
if (!this.stage) return 0;
return this.stage.viewRect.height;
}
/**
* 舞台可见宽度
*/
get stageWidth() {
if (!this.stage) return 0;
return this.stage.viewRect.width;
}
/**
* 适配方式x两边偏移的量,固定宽度x为0
*/
get stageOffsetX() {
if (!this.stage) return 0;
return this.stage.viewRect.x;
}
get stageOffsetY() {
if (!this.stage) return 0;
return this.stage.viewRect.y;
}
/**
* 舞台中心点位置x
*/
// get stageCenterX(): number {
// return this.stage.viewRect.x + this.stage.viewRect.width >> 1;
// }
/**
* 舞台中心点位置y,layer位置做过偏移的就不对了,所以还是自行算吧
*/
// get stageCenterY(): number {
// return this.stage.viewRect.y + this.stage.viewRect.height >> 1;
// }
}
export const layers = new Layers();
//先执行,在淘宝小程序中重新进入会再次初始化
export function destroyLayers() {
//所有层级移除,init会重新建
layers.removeChildren();
//从父级stage移除自己,init会重新加
if (layers.parent) layers.parent.removeChild(layers)
}
import Dream from "../Dream";
import { DotAni, DreamDotAni, DreamSpriteV2 } from "../Dream/UI";
import { UseAni } from "../modules/UseDecorator/useAni";
function fadeIn(cont: FYGE.Container) {
return new Promise(r => {
FYGE.Tween.removeTweens(cont)
FYGE.Tween.get(cont)
.set({alpha: 0})
.to({
alpha: 1
}, 500, FYGE.Ease.quadIn)
.call(r)
})
}
function fadeOut(cont: FYGE.Container) {
return new Promise(r => {
FYGE.Tween.removeTweens(cont)
FYGE.Tween.get(cont)
.to({
alpha: 0
}, 500, FYGE.Ease.quadIn)
.call(r)
})
}
@UseAni({
showCall: fadeIn,
hideCall: fadeOut
})
class Test extends Dream.RenderContainer{
render() {
console.log(this.props)
return (
<FYGE.Container>
<DreamSpriteV2 src="http://qnpic.top/yoona2.jpg"></DreamSpriteV2>
</FYGE.Container>
)
}
}
export class HomeScene extends Dream.RenderContainer {
cont: FYGE.Container
addFadeinCont() {
this.cont.addChild(<Test tt={3} ref={el => {
setTimeout(() => {
el.hideAni()
}, 3000)
}}></Test>)
}
render() {
return (
// <DreamSpriteV2 src="http://qnpic.top/yoona2.jpg"></DreamSpriteV2>
<FYGE.Container ref={(el: any) => {
this.cont = el
}}>
<FYGE.TextField onClick={() => {
this.addFadeinCont()
}} inlineProps={{
text: '爱的爱我',
size: 90,
fillColor: '#000000'
}}></FYGE.TextField>
<DreamDotAni></DreamDotAni>
</FYGE.Container>
)
}
}
\ No newline at end of file
......@@ -14,7 +14,7 @@
"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. */
"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'. */
......@@ -76,7 +76,7 @@
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
"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. */
......
......@@ -203,7 +203,7 @@
"@babel/helper-wrap-function" "^7.18.9"
"@babel/types" "^7.18.9"
"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.18.9":
"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.18.9", "@babel/helper-replace-supers@^7.19.1":
version "7.19.1"
resolved "http://npm.dui88.com:80/@babel%2fhelper-replace-supers/-/helper-replace-supers-7.19.1.tgz#e1592a9b4b368aa6bdb8784a711e0bcbf0612b78"
integrity sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw==
......@@ -326,6 +326,17 @@
"@babel/helper-plugin-utils" "^7.18.6"
"@babel/plugin-syntax-class-static-block" "^7.14.5"
"@babel/plugin-proposal-decorators@^7.19.3":
version "7.19.3"
resolved "http://npm.dui88.com:80/@babel%2fplugin-proposal-decorators/-/plugin-proposal-decorators-7.19.3.tgz#c1977e4902a18cdf9051bf7bf08d97db2fd8b110"
integrity sha512-MbgXtNXqo7RTKYIXVchVJGPvaVufQH3pxvQyfbGvNw1DObIhph+PesYXJTcd8J4DdWibvf6Z2eanOyItX8WnJg==
dependencies:
"@babel/helper-create-class-features-plugin" "^7.19.0"
"@babel/helper-plugin-utils" "^7.19.0"
"@babel/helper-replace-supers" "^7.19.1"
"@babel/helper-split-export-declaration" "^7.18.6"
"@babel/plugin-syntax-decorators" "^7.19.0"
"@babel/plugin-proposal-dynamic-import@^7.18.6":
version "7.18.6"
resolved "http://npm.dui88.com:80/@babel%2fplugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz#72bcf8d408799f547d759298c3c27c7e7faa4d94"
......@@ -449,6 +460,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.14.5"
"@babel/plugin-syntax-decorators@^7.19.0":
version "7.19.0"
resolved "http://npm.dui88.com:80/@babel%2fplugin-syntax-decorators/-/plugin-syntax-decorators-7.19.0.tgz#5f13d1d8fce96951bea01a10424463c9a5b3a599"
integrity sha512-xaBZUEDntt4faL1yN8oIFlhfXeQAWJW7CLKYsHTUqriCUbj8xOra8bfxxKGi/UwExPFBuPdH4XfHc9rGQhrVkQ==
dependencies:
"@babel/helper-plugin-utils" "^7.19.0"
"@babel/plugin-syntax-dynamic-import@^7.8.3":
version "7.8.3"
resolved "http://npm.dui88.com:80/@babel%2fplugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment