...
@ -61,7 +61,11 @@ export class BezierCurveAnimation extends Component {
|
||||
tweenSwitchEvent = new cc.EventHandler();
|
||||
|
||||
/** 更新事件 */
|
||||
@property({ displayName: '更新事件', tooltip: '(曲线Y_yN)', type: cc.EventHandler })
|
||||
@property({
|
||||
displayName: '更新事件',
|
||||
tooltip: '(总曲线Y_yN, 当前缓动下标_indexN, 当前缓动曲线Y_yN)',
|
||||
type: cc.EventHandler
|
||||
})
|
||||
updateEvent = new cc.EventHandler();
|
||||
|
||||
/** 结束事件 */
|
||||
@ -69,25 +73,36 @@ export class BezierCurveAnimation extends Component {
|
||||
endEvent = new cc.EventHandler();
|
||||
/* --------------- private --------------- */
|
||||
/* ------------------------------- segmentation ------------------------------- */
|
||||
/** 开始缓动 */
|
||||
startTween(): cc.Tween<any> {
|
||||
/**
|
||||
* 开始缓动
|
||||
* @param startIndexN_ 缓动开始下标
|
||||
* @param endIndexN_ 缓动结束下标
|
||||
* @returns
|
||||
*/
|
||||
startTween(startIndexN_?: number, endIndexN_ = (startIndexN_ ?? 0) + 1): cc.Tween<any> {
|
||||
let tweenUnitAs = this.tweenUnitAs;
|
||||
if (startIndexN_ !== undefined) {
|
||||
tweenUnitAs = tweenUnitAs.slice(startIndexN_, endIndexN_);
|
||||
}
|
||||
/** 总时间(秒) */
|
||||
let totalTimeSN = this.tweenUnitAs.reduce((preValue, currValue) => preValue + currValue.timeSN, 0);
|
||||
let totalTimeSN = tweenUnitAs.reduce((preValue, currValue) => preValue + currValue.timeSN, 0);
|
||||
/** 时间占比 */
|
||||
let timeRatioNs: number[] = [];
|
||||
{
|
||||
let currN = 0;
|
||||
this.tweenUnitAs.forEach((v, kN) => {
|
||||
tweenUnitAs.forEach((v, kN) => {
|
||||
let ratioN = v.timeSN / totalTimeSN;
|
||||
currN += ratioN;
|
||||
timeRatioNs.push(currN);
|
||||
});
|
||||
}
|
||||
/** 曲线函数 */
|
||||
let curveFS = this.tweenUnitAs.map((v) => {
|
||||
let curveFS = tweenUnitAs.map((v) => {
|
||||
if (v.customCurveB) {
|
||||
let curve = new BezierCurve(v.controlPointV3S);
|
||||
return curve.point.bind(curve) as (kN: number) => number;
|
||||
return (kN: number) => {
|
||||
return curve.point(kN).y;
|
||||
};
|
||||
} else {
|
||||
return cc.easing[easingEnum[v.easing]].bind(cc.easing) as (kN: number) => number;
|
||||
}
|
||||
@ -118,13 +133,17 @@ export class BezierCurveAnimation extends Component {
|
||||
/** 曲线位置 */
|
||||
let posN = (ratioN - lastTimeRatioN) / timeRangeN;
|
||||
/** 曲线位置 */
|
||||
let yN = curveFS[tweenIndexN](posN) * timeRangeN + lastTimeRatioN;
|
||||
let yN = curveFS[tweenIndexN](posN);
|
||||
let y2N = yN * timeRangeN + lastTimeRatioN;
|
||||
// 缓动切换事件触发
|
||||
if (lastTweenIndexN !== tweenIndexN) {
|
||||
this.tweenSwitchEvent?.emit([lastTweenIndexN]);
|
||||
}
|
||||
if (y2N === undefined) {
|
||||
debugger;
|
||||
}
|
||||
// 更新事件触发
|
||||
this.updateEvent?.emit([yN]);
|
||||
this.updateEvent?.emit([y2N, tweenIndexN, yN]);
|
||||
// 更新缓动下标
|
||||
lastTweenIndexN = tweenIndexN;
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { _decorator, Component, Node } from 'cc';
|
||||
import * as cc from 'cc';
|
||||
import { BezierCurveAnimation } from './BezierCurveAnimation';
|
||||
const { ccclass, property, requireComponent } = _decorator;
|
||||
import { EDITOR } from 'cc/env';
|
||||
const { ccclass, property, requireComponent, executeInEditMode } = _decorator;
|
||||
|
||||
/** 旋转抽奖方向 */
|
||||
export enum RollingLotteryDirection {
|
||||
@ -30,19 +31,27 @@ export class RollingLottery extends Component {
|
||||
/** transform 组件 */
|
||||
private _uiTransform: cc.UITransform;
|
||||
/** 周长 */
|
||||
private _perimeterN: number;
|
||||
/** 当前距离 */
|
||||
private _currDistN = 0;
|
||||
private _perimeterV3 = cc.v3();
|
||||
/** 总距离 */
|
||||
private _totalDistN: number;
|
||||
private _totalDistV3: cc.Vec3;
|
||||
/** 当前下标 */
|
||||
private _currIndexN: number;
|
||||
private _currIndexN = 0;
|
||||
/** 子节点大小 */
|
||||
private _ItemSize: cc.Size;
|
||||
/** 运动状态 */
|
||||
/** 滚动状态 */
|
||||
private _scrollB = false;
|
||||
/** 循环状态 */
|
||||
private _loopScrollB = false;
|
||||
/** 循环缓动 */
|
||||
private _loopTween: cc.Tween<any>;
|
||||
/** 自己矩形 */
|
||||
private _selfRect = cc.rect();
|
||||
/** 上次曲线 Y */
|
||||
private _lastCurveYN = 0;
|
||||
/** 当前目标 */
|
||||
private _currTargetN: number;
|
||||
/** 当前缓动下标 */
|
||||
private _currTweenN = 0;
|
||||
/* --------------- 临时变量 --------------- */
|
||||
private _tempM4 = cc.mat4();
|
||||
private _temp2M4 = cc.mat4();
|
||||
@ -53,92 +62,175 @@ export class RollingLottery extends Component {
|
||||
this._initEvent();
|
||||
}
|
||||
/* ------------------------------- 功能 ------------------------------- */
|
||||
/**
|
||||
* 获取格子移动后距离
|
||||
* @param currIndexN_ 当前格子下标
|
||||
* @param targetIndexN_ 目标格子下标
|
||||
* @returns
|
||||
*/
|
||||
private _getItemMovePos(currIndexN_: number, targetIndexN_: number): void {
|
||||
/** 当前节点 */
|
||||
let currNode = this.node.getChildByName(currIndexN_ + '');
|
||||
/** 节点矩形 */
|
||||
let currRect = this._getBoundingBoxToWorld(currNode);
|
||||
/** 移动距离 */
|
||||
let moveDistV3 = cc.v3(this._ItemSize.x, this._ItemSize.y).multiplyScalar(targetIndexN_ - currIndexN_);
|
||||
/** 移动距离 */
|
||||
let moveDistN: number;
|
||||
if (this.dire === RollingLotteryDirection.HORIZONTAL) {
|
||||
moveDistV3.y = 0;
|
||||
moveDistN = moveDistV3.x;
|
||||
} else {
|
||||
moveDistV3.x = 0;
|
||||
moveDistN = moveDistV3.y;
|
||||
}
|
||||
currRect.x += moveDistV3.x;
|
||||
currRect.y += moveDistV3.y;
|
||||
|
||||
/** 终点坐标 */
|
||||
let endPosV3 = this.node
|
||||
.getChildByName(currIndexN_ + '')
|
||||
.worldPosition.clone()
|
||||
.add(moveDistV3);
|
||||
/** 圈数 */
|
||||
let circleN = Math.floor(moveDistN / this._perimeterN);
|
||||
// /** 额外移动距离 */
|
||||
// let extraMoveDistN = moveDistN - circleN * this._perimeterN;
|
||||
}
|
||||
|
||||
/** 获取在世界坐标系下的节点包围盒(不包含自身激活的子节点范围) */
|
||||
private _getBoundingBoxToWorld(node_: cc.Node): cc.Rect {
|
||||
node_.getWorldMatrix(this._temp2M4);
|
||||
cc.Mat4.fromRTS(this._tempM4, this.node.getRotation(), this.node.getPosition(), this.node.getScale());
|
||||
let width = this._uiTransform.contentSize.width;
|
||||
let height = this._uiTransform.contentSize.height;
|
||||
let rect = new cc.Rect(
|
||||
-this._uiTransform.anchorPoint.x * width,
|
||||
-this._uiTransform.anchorPoint.y * height,
|
||||
width,
|
||||
height
|
||||
);
|
||||
let uiTransform = node_.getComponent(cc.UITransform);
|
||||
cc.Mat4.fromRTS(this._tempM4, node_.getRotation(), node_.getPosition(), node_.getScale());
|
||||
const width = uiTransform.contentSize.width;
|
||||
const height = uiTransform.contentSize.height;
|
||||
const rect = cc.rect(-uiTransform.anchorPoint.x * width, -uiTransform.anchorPoint.y * height, width, height);
|
||||
|
||||
node_.parent.getWorldMatrix(this._temp2M4);
|
||||
cc.Mat4.multiply(this._temp2M4, this._temp2M4, this._tempM4);
|
||||
rect.transformMat4(this._temp2M4);
|
||||
return rect;
|
||||
}
|
||||
|
||||
/** 检测参数节点是否与当前节点碰撞 */
|
||||
private _checkCollision(node_: cc.Node): boolean {
|
||||
let rect = this._getBoundingBoxToWorld(this.node);
|
||||
let rect2 = this._getBoundingBoxToWorld(node_);
|
||||
// 增加保险范围
|
||||
rect.width += rect.width * 0.5;
|
||||
rect.height += rect.height * 0.5;
|
||||
rect.x -= rect.width * 0.25;
|
||||
rect.y -= rect.height * 0.25;
|
||||
return rect.intersects(rect2);
|
||||
/**
|
||||
* 滚动子节点
|
||||
* @param distV3_ 距离
|
||||
*/
|
||||
private _scrollChild(distV3_: cc.Vec3): void {
|
||||
/** 当前节点矩形 */
|
||||
let currNodeRect: cc.Rect;
|
||||
/** 更新节点坐标 */
|
||||
let updatePosB = false;
|
||||
/** 当前节点 UITransform */
|
||||
let currTransform: cc.UITransform;
|
||||
/** 头节点 */
|
||||
let headNode = this.node.children[0];
|
||||
/** 尾节点 */
|
||||
let tailNode = this.node.children[this.node.children.length - 1];
|
||||
/** 头节点矩形 */
|
||||
let headNodeRect: cc.Rect;
|
||||
/** 尾节点矩形 */
|
||||
let tailNodeRect: cc.Rect;
|
||||
|
||||
// 左右滚动
|
||||
if (this.dire === RollingLotteryDirection.HORIZONTAL) {
|
||||
}
|
||||
// 上下滚动
|
||||
else {
|
||||
// 从上往下滚动
|
||||
if (distV3_.y < 0) {
|
||||
this.node.children.forEach((v, kN) => {
|
||||
currTransform = v.getComponent(cc.UITransform);
|
||||
updatePosB = false;
|
||||
currNodeRect = this._getBoundingBoxToWorld(v);
|
||||
|
||||
// 移动坐标
|
||||
currNodeRect.y += distV3_.y;
|
||||
|
||||
// 相交则更新节点坐标
|
||||
if (currNodeRect.intersects(this._selfRect)) {
|
||||
updatePosB = true;
|
||||
}
|
||||
// 若不相交则超出范围
|
||||
else {
|
||||
// 若节点在上方则跳过更新
|
||||
if (currNodeRect.yMin > this._selfRect.yMax) {
|
||||
updatePosB = true;
|
||||
} else {
|
||||
// setTimeout 防止获取节点坐标错误、防止 setSiblingIndex 后遍历错误
|
||||
setTimeout(() => {
|
||||
// 切换位置到头节点上方
|
||||
headNodeRect = this._getBoundingBoxToWorld(headNode);
|
||||
v.worldPosition = cc.v3(
|
||||
v.worldPosition.x,
|
||||
headNodeRect.yMax + currNodeRect.height * currTransform.anchorY
|
||||
);
|
||||
|
||||
// 更新 item
|
||||
let indexN = Number(headNode.name) - 1;
|
||||
v.name = indexN + '';
|
||||
this.itemUpdateEvent.emit([v, indexN]);
|
||||
|
||||
// 更新 item 下标
|
||||
headNode = v;
|
||||
v.setSiblingIndex(0);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新节点坐标
|
||||
if (updatePosB) {
|
||||
v.worldPosition = cc.v3(
|
||||
v.worldPosition.x,
|
||||
currNodeRect.y + currNodeRect.height * currTransform.anchorY
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 从下往上滚动
|
||||
else {
|
||||
this.node.children.forEach((v, kN) => {
|
||||
currTransform = v.getComponent(cc.UITransform);
|
||||
updatePosB = false;
|
||||
currNodeRect = this._getBoundingBoxToWorld(v);
|
||||
|
||||
// 移动坐标
|
||||
currNodeRect.y += distV3_.y;
|
||||
|
||||
// 相交则更新节点坐标
|
||||
if (currNodeRect.intersects(this._selfRect)) {
|
||||
updatePosB = true;
|
||||
}
|
||||
// 若不相交则超出范围
|
||||
else {
|
||||
// 若节点在下方则跳过更新
|
||||
if (this._selfRect.yMin > currNodeRect.yMax) {
|
||||
updatePosB = true;
|
||||
} else {
|
||||
// setTimeout 防止获取节点坐标错误、防止 setSiblingIndex 后遍历错误
|
||||
setTimeout(() => {
|
||||
// 切换位置到尾节点下方
|
||||
tailNodeRect = this._getBoundingBoxToWorld(tailNode);
|
||||
v.worldPosition = cc.v3(
|
||||
v.worldPosition.x,
|
||||
tailNodeRect.yMin - currNodeRect.height * (1 - currTransform.anchorY)
|
||||
);
|
||||
|
||||
// 更新 item
|
||||
let indexN = Number(tailNode.name) + 1;
|
||||
v.name = indexN + '';
|
||||
this.itemUpdateEvent.emit([v, indexN]);
|
||||
|
||||
// 更新 item 下标
|
||||
tailNode = v;
|
||||
v.setSiblingIndex(this.node.children.length - 1);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新节点坐标
|
||||
if (updatePosB) {
|
||||
v.worldPosition = cc.v3(
|
||||
v.worldPosition.x,
|
||||
currNodeRect.y + currNodeRect.height * currTransform.anchorY
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 更新运动距离 */
|
||||
private _updateMoveDist(indexN_: number): void {
|
||||
/** 当前节点 */
|
||||
let currNode = this.node.getChildByName(this._currIndexN + '');
|
||||
/** 间隔格子 */
|
||||
let intervalN = indexN_ - this._currIndexN;
|
||||
/** 格子距离 */
|
||||
let boxDistN = this.dire === RollingLotteryDirection.HORIZONTAL ? this._ItemSize.width : this._ItemSize.height;
|
||||
/** 超出当前格子距离 */
|
||||
let overDistN = this._currDistN - boxDistN * this._currIndexN;
|
||||
/** 当前格子距父节点(0, 0)的偏移坐标 */
|
||||
let offsetDistV3 = this.node.worldPosition.clone().subtract(currNode.worldPosition);
|
||||
// 设置总距离
|
||||
this._totalDistN = intervalN * boxDistN - overDistN;
|
||||
if (this.dire === RollingLotteryDirection.HORIZONTAL) {
|
||||
this._totalDistV3 = cc.v3(intervalN * boxDistN + offsetDistV3.x);
|
||||
} else {
|
||||
this._totalDistV3 = cc.v3(0, intervalN * boxDistN + offsetDistV3.y);
|
||||
}
|
||||
}
|
||||
|
||||
/** 更新数据 */
|
||||
private _updateData(): void {
|
||||
// 单圈长度
|
||||
this._perimeterN = 0;
|
||||
this._perimeterV3.set(cc.Vec3.ZERO);
|
||||
let size: cc.Size;
|
||||
this.node.children.forEach((v1) => {
|
||||
this._perimeterN += v1.getComponent(cc.UITransform).height;
|
||||
size = v1.getComponent(cc.UITransform).contentSize;
|
||||
this._perimeterV3.add3f(size.x, size.y, 0);
|
||||
});
|
||||
// 重置距离
|
||||
this._currDistN = 0;
|
||||
}
|
||||
|
||||
/** 初始化数据 */
|
||||
@ -147,29 +239,31 @@ export class RollingLottery extends Component {
|
||||
this._uiTransform = this.node.getComponent(cc.UITransform);
|
||||
this._ItemSize = this.node.children[0].getComponent(cc.UITransform).contentSize.clone();
|
||||
|
||||
// 自己矩形
|
||||
this._selfRect = this._getBoundingBoxToWorld(this.node);
|
||||
|
||||
// 设置更新事件
|
||||
this._curveComp.updateEvent.component = cc.js.getClassName(this);
|
||||
this._curveComp.updateEvent.handler = 'updateEvent';
|
||||
this._curveComp.updateEvent.target = this.node;
|
||||
|
||||
// 设置结束事件
|
||||
this._curveComp.endEvent.component = cc.js.getClassName(this);
|
||||
this._curveComp.endEvent.handler = 'updateEvent';
|
||||
|
||||
// 更新当前距离
|
||||
{
|
||||
let distV3 = this.node.worldPosition.clone().subtract(this.node.children[0].worldPosition);
|
||||
this._currDistN = this.dire === RollingLotteryDirection.HORIZONTAL ? distV3.x : distV3.y;
|
||||
}
|
||||
|
||||
// 自己矩形
|
||||
this._selfRect = this._getBoundingBoxToWorld(this.node);
|
||||
this._curveComp.endEvent.handler = 'endEvent';
|
||||
this._curveComp.endEvent.target = this.node;
|
||||
|
||||
this._updateData();
|
||||
}
|
||||
|
||||
/** 初始化视图 */
|
||||
private _initView(): void {
|
||||
this.scroll(0);
|
||||
// 重置子节点
|
||||
this.node.children.forEach((v, kN) => {
|
||||
v.name = kN + this._currIndexN + '';
|
||||
this.itemUpdateEvent.emit([v, kN + this._currIndexN]);
|
||||
});
|
||||
|
||||
this.jump(this._currIndexN);
|
||||
}
|
||||
|
||||
/** 初始化事件 */
|
||||
@ -179,41 +273,105 @@ export class RollingLottery extends Component {
|
||||
|
||||
/**
|
||||
* 循环滚动
|
||||
* @param speedN_ 速度
|
||||
* @param speedN_ 速度/秒
|
||||
* @param timeSN_ 时间(秒),不填则一直滚动
|
||||
*/
|
||||
loop(speedN_: number, timeSN_?: number): void {}
|
||||
|
||||
/** 滚动到指定下标 */
|
||||
scroll(indexN_: number, timeSN_?: number): void {
|
||||
loop(speedN_: number, timeSN_?: number): void {
|
||||
if (this._scrollB) {
|
||||
return;
|
||||
}
|
||||
this._scrollB = true;
|
||||
this._updateMoveDist(indexN_);
|
||||
|
||||
/** 移动距离 */
|
||||
let moveDistN = this._totalDistN - this._currDistN;
|
||||
|
||||
// 直接跳转
|
||||
if (!timeSN_) {
|
||||
/** 圈数 */
|
||||
let circleN = Math.floor(moveDistN / this._perimeterN);
|
||||
/** 额外移动距离 */
|
||||
let extraMoveDistN = moveDistN - circleN * this._perimeterN;
|
||||
this._loopScrollB = true;
|
||||
let distV3 = cc.v3();
|
||||
this._loopTween = cc
|
||||
.tween({ valueN: 0, lastValueN: 0 })
|
||||
.repeatForever(
|
||||
cc.tween().by(
|
||||
1,
|
||||
{
|
||||
valueN: 1
|
||||
},
|
||||
{
|
||||
onUpdate: (target?: any, ratioN?: number) => {
|
||||
if (this.dire === RollingLotteryDirection.HORIZONTAL) {
|
||||
distV3.x = (target.valueN - target.lastValueN) * speedN_;
|
||||
} else {
|
||||
distV3.y = (target.valueN - target.lastValueN) * speedN_;
|
||||
}
|
||||
this._scrollChild(distV3);
|
||||
target.lastValueN = target.valueN;
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
.start();
|
||||
if (timeSN_ !== undefined) {
|
||||
this.scheduleOnce(() => {
|
||||
this._loopTween.stop();
|
||||
this._loopTween = null;
|
||||
this._loopScrollB = false;
|
||||
}, timeSN_);
|
||||
}
|
||||
}
|
||||
/* ------------------------------- 自定义事件 ------------------------------- */
|
||||
tweenSwitchEvent(indexN_: number): void {
|
||||
// cc.log('缓动切换', indexN_);
|
||||
}
|
||||
updateEvent(yN_: number): void {
|
||||
// cc.log('缓动更新', yN_);
|
||||
}
|
||||
endEvent(): void {
|
||||
// cc.log('缓动结束');
|
||||
|
||||
/** 跳转到指定下标 */
|
||||
jump(indexN_: number): void {
|
||||
if (this._scrollB) {
|
||||
return;
|
||||
}
|
||||
this._scrollB = true;
|
||||
|
||||
// 更新移动距离
|
||||
this._updateMoveDist(indexN_);
|
||||
// 开始滚动
|
||||
this._scrollChild(this._totalDistV3);
|
||||
// 更新状态
|
||||
this._currIndexN = indexN_;
|
||||
this._scrollB = false;
|
||||
}
|
||||
|
||||
/** 子节点更新 */
|
||||
childUpdate(node_: cc.Node, indexN_: number): void {
|
||||
node_.getComponentInChildren(cc.Label).string = indexN_ + '';
|
||||
/** 滚动到指定下标 */
|
||||
scroll(indexN_: number): void {
|
||||
if (this._scrollB && !this._loopScrollB) {
|
||||
return;
|
||||
}
|
||||
this._scrollB = true;
|
||||
|
||||
// 停止循环滚动
|
||||
if (this._loopScrollB) {
|
||||
this._loopTween.stop();
|
||||
this._loopTween = null;
|
||||
this._loopScrollB = false;
|
||||
}
|
||||
|
||||
this._currTargetN = indexN_;
|
||||
this._lastCurveYN = 0;
|
||||
// 更新移动距离
|
||||
this._updateMoveDist(indexN_);
|
||||
// 开始滚动
|
||||
this._currTweenN = 0;
|
||||
this._curveComp.startTween(this._currTweenN);
|
||||
}
|
||||
/* ------------------------------- 自定义事件 ------------------------------- */
|
||||
updateEvent(yN_: number, indexN_: number, y2N_: number): void {
|
||||
if (this.dire === RollingLotteryDirection.HORIZONTAL) {
|
||||
} else {
|
||||
this._scrollChild(cc.v3(0, (yN_ - this._lastCurveYN) * this._totalDistV3.y));
|
||||
}
|
||||
this._lastCurveYN = yN_;
|
||||
if (yN_ === undefined) {
|
||||
debugger;
|
||||
}
|
||||
// cc.log('缓动更新', yN_, indexN_, y2N_, yN_ - this._lastCurveYN);
|
||||
}
|
||||
|
||||
endEvent(): void {
|
||||
this._scrollB = false;
|
||||
// 继续缓动
|
||||
this._currTweenN++;
|
||||
if (this._currTweenN < this._curveComp.tweenUnitAs.length) {
|
||||
this._curveComp.startTween(this._currTweenN);
|
||||
}
|
||||
}
|
||||
/* ------------------------------- 节点事件 ------------------------------- */
|
||||
private _nodeSiblingOrderChanged(): void {
|
||||
|
2684
assets/main.scene
21
preview-template/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
61
preview-template/README.md
Normal file
@ -0,0 +1,61 @@
|
||||
# ccc-devtools v3.0.0
|
||||
Cocos Creator 网页调试工具,运行时查看、修改节点树,实时更新节点属性,可视化缓存资源。
|
||||
|
||||
## 功能
|
||||
|
||||
- 场景节点树实时显示,节点、组件属性实时显示更改
|
||||
|
||||

|
||||
|
||||
- 可视化缓存资源
|
||||
|
||||

|
||||
|
||||
- 标记场景中节点位置
|
||||
|
||||

|
||||
|
||||
- 输出节点、组件引用到控制台
|
||||
|
||||

|
||||
|
||||
- cc控制台功能扩展
|
||||
|
||||

|
||||
|
||||
## 全局使用
|
||||
|
||||
Cocos Creator 3.0暂不支持全局使用
|
||||
|
||||
## 项目使用
|
||||
|
||||
- `cd PROJECT_PATH && git clone -b v3.0.0 https://github.com/potato47/ccc-devtools.git preview-template`
|
||||
- 或者手动将本项目对应分支下载到项目目录后,将名字改为 `preview-template`
|
||||
|
||||
## 自定义
|
||||
|
||||
- 本项目使用了 vue 和 vuetify,可根据 [vuetify 文档](https://vuetifyjs.com/en/getting-started/quick-start/) 对页面进行修改
|
||||
|
||||
- 节点、组件显示属性可在 `config.js` 里配置,目前支持 text,number,textarea,color,bool 几种类型
|
||||
|
||||
## 需求、更新
|
||||
|
||||
https://github.com/potato47/ccc-devtools
|
||||
|
||||
如果没有更改源码,可直接在目录下 git pull
|
||||
|
||||
论坛讨论地址:https://forum.cocos.com/t/creator-20190201/71578
|
||||
|
||||
## 贡献指南
|
||||
|
||||
- 版本号命名规则 https://semver.org/lang/zh-CN/ ,简单来讲,新功能第二位加一,修复bug第三位加一
|
||||
|
||||
- 如果新增功能请在README中添加预览截图说明
|
||||
|
||||
- 记得更新version.json中的版本号
|
||||
|
||||
## 前人种树
|
||||
|
||||
- https://github.com/vuejs/vue
|
||||
|
||||
- https://github.com/vuetifyjs/vuetify
|
68
preview-template/config.js
Normal file
@ -0,0 +1,68 @@
|
||||
const NEX_CONFIG = {
|
||||
nodeSchema: {
|
||||
node: {
|
||||
title: 'Node',
|
||||
key: 'cc.Node',
|
||||
rows: [
|
||||
{ name: 'Name', key: 'name', type: 'text' },
|
||||
{ name: 'Position.X', parentKey: 'position', key: 'x', type: 'object_number', method: 'setPosition' },
|
||||
{ name: 'Position.Y', parentKey: 'position', key: 'y', type: 'object_number', method: 'setPosition' },
|
||||
{ name: 'Position.Z', parentKey: 'position', key: 'z', type: 'object_number', method: 'setPosition' },
|
||||
{ name: 'Angle.X', parentKey: 'eulerAngles', key: 'x', type: 'object_number', method: 'setRotationFromEuler' },
|
||||
{ name: 'Angle.Y', parentKey: 'eulerAngles', key: 'y', type: 'object_number', method: 'setRotationFromEuler' },
|
||||
{ name: 'Angle.Z', parentKey: 'eulerAngles', key: 'z', type: 'object_number', method: 'setRotationFromEuler' },
|
||||
{ name: 'Scale.X', parentKey: 'scale', key: 'x', type: 'object_number', method: 'setScale' },
|
||||
{ name: 'Scale.Y', parentKey: 'scale', key: 'y', type: 'object_number', method: 'setScale' },
|
||||
{ name: 'Scale.Z', parentKey: 'scale', key: 'z', type: 'object_number', method: 'setScale' },
|
||||
]
|
||||
},
|
||||
},
|
||||
componentsSchema: {
|
||||
'cc.Camera': {
|
||||
title: 'cc.Camera',
|
||||
key: 'cc.Camera',
|
||||
rows: [
|
||||
{ name: 'ClearDepth', key: 'clearDepth', type: 'number' },
|
||||
{ name: 'ClearColor', key: 'hex_clearColor', rawKey: 'clearColor', type: 'color' },
|
||||
]
|
||||
},
|
||||
'cc.DirectionalLight': {
|
||||
title: 'cc.DirectionalLight',
|
||||
key: 'cc.DirectionalLight',
|
||||
rows: [
|
||||
{ name: 'UseColorTemperature', key: 'useColorTemperature', type: 'bool' },
|
||||
{ name: 'ColorTemperature', key: 'colorTemperature', type: 'number' },
|
||||
{ name: 'Illuminance', key: 'illuminance', type: 'number' },
|
||||
{ name: 'Color', key: 'hex_color', rawKey: 'color', type: 'color' },
|
||||
]
|
||||
},
|
||||
'cc.UITransform': {
|
||||
key: 'cc.UITransform',
|
||||
title: 'cc.UITransform',
|
||||
rows: [
|
||||
{ name: 'Width', key: 'width', type: 'number' },
|
||||
{ name: 'Height', key: 'height', type: 'number' },
|
||||
{ name: 'AnchorX', key: 'anchorX', type: 'number' },
|
||||
{ name: 'AnchorY', key: 'anchorY', type: 'number' },
|
||||
{ name: 'Priority', key: 'priority', type: 'number' },
|
||||
]
|
||||
},
|
||||
'cc.Sprite': {
|
||||
key: 'cc.Sprite',
|
||||
title: 'cc.Sprite',
|
||||
rows: [
|
||||
{ name: 'Color', key: 'hex_color', rawKey: 'color', type: 'color' },
|
||||
]
|
||||
},
|
||||
'cc.Label': {
|
||||
title: 'cc.Label',
|
||||
key: 'cc.Label',
|
||||
rows: [
|
||||
{ name: 'String', key: 'string', type: 'textarea' },
|
||||
{ name: 'Font Size', key: 'fontSize', type: 'number' },
|
||||
{ name: 'Line Height', key: 'lineHeight', type: 'number' },
|
||||
{ name: 'Color', key: 'hex_color', rawKey: 'color', type: 'color' },
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
257
preview-template/custom.html
Normal file
@ -0,0 +1,257 @@
|
||||
<link href="./libs/css/materialdesignicons.min.css" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link href="./libs/css/vuetify.min.css" rel="stylesheet" type="text/css">
|
||||
<style>
|
||||
html {
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<v-app id="app">
|
||||
<v-app-bar app clipped-left color="gray" dense v-if="isShowTop">
|
||||
<v-app-bar-nav-icon @click.stop="drawer = !drawer"></v-app-bar-nav-icon>
|
||||
<div id="recompiling"><span>Recompiling...</span></div>
|
||||
<v-spacer></v-spacer>
|
||||
<%- include(cocosToolBar, {config: config}) %>
|
||||
<div class="toolbar">
|
||||
<!-- <div class="toolbar disabled"> -->
|
||||
<!-- <div class="item">
|
||||
<select id="opts-device" value="<%=config.device%>">
|
||||
<% Object.keys(devices).forEach((key) => {%>
|
||||
<option value="<%=key%>"><%=devices[key].name%>(<%=devices[key].width%> X <%=devices[key].height%>)</option>
|
||||
<% }) %>
|
||||
</select>
|
||||
</div>
|
||||
<div class="item"><button id="btn-rotate" class="<%=config.rotate ? 'checked' : ''%>">Rotate</button></div>
|
||||
<span style="font-size: small;" class="item">Debug Mode:</span>
|
||||
<div class="item">
|
||||
<select id="opts-debug-mode" value="<%=config.debugMode%>">
|
||||
<option value="0">None</option>
|
||||
<option value="1">Info</option>
|
||||
<option value="2">Warn</option>
|
||||
<option value="3">Error</option>
|
||||
<option value="4">Info For Web Page</option>
|
||||
<option value="5">Warn For Web Page</option>
|
||||
<option value="6">Error For Web Page</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="item"><button id="btn-show-fps" class="<%=config.showFps ? 'checked' : ''%>">Show FPS</button></div>
|
||||
<div class="item">
|
||||
<span style="font-size: small;" class="item">FPS:</span><input id="input-set-fps" type="number" value="<%=config.fps%>" />
|
||||
</div>
|
||||
<div style="margin-right: 0;" class="item"><button id="btn-pause">Pause</button><button id="btn-step">Step</button></div>
|
||||
<div class="item"><button id="btn-step" style="display: none;">Step</button></div> -->
|
||||
<v-icon @click="openCocosDocs" small>mdi-cloud-search</v-icon>
|
||||
<v-icon @click="openCocosForum" small>mdi-forum</v-icon>
|
||||
<v-icon @click="openCacheDialog" small>mdi-table</v-icon>
|
||||
<v-icon @click="openGithub" small>mdi-home</v-icon>
|
||||
</div>
|
||||
</v-app-bar>
|
||||
|
||||
<div v-if="!isShowTop">
|
||||
<div id="recompiling"><span>Recompiling...</span></div>
|
||||
<div class="toolbar">
|
||||
<div class="item">
|
||||
<select id="opts-device">
|
||||
<option value="0">Default</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="item">
|
||||
<v-btn id="btn-rotate" small height="25"><span style="color: #aaa;">Rotate</span></v-btn>
|
||||
</div>
|
||||
<span style="font-size: small;display: none;" class="item">Debug Mode:</span>
|
||||
<div class="item" style="display: none;">
|
||||
<select id="opts-debug-mode">
|
||||
<option value="0">None</option>
|
||||
<option value="1">Info</option>
|
||||
<option value="2">Warn</option>
|
||||
<option value="3">Error</option>
|
||||
<option value="4">Info For Web Page</option>
|
||||
<option value="5">Warn For Web Page</option>
|
||||
<option value="6">Error For Web Page</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="item">
|
||||
<v-btn id="btn-show-fps" small height="25"><span style="color: #aaa;">Show FPS</span></v-btn>
|
||||
</div>
|
||||
<div class="item">
|
||||
<span style="font-size: small;color: #aaa;" class="item">FPS:</span><input id="input-set-fps"
|
||||
type="number" />
|
||||
</div>
|
||||
<div style="margin-right: 0px;" class="item">
|
||||
<v-btn id="btn-pause" small height="25"><span style="color: #aaa;">Pause</span></v-btn>
|
||||
</div>
|
||||
<div class="item">
|
||||
<v-btn id="btn-step" style="display: none;" small height="25">
|
||||
<span style="color: #aaa;">Step</span>
|
||||
</v-btn>
|
||||
</div>
|
||||
<div class="item">
|
||||
<v-btn id="btn-recompile" small height="25"><span style="color: #aaa;">Recompile</span></v-btn>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<v-navigation-drawer v-model="drawer" app clipped fixed width="512" v-if="isShowTop">
|
||||
<v-container style="height: 50%;overflow: auto;">
|
||||
<v-text-field v-model="treeSearchText" dense label="Search Node or Component" dark flat solo-inverted
|
||||
hide-details clearable clear-icon="mdi-close-circle-outline"></v-text-field>
|
||||
<v-treeview :items="treeData" item-key="id" dense activatable :search="treeSearchText"
|
||||
:active.sync="selectedNodes">
|
||||
<template v-slot:label="{ item, active, open }">
|
||||
<label v-if="item.active" style="color: white;">{{ item.name }}</label>
|
||||
<label v-else style="color: gray;">{{ item.name }}</label>
|
||||
</template>
|
||||
</v-treeview>
|
||||
</v-container>
|
||||
<v-container style="border-top: 2px solid darkgray;height: 50%;overflow-y: auto;">
|
||||
<template v-if="selectedNode && selectedNode.parent">
|
||||
<!-- Node -->
|
||||
<table style="width: 100%;color: white;" border="1">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2" style="text-align: left; padding: 10px;">
|
||||
<div class="float-left" style="display:inline-flex;">
|
||||
<v-simple-checkbox v-model="selectedNode.active"></v-simple-checkbox>
|
||||
<span style="margin-left: 10px;">{{ nodeSchema.title }}</span>
|
||||
</div>
|
||||
<div class="float-right">
|
||||
<v-icon style="margin-left: 10px;margin-right: 10px;" @click="drawNodeRect()">
|
||||
mdi-adjust</v-icon>
|
||||
<v-icon @click="outputNodeHandler()">mdi-send</v-icon>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="row in nodeSchema.rows" :key="row.name">
|
||||
<td style="padding: 10px;width: 40%;">{{ row.name }}</td>
|
||||
<td style="width: 60%;">
|
||||
<v-color-picker v-if="row.type == 'color'" class="ma-2" canvas-height="80" width="259"
|
||||
v-model="selectedNode[row.key]"></v-color-picker>
|
||||
<v-simple-checkbox v-else-if="row.type == 'bool'" v-model="selectedNode[row.key]"
|
||||
style="padding: 10px;width: 100%;"></v-simple-checkbox>
|
||||
<input v-else-if="row.type == 'object_number'" type="number" v-model.number="selectedNode[row.parentKey][row.key]"
|
||||
style="padding: 10px;width: 100%;" @input="onSubPropInput(selectedNode, row)"></input>
|
||||
<input v-else :type="row.type" v-model="selectedNode[row.key]"
|
||||
style="padding: 10px;width: 100%;"></input>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!-- Components -->
|
||||
<table v-for="component in componentsSchema" style="width: 100%;color: white;" border="1">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2" style="text-align: left; padding: 10px;">
|
||||
<div class="float-left" style="display:inline-flex;">
|
||||
<v-simple-checkbox v-model="selectedNode[component.key].enabled">
|
||||
</v-simple-checkbox>
|
||||
<span style="margin-left: 10px;">{{ component.title }}</span>
|
||||
</div>
|
||||
<div class="float-right">
|
||||
<v-icon @click="outputComponentHandler(component.key)">mdi-send</v-icon>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="row in component.rows" :key="row.key">
|
||||
<td style="padding: 10px;width: 40%;">{{ row.name }}</td>
|
||||
<td style="width: 60%;">
|
||||
<v-color-picker v-if="row.type == 'color'" class="ma-2" canvas-height="80" width="259"
|
||||
v-model="selectedNode[component.key][row.key]"></v-color-picker>
|
||||
<textarea v-else-if="row.type == 'textarea'" rows="1"
|
||||
v-model="selectedNode[component.key][row.key]" style="padding: 10px;width: 100%;">
|
||||
</textarea>
|
||||
<v-simple-checkbox v-else-if="row.type == 'bool'"
|
||||
v-model="selectedNode[component.key][row.key]" style="padding: 10px;width: 100%;">
|
||||
</v-simple-checkbox>
|
||||
<input v-else-if="row.type == 'number'" type="number" v-model.number="selectedNode[component.key][row.key]"
|
||||
style="padding: 10px;width: 100%;"></input>
|
||||
<input v-else :type="row.type" v-model="selectedNode[component.key][row.key]"
|
||||
style="padding: 10px;width: 100%;"></input>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
<template v-else-if="selectedNode && !selectedNode.parent">
|
||||
<!-- TODO:场景根节点 -->
|
||||
</template>
|
||||
</v-container>
|
||||
</v-navigation-drawer>
|
||||
|
||||
<v-content>
|
||||
<v-container fill-height>
|
||||
<div id="content" class="content">
|
||||
<div class="contentWrap">
|
||||
<div id="GameDiv" class="wrapper">
|
||||
<canvas id="GameCanvas"></canvas>
|
||||
<div id="splash">
|
||||
<div class="progress-bar stripes"><span></span></div>
|
||||
</div>
|
||||
<div id="bulletin">
|
||||
<div id="sceneIsEmpty" class="inner"><%=tip_sceneIsEmpty%></div>
|
||||
</div>
|
||||
<div class="error" id="error">
|
||||
<div class="title">Error <i>(Please open the console to see detailed errors)</i></div>
|
||||
<div class="error-main"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="footer">
|
||||
Created with <a href="https://www.cocos.com/products" target="_blank" title="Cocos Creator 3D">Cocos Creator 3D</a>
|
||||
</p>
|
||||
</div>
|
||||
</v-container>
|
||||
</v-content>
|
||||
|
||||
<v-dialog v-model="cacheDialog" persistent scrollable>
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
{{ cacheTitle }}
|
||||
<v-spacer></v-spacer>
|
||||
<v-text-field v-model="cacheSearchText" append-icon="mdi-magnify" label="Search" single-line
|
||||
hide-details>
|
||||
</v-text-field>
|
||||
</v-card-title>
|
||||
<v-divider></v-divider>
|
||||
<v-card-text>
|
||||
<v-data-table :headers="cacheHeaders" :items="cacheData" :search="cacheSearchText" :sort-by="['size']"
|
||||
click:row="openGithub"
|
||||
:sort-desc="[true]" :footer-props="{
|
||||
showFirstLastPage: true,
|
||||
firstIcon: 'mdi-chevron-double-left',
|
||||
lastIcon: 'mdi-chevron-double-right',
|
||||
}">
|
||||
<template v-slot:item.size="{ item }">
|
||||
{{ item.size == -1 ? '_' : (item.size +'MB') }}
|
||||
</template>
|
||||
<template v-slot:item.preview="{ item }">
|
||||
<div style="height: 60px;display: flex;align-items: center;">
|
||||
<img :src="window.location.protocol + '//' + window.location.host + '/' + item.preview"
|
||||
style="max-height: 60px;max-width: 120px;" v-if="item.preview">
|
||||
<template v-else>_</template>
|
||||
</div>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</v-card-text>
|
||||
<v-divider></v-divider>
|
||||
<v-card-actions>
|
||||
<v-btn color="blue darken-1" text @click="cacheDialog = false">Close</v-btn>
|
||||
<v-spacer></v-spacer>
|
||||
<v-switch v-model="cacheOnlyTexture" label="只显示纹理"></v-switch>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
</v-app>
|
||||
|
||||
<script src="./libs/js/vue.min.js"></script>
|
||||
<script src="./libs/js/vuetify.js"></script>
|
||||
<script src="./config.js"></script>
|
||||
<script src="./libs/js/cc-console-utils.js"></script>
|
||||
<script src="./preview.js"></script>
|
||||
|
||||
<%- include(cocosTemplate, {}) %>
|
23
preview-template/index.ejs
Normal file
@ -0,0 +1,23 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="icon" href="./favicon.ico" />
|
||||
<meta charset="utf-8" />
|
||||
<title><%=title%></title>
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width,user-scalable=no,initial-scale=1, minimum-scale=1,maximum-scale=1"
|
||||
/>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="full-screen" content="yes" />
|
||||
<meta name="screen-orientation" content="portrait" />
|
||||
<meta name="x5-fullscreen" content="true" />
|
||||
<meta name="360-fullscreen" content="true" />
|
||||
<meta name="renderer" content="webkit" />
|
||||
<meta name="force-rendering" content="webkit" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<link rel="stylesheet" type="text/css" href="./index.css" />
|
||||
</head>
|
||||
<body>
|
||||
<%- include("./custom.html") %>
|
||||
</body>
|
||||
</html>
|
336
preview-template/libs/css/googlefonts.css
Normal file
@ -0,0 +1,336 @@
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
src: local('Roboto Thin'), local('Roboto-Thin'), url(../fonts/googlefonts-base.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
src: local('Roboto Thin'), local('Roboto-Thin'), url(../fonts/googlefonts-base.woff22) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
src: local('Roboto Thin'), local('Roboto-Thin'), url(../fonts/googlefonts-base.woff22) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
src: local('Roboto Thin'), local('Roboto-Thin'), url(../fonts/googlefonts-base.woff22) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
src: local('Roboto Thin'), local('Roboto-Thin'), url(../fonts/googlefonts-base.woff22) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
src: local('Roboto Thin'), local('Roboto-Thin'), url(../fonts/googlefonts-base.woff22) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
src: local('Roboto Thin'), local('Roboto-Thin'), url(../fonts/googlefonts-base.woff2format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Roboto Light'), local('Roboto-Light'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Roboto Light'), local('Roboto-Light'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Roboto Light'), local('Roboto-Light'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Roboto Light'), local('Roboto-Light'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Roboto Light'), local('Roboto-Light'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Roboto Light'), local('Roboto-Light'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Roboto Light'), local('Roboto-Light'), url(../fonts/googlefonts-base.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(../fonts/googlefonts-base.woff2format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(../fonts/googlefonts-base.woff2format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(../fonts/googlefonts-base.woff2format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(../fonts/googlefonts-base.woff2format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(../fonts/googlefonts-base.woff2format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(../fonts/googlefonts-base.woff2format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(../fonts/googlefonts-base.woff2rmat('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(../fonts/googlefonts-base.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Roboto Bold'), local('Roboto-Bold'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Roboto Bold'), local('Roboto-Bold'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Roboto Bold'), local('Roboto-Bold'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Roboto Bold'), local('Roboto-Bold'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Roboto Bold'), local('Roboto-Bold'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Roboto Bold'), local('Roboto-Bold'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Roboto Bold'), local('Roboto-Bold'), url(../fonts/googlefonts-base.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
src: local('Roboto Black'), local('Roboto-Black'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
src: local('Roboto Black'), local('Roboto-Black'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
src: local('Roboto Black'), local('Roboto-Black'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
src: local('Roboto Black'), local('Roboto-Black'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
src: local('Roboto Black'), local('Roboto-Black'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
src: local('Roboto Black'), local('Roboto-Black'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
src: local('Roboto Black'), local('Roboto-Black'), url(../fonts/googlefonts-base.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
1
preview-template/libs/css/materialdesignicons.min.css
vendored
Normal file
8
preview-template/libs/css/vuetify.min.css
vendored
Normal file
BIN
preview-template/libs/fonts/googlefonts-base.woff2
Normal file
BIN
preview-template/libs/fonts/materialdesignicons-webfont.eot
Normal file
BIN
preview-template/libs/fonts/materialdesignicons-webfont.ttf
Normal file
BIN
preview-template/libs/fonts/materialdesignicons-webfont.woff
Normal file
BIN
preview-template/libs/fonts/materialdesignicons-webfont.woff2
Normal file
178
preview-template/libs/js/cc-console-utils.js
Normal file
@ -0,0 +1,178 @@
|
||||
const initConsoleUtil = function () {
|
||||
if (cc.tree) return;
|
||||
cc.tree = function (key) {
|
||||
let index = key || 0;
|
||||
let treeNode = function (node) {
|
||||
let nameStyle =
|
||||
`color: ${node.parent === null || node.activeInHierarchy ? 'green' : 'grey'}; font-size: 14px;font-weight:bold`;
|
||||
let propStyle =
|
||||
`color: black; background: lightgrey;margin-left: 5px;border-radius:3px;padding: 0 3px;font-size: 10px;font-weight:bold`;
|
||||
let indexStyle =
|
||||
`color: orange; background: black;margin-left: 5px;border-radius:3px;padding:0 3px;fonrt-size: 10px;font-weight:bold;`
|
||||
let nameValue = `%c${node.name}`;
|
||||
let propValue =
|
||||
`%c${node.position.x.toFixed(0) + ',' + node.position.y.toFixed(0)}`
|
||||
let indexValue = `%c${index++}`;
|
||||
if (node.children.length > 0) {
|
||||
console.groupCollapsed(nameValue + propValue + indexValue, nameStyle,
|
||||
propStyle, indexStyle);
|
||||
for (let i = 0; i < node.children.length; i++) {
|
||||
treeNode(node.children[i]);
|
||||
}
|
||||
console.groupEnd();
|
||||
} else {
|
||||
console.log(nameValue + propValue + indexValue, nameStyle, propStyle,
|
||||
indexStyle);
|
||||
}
|
||||
}
|
||||
if (key) {
|
||||
let node = cc.cat(key);
|
||||
index = node['tempIndex'];
|
||||
treeNode(node);
|
||||
} else {
|
||||
let scene = cc.director.getScene();
|
||||
treeNode(scene);
|
||||
}
|
||||
return '属性依次为x,y,width,height,scale.使用cc.cat(id)查看详细属性.';
|
||||
}
|
||||
cc.cat = function (key) {
|
||||
let index = 0;
|
||||
let target;
|
||||
let sortId = function (node) {
|
||||
if (target) return;
|
||||
if (cc.js.isNumber(key)) {
|
||||
if (key === index++) {
|
||||
target = node;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (key.toLowerCase() === node.name.toLowerCase()) {
|
||||
target = node;
|
||||
return;
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
if (node.children.length > 0) {
|
||||
for (let i = 0; i < node.children.length; i++) {
|
||||
sortId(node.children[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
let scene = cc.director.getScene();
|
||||
sortId(scene);
|
||||
target['tempIndex'] = cc.js.isNumber(key) ? key : index;
|
||||
return target;
|
||||
}
|
||||
cc.list = function (key) {
|
||||
let targets = [];
|
||||
let step = function (node) {
|
||||
if (node.name.toLowerCase().indexOf(key.toLowerCase()) > -1) {
|
||||
targets.push(node);
|
||||
}
|
||||
if (node.children.length > 0) {
|
||||
for (let i = 0; i < node.children.length; i++) {
|
||||
step(node.children[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
let scene = cc.director.getScene();
|
||||
step(scene);
|
||||
if (targets.length === 1) {
|
||||
return targets[0];
|
||||
} else {
|
||||
return targets;
|
||||
}
|
||||
}
|
||||
cc.where = function (key) {
|
||||
let target = key.name ? key : cc.cat(key);
|
||||
if (!target) {
|
||||
return null;
|
||||
}
|
||||
let rect;
|
||||
let transform = target.getComponent(cc.UITransformComponent);
|
||||
if (transform) {
|
||||
rect = getSelfBoundingBoxToWold(transform);
|
||||
} else {
|
||||
let worldPos = cc.v3();
|
||||
target.getWorldPosition(worldPos);
|
||||
rect = cc.rect(worldPos.x, worldPos.y, 0, 0);
|
||||
}
|
||||
let canvasNode = new cc.Node('Canvas');
|
||||
let scene = cc.director.getScene();
|
||||
scene.addChild(canvasNode);
|
||||
canvasNode.addComponent(cc.Canvas);
|
||||
let bgNode = new cc.Node();
|
||||
let graphics = bgNode.addComponent(cc.GraphicsComponent);
|
||||
let bgTransform = bgNode.addComponent(cc.UITransformComponent);
|
||||
canvasNode.addChild(bgNode);
|
||||
let centerPos = cc.v3(rect.center.x, rect.center.y, 0);
|
||||
let localPos = cc.v3();
|
||||
canvasNode.getComponent(cc.UITransformComponent).convertToNodeSpaceAR(centerPos, localPos);
|
||||
bgNode.setPosition(localPos);
|
||||
bgNode.layer = target.layer;
|
||||
let isZeroSize = rect.width === 0 || rect.height === 0;
|
||||
if (isZeroSize) {
|
||||
graphics.circle(0, 0, 100);
|
||||
graphics.fillColor = cc.Color.GREEN;
|
||||
graphics.fill();
|
||||
} else {
|
||||
bgTransform.width = rect.width;
|
||||
bgTransform.height = rect.height;
|
||||
graphics.rect(-bgTransform.width / 2, -bgTransform.height / 2, bgTransform.width, bgTransform.height);
|
||||
graphics.fillColor = new cc.Color().fromHEX('#E91E6390');
|
||||
graphics.fill();
|
||||
}
|
||||
setTimeout(() => {
|
||||
if (cc.isValid(canvasNode)) {
|
||||
canvasNode.destroy();
|
||||
}
|
||||
}, 2000);
|
||||
return target;
|
||||
}
|
||||
cc.cache = function () {
|
||||
let rawCacheData = cc.loader._cache;
|
||||
let cacheData = [];
|
||||
let totalTextureSize = 0;
|
||||
for (let k in rawCacheData) {
|
||||
let item = rawCacheData[k];
|
||||
let preview = '';
|
||||
let type = item.__classname__;
|
||||
let formatSize = -1;
|
||||
if (type === 'cc.Texture2D') {
|
||||
let image = item.mipmaps[0]
|
||||
preview = image.url;
|
||||
let textureSize = image.width * image.height * ((image._native === '.jpg' ? 3 : 4) / 1024 / 1024);
|
||||
totalTextureSize += textureSize;
|
||||
// sizeStr = textureSize.toFixed(3) + 'M';
|
||||
formatSize = Math.round(textureSize * 1000) / 1000;
|
||||
}
|
||||
cacheData.push({
|
||||
type: type,
|
||||
preview: preview,
|
||||
id: item._uuid,
|
||||
size: formatSize
|
||||
});
|
||||
}
|
||||
let cacheTitle = `缓存 [文件总数:${cacheData.length}][纹理缓存:${totalTextureSize.toFixed(2) + 'M'}]`;
|
||||
return [cacheData, cacheTitle];
|
||||
}
|
||||
}
|
||||
|
||||
function getSelfBoundingBoxToWold(transform) {
|
||||
let _worldMatrix = cc.mat4();
|
||||
if (transform.node.parent) {
|
||||
transform.node.parent.getWorldMatrix(_worldMatrix);
|
||||
let parentMat = _worldMatrix;
|
||||
let _matrix = cc.mat4();
|
||||
cc.Mat4.fromRTS(_matrix, transform.node.getRotation(), transform.node.getPosition(), transform.node.getScale());
|
||||
const width = transform._contentSize.width;
|
||||
const height = transform._contentSize.height;
|
||||
const rect = cc.rect(-transform._anchorPoint.x * width, -transform._anchorPoint.y * height, width, height);
|
||||
cc.Mat4.multiply(_worldMatrix, parentMat, _matrix);
|
||||
rect.transformMat4(_worldMatrix);
|
||||
return rect;
|
||||
} else {
|
||||
return transform.getBoundingBox();
|
||||
}
|
||||
}
|
6
preview-template/libs/js/vue.min.js
vendored
Normal file
41498
preview-template/libs/js/vuetify.js
Normal file
201
preview-template/preview.js
Normal file
@ -0,0 +1,201 @@
|
||||
const app = new Vue({
|
||||
el: '#app',
|
||||
vuetify: new Vuetify({
|
||||
theme: { dark: true }
|
||||
}),
|
||||
data: {
|
||||
isShowTop: true,
|
||||
drawer: false,
|
||||
cacheDialog: false,
|
||||
cacheTitle: '',
|
||||
cacheHeaders: [
|
||||
{ text: 'Type', value: 'type' },
|
||||
{ text: 'Preivew', value: 'preview' },
|
||||
{ text: 'ID', value: 'id' },
|
||||
{ text: 'Size', value: 'size' },
|
||||
],
|
||||
cacheRawData: [],
|
||||
cacheData: [],
|
||||
cacheSearchText: null,
|
||||
cacheOnlyTexture: true,
|
||||
treeData: [],
|
||||
selectedNodes: [],
|
||||
intervalId: -1,
|
||||
treeSearchText: null,
|
||||
nodeSchema: {},
|
||||
componentsSchema: [],
|
||||
},
|
||||
created() {
|
||||
if (window.innerHeight === window.outerHeight) { // 手机端,chrome device模式
|
||||
this.isShowTop = false;
|
||||
}
|
||||
this.waitCCInit().then(() => {
|
||||
if (this.isShowTop) {
|
||||
this.startUpdateTree();
|
||||
}
|
||||
initConsoleUtil();
|
||||
});
|
||||
},
|
||||
watch: {
|
||||
cacheOnlyTexture() {
|
||||
this.updateCacheData();
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
treeFilter() {
|
||||
return (item, search, textKey) => item[textKey].indexOf(search) > -1;
|
||||
},
|
||||
selectedNode() {
|
||||
if (!this.selectedNodes.length) return undefined
|
||||
let node = getNodeById(this.selectedNodes[0]);
|
||||
if (node) {
|
||||
let superPreLoad = node._onPreDestroy;
|
||||
node._onPreDestroy = () => {
|
||||
superPreLoad.apply(node);
|
||||
if (this.selectedNodes.length > 0 && this.selectedNodes[0] === node._id) {
|
||||
this.selectedNodes.pop();
|
||||
}
|
||||
}
|
||||
this.nodeSchema = NEX_CONFIG.nodeSchema.node;
|
||||
let componentsSchema = [];
|
||||
for (let component of node._components) {
|
||||
let schema = NEX_CONFIG.componentsSchema[component.__classname__];
|
||||
if (schema) {
|
||||
node[schema.key] = node.getComponent(schema.key);
|
||||
for (let i = 0; i < schema.rows.length; i++) {
|
||||
if (schema.rows[i].type === 'color') {
|
||||
if (!node[schema.key][schema.rows[i].key]) {
|
||||
cc.js.getset(node[schema.key], schema.rows[i].key, () => {
|
||||
return '#' + node.getComponent(schema.key)[schema.rows[i].rawKey].toHEX('#rrggbb');
|
||||
}, (hex) => {
|
||||
node.getComponent(schema.key)[schema.rows[i].rawKey] = new cc.Color().fromHEX(hex);
|
||||
}, false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
schema = {
|
||||
title: component.__classname__,
|
||||
key: component.__classname__
|
||||
};
|
||||
node[schema.key] = node.getComponent(schema.key);
|
||||
}
|
||||
componentsSchema.push(schema);
|
||||
}
|
||||
this.componentsSchema = componentsSchema;
|
||||
}
|
||||
return node;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
waitCCInit() {
|
||||
return new Promise((resolve, reject) => {
|
||||
let id = setInterval(() => {
|
||||
if (window.cc) {
|
||||
resolve();
|
||||
clearInterval(id);
|
||||
}
|
||||
}, 500);
|
||||
});
|
||||
},
|
||||
refreshTree: function () {
|
||||
if (!this.$data.drawer || !window.cc) {
|
||||
return;
|
||||
}
|
||||
let currentScene = cc.director.getScene();
|
||||
if (!currentScene || !currentScene.children) {
|
||||
return;
|
||||
}
|
||||
// 包含场景根节点
|
||||
// this.$data.treeData = [{ id: currentScene._id, name: 'Scene', active: currentScene.activeInHierarchy, open: true, children: getChildren(currentScene) }];
|
||||
this.$data.treeData = getChildren(currentScene);
|
||||
},
|
||||
startUpdateTree: function () {
|
||||
this.$data.intervalId = setInterval(() => {
|
||||
this.refreshTree();
|
||||
}, 200);
|
||||
},
|
||||
stopUpdateTree: function () {
|
||||
clearInterval(this.$data.intervalId);
|
||||
},
|
||||
onSubPropInput: function (target, row) {
|
||||
let value = target[row.parentKey][row.key];
|
||||
if (typeof value === 'number' && !isNaN(value)) {
|
||||
if (row.method === 'setRotationFromEuler') {
|
||||
target.setRotationFromEuler(target.eulerAngles.x, target.eulerAngles.y, target.eulerAngles.z);
|
||||
} else {
|
||||
target[row.method](target[row.parentKey]);
|
||||
}
|
||||
}
|
||||
},
|
||||
outputNodeHandler(id) {
|
||||
let i = 1;
|
||||
while (window['temp' + i] !== undefined) {
|
||||
i++;
|
||||
}
|
||||
window['temp' + i] = this.selectedNode;
|
||||
console.log('temp' + i);
|
||||
console.log(window['temp' + i]);
|
||||
},
|
||||
outputComponentHandler(component) {
|
||||
let i = 1;
|
||||
while (window['temp' + i] !== undefined) {
|
||||
i++;
|
||||
}
|
||||
window['temp' + i] = this.selectedNode.getComponent(component);
|
||||
console.log('temp' + i);
|
||||
console.log(window['temp' + i]);
|
||||
},
|
||||
drawNodeRect() {
|
||||
cc.where(this.selectedNode);
|
||||
},
|
||||
updateCacheData() {
|
||||
if (this.$data.cacheOnlyTexture) {
|
||||
this.$data.cacheData = this.$data.cacheRawData.filter(item => item.type === 'cc.Texture2D');
|
||||
} else {
|
||||
this.$data.cacheData = this.$data.cacheRawData;
|
||||
}
|
||||
},
|
||||
openCacheDialog() {
|
||||
[this.$data.cacheRawData, this.$data.cacheTitle] = cc.cache();
|
||||
this.updateCacheData();
|
||||
this.$data.cacheDialog = true;
|
||||
},
|
||||
openGithub() {
|
||||
window.open('https://github.com/potato47/ccc-devtools');
|
||||
},
|
||||
openCocosForum() {
|
||||
window.open('https://forum.cocos.com/');
|
||||
},
|
||||
openCocosDocs() {
|
||||
window.open('https://docs.cocos.com/');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function getChildren(node) {
|
||||
return node.children.map(child => {
|
||||
let children = (child.children && child.children.length > 0) ? getChildren(child) : [];
|
||||
return { id: child._id, name: child.name, active: child.activeInHierarchy, children };
|
||||
});
|
||||
}
|
||||
|
||||
function getNodeById(id) {
|
||||
let target;
|
||||
const search = function (node) {
|
||||
if (node._id === id) {
|
||||
target = node;
|
||||
return;
|
||||
}
|
||||
if (node.children.length) {
|
||||
for (let i = 0; i < node.children.length; i++) {
|
||||
if (!target) {
|
||||
search(node.children[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const scene = cc.director.getScene();
|
||||
search(scene);
|
||||
return target;
|
||||
}
|
BIN
preview-template/screenshots/preview1.gif
Normal file
After Width: | Height: | Size: 849 KiB |
BIN
preview-template/screenshots/preview2.png
Normal file
After Width: | Height: | Size: 121 KiB |
BIN
preview-template/screenshots/preview3.png
Normal file
After Width: | Height: | Size: 119 KiB |
BIN
preview-template/screenshots/preview4.png
Normal file
After Width: | Height: | Size: 205 KiB |
BIN
preview-template/screenshots/preview5.png
Normal file
After Width: | Height: | Size: 189 KiB |
BIN
preview-template/screenshots/t1.png
Normal file
After Width: | Height: | Size: 9.2 KiB |
BIN
preview-template/screenshots/t2.png
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
preview-template/screenshots/t3.png
Normal file
After Width: | Height: | Size: 211 KiB |