diff --git a/README.md b/README.md index a2e1dbb..411cd4d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ccc-devtools v2.1.1 +# ccc-devtools v2.2.0 Cocos Creator 网页调试器,运行时查看、修改节点树,实时更新节点属性。 ## 预览 @@ -22,7 +22,7 @@ v2.1.0: 区分手动刷新和自动刷新两种模式,手动刷新时支持搜 ![preview3](./screenshots/preview3.png) -v2.1.2: 新增黑色主题 +v2.2.0: 新增黑色主题(感谢[@wheatup](https://github.com/wheatup) ) ![dark-theme](./screenshots/dark-theme.png) ## 使用 @@ -51,6 +51,14 @@ 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 diff --git a/css/style.css b/css/style.css new file mode 100644 index 0000000..659c140 --- /dev/null +++ b/css/style.css @@ -0,0 +1,36 @@ +body { + background-color: #333; +} + +.el-color-picker, .el-color-picker__trigger { + width: 100% !important; +} + +.el-input-number .el-input__inner { + text-align: left; +} + +.el-input-number.is-controls-right .el-input__inner { + padding-left: 5px; + padding-right: 5px; +} + +.el-card__body { + padding-left: 20px; + padding-right: 10px; +} + +.ivu-collapse-content { + color: #515a6e; + padding: 0 5px 0 16px; + background-color: #fff; +} + +.el-input-number--mini .el-input-number__decrease, .el-input-number--mini .el-input-number__increase { + width: 12px; + font-size: 12px; +} + +.el-select.el-select--mini { + width: 100%; +} \ No newline at end of file diff --git a/index.html b/index.html index 1a55d57..04306ea 100644 --- a/index.html +++ b/index.html @@ -1,67 +1,50 @@ - - - - + + + + -
-
+ +
+
+
- - - + + + + - {{ node.label }} + {{ node.label }} - +
自动刷新 - + +
-
+ +
@@ -72,51 +55,70 @@
2.5d
+ @click="handleChangeNodeSchema">2.5d
{{ nodeSchema.title }} - 输出引用 - 标记位置 + 输出引用 + 标记位置

- - + + {{col.field}} - - - - + + + + + +
- +
{{ section.title }} - 输出引用 + 输出引用

- - + + {{col.field}} - - - + + + - - - - + + + + @@ -128,6 +130,7 @@
+
@@ -137,8 +140,8 @@ 节点树 - + @@ -147,8 +150,8 @@ FPS - + @@ -157,8 +160,8 @@ 帧时间 - + @@ -167,8 +170,29 @@ 内存 - + + + + + + + + + 暗黑主题 + + + @@ -186,430 +210,26 @@
+ + + + + + +
- - - - - + + + + + + + - - diff --git a/js/app.js b/js/app.js new file mode 100644 index 0000000..92e0a2d --- /dev/null +++ b/js/app.js @@ -0,0 +1,579 @@ +let app = new Vue({ + el: '#app', + data: { + needUpdate: false, + is3DNode: false, + isDevMode: false, + isShowFPS: false, + isShowMS: false, + isShowMB: false, + isShowCache: false, + isAutoRefreshTree: true, + isDarkTheme: false, + filterText: '', + splitLeft: 0.7, + splitRight: 0.5, + defaultExpandedKeys: [], + sceneTreeData: [], + nodeProps: { + children: 'children', + label: 'name', + isLeaf: 'leaf' + }, + node: null, + nodeSchema: {}, + componentsSchema: [], + intervalId: -1, + cacheTitle: '缓存', + cacheColumns: [{ + title: 'Type', + key: 'content', + width: 150, + align: 'center', + filters: [{ + label: 'png', + value: 'png' + }, { + label: 'jpg', + value: 'jpg' + }, { + label: 'cc.Texture2D', + value: 'cc.Texture2D' + }, { + label: 'cc.SpriteFrame', + value: 'cc.SpriteFrame' + }, { + label: 'cc.Sprite', + value: 'cc.Sprite' + }, { + label: 'cc.Prefab', + value: 'cc.Prefab' + }, { + label: 'cc.AnimationClip', + value: 'cc.AnimationClip' + }], + filterMultiple: false, + filterMethod(value, row) { + return row.content === value; + } + }, { + title: 'Name', + key: 'name', + width: 220, + align: 'center', + sortable: true + }, { + title: 'Size', + key: 'size', + align: 'center', + width: 120, + sortable: true + }, { + title: 'Queue', + key: 'queueId', + sortable: true, + width: 120, + align: 'center' + }, { + title: 'Preview', + slot: 'cache_preview', + align: 'center', + width: 150 + }, { + title: 'Action', + slot: 'cache_action', + width: 150, + align: 'center', + fixed: 'right' + }], + cacheData: [], + cacheDataLoading: false + }, + methods: { + api: function (url, cb) { + let xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function () { + if (xhr.readyState == 4 && (xhr.status >= 200 && xhr.status < 400)) { + let response = xhr.responseText; + cb(JSON.parse(xhr.responseText)); + } + }; + xhr.open("GET", url, true); + xhr.send(); + }, + compareVersion: function (localVersion, remoteVersion) { + let vL = localVersion.split('.'); + let vR = remoteVersion.split('.'); + for (let i = 0; i < vL.length; ++i) { + let a = parseInt(vL[i], 10); + let b = parseInt(vR[i] || '0', 10); + if (a === b) { + continue; + } else if (a > b) { + return false; + } else if (a < b) { + return true; + } + } + if (vR.length > vL.length) { + return true; + } else { + return false; + } + }, + checkVersion: function () { + this.api('https://raw.githubusercontent.com/potato47/ccc-devtools/master/version.json', ( + data) => { + let remoteVersion = data.version; + this.api('app/editor/static/preview-templates/ccc-devtools/version.json', ( + data) => { + let localVersion = data.version; + versionStatus = this.compareVersion(localVersion, remoteVersion); + this.$data.needUpdate = versionStatus; + console.groupCollapsed('ccc-devtoos') + console.log('本地版本:' + localVersion); + console.log('远程版本:' + remoteVersion); + console.log('更新地址:' + 'https://github.com/potato47/ccc-devtools.git'); + console.groupEnd('ccc-devtoos'); + }); + }); + }, + handleRefreshTree() { + this.$data.sceneTreeData = []; + setTimeout(() => { + this.updateTreeData(); + }, 0); + }, + handleSwitchTreeMode() { + + }, + updateTreeData() { + this.$data.sceneTreeData = cc.director.getScene().children; + this.defaultExpandedKeys = [this.$data.sceneTreeData[0]._id]; + }, + handleNodesSelect(nodes) { + if (nodes.length === 1) { + this.handleNodeClick(nodes[0]); + } else { + this.handleNodeClick(null); + } + }, + handleNodeClick(node) { + if (node) { + this.$data.node = node; + cc.js.getset(node, 'hex_color', () => { + return '#' + node.color.toHEX('#rrggbb'); + }, (hex) => { + node.color = new cc.Color().fromHEX(hex); + }, false, true); + let superPreLoad = node._onPreDestroy; + node._onPreDestroy = () => { + superPreLoad.apply(node); + if (this.$data && this.$data.node === node) { + this.$data.node = null; + } + } + this.$data.nodeSchema = this.$data.is3DNode ? NEX_CONFIG.nodeSchema.node3d : NEX_CONFIG + .nodeSchema.node2d; + 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++) { + for (let j = 0; j < schema.rows[i].length; j++) { + if (schema.rows[i][j].type === 'color') { + cc.js.getset(node[schema.key], schema.rows[i][j].field, () => { + return '#' + node.getComponent(schema.key)[schema.rows[i][j] + .rawField].toHEX('#rrggbb'); + }, (hex) => { + node.getComponent(schema.key)[schema.rows[i][j].rawField] = + 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.$data.componentsSchema = componentsSchema; + } else { + this.$data.node = null; + } + }, + outputNodeHandler(target) { + let i = 1; + while (window['temp' + i] !== undefined) { + i++; + } + window['temp' + i] = this.$data.node; + console.log('temp' + i); + console.log(window['temp' + i]); + }, + outputComponentHandler(data) { + let i = 1; + while (window['temp' + i] !== undefined) { + i++; + } + window['temp' + i] = this.$data.node.getComponent(data); + console.log('temp' + i); + console.log(window['temp' + i]); + }, + drawNodeRect(node) { + cc.where(this.$data.node); + }, + releaseCacheItem(id) { + console.log('resease item ', id); + cc.loader.release(id); + }, + showCacheItem(id) { + console.log(cc.loader._cache[id]); + }, + openGithub() { + window.open('https://github.com/potato47/ccc-devtools'); + }, + filterNode(value, node) { + if (!value) return true; + return node.name.toLowerCase().indexOf(value.toLowerCase()) !== -1; + }, + openCachePanel() { + console.log('open cache panel'); + this.$data.cacheDataLoading = true; + setTimeout(() => { + let rawCacheData = cc.loader._cache; + let cacheData = []; + for (let k in rawCacheData) { + let item = rawCacheData[k]; + // console.log(item) + if (item.type !== 'js' && item.type !== 'json') { + let itemName = '_'; + if (item.type === 'png' && item.type !== 'jpg') { + let texture = rawCacheData[k.replace('.png', '.json')]; + if (texture && texture._owner && texture._owner._name) { + itemName = texture._owner._name; + } + } + else { + if (item.content.name && item.content.name.length > 0) { + itemName = item.content.name; + } else if (item._owner) { + itemName = item._owner.name || '_'; + } + } + cacheData.push({ + queueId: item.queueId, + type: item.type, + name: itemName, + id: item.id, + content: item.content.__classname__ ? item.content.__classname__ : item.type, + size: Math.random() * 10000 | 0 + }); + } + } + this.$data.cacheData = cacheData; + this.$data.cacheTitle = `缓存 [文件总数:${cacheData.length}]`; + this.$data.cacheDataLoading = false; + }, 0); + + }, + closeCachePanel() { + console.log('close cache panel'); + this.$data.cacheData = []; + this.$data.cacheTitle = `缓存`; + }, + openDevMode() { + setTimeout(() => { + cc.js.getset(cc.Node.prototype, 'isLeaf', function () { + return this.childrenCount === 0; + }, function (value) { + + }, false, true); + let top = document.getElementById('top') + top.appendChild(document.getElementsByClassName('toolbar')[0]); + document.getElementById('game_panel').appendChild(document.getElementById('content')); + let scene = cc.director.getScene(); + if (scene) { + this.updateTreeData(); + } + cc.director.on(cc.Director.EVENT_AFTER_SCENE_LAUNCH, () => { + this.updateTreeData(); + }, this); + cc.director.on(cc.Director.EVENT_BEFORE_SCENE_LOADING, () => { + this.$data.node = null; + this.$data.sceneTreeData = []; + this.$data.treeParam = []; + }, this); + localStorage.setItem('isDevMode', 1); + }, 0); + }, + closeDevMode() { + this.$data.node = null; + this.$data.sceneTreeData = []; + cc.director.targetOff(this); + clearInterval(this.$data.intervalId); + document.body.appendChild(document.getElementsByClassName('toolbar')[0]); + document.body.appendChild(document.getElementById('content')); + localStorage.setItem('isDevMode', 0); + }, + handleChangeNodeSchema() { + if (this.is3DNode) { + this.is3DNode = false; + } else { + this.is3DNode = true; + } + this.$data.nodeSchema = this.$data.is3DNode ? NEX_CONFIG.nodeSchema.node3d : NEX_CONFIG.nodeSchema + .node2d; + }, + handleChangeMode(data) { + data ? this.openDevMode() : this.closeDevMode(); + }, + handleChangeCachePanel(data) { + data ? this.openCachePanel() : this.closeCachePanel(); + }, + handleChangeStats() { + let panels = document.getElementsByClassName('statsPanel'); + while (panels.length > 0) { + panels[0].parentElement.removeChild(panels[0]); + } + let newPanels = []; + let array = []; + this.$data.isShowFPS ? (array.push(0) && localStorage.setItem('isShowFPS', '1')) : localStorage + .setItem( + 'isShowFPS', '0'); + this.$data.isShowMS ? (array.push(1) && localStorage.setItem('isShowMS', '1')) : localStorage + .setItem('isShowMS', + '0'); + this.$data.isShowMB ? (array.push(2) && localStorage.setItem('isShowMB', '1')) : localStorage + .setItem('isShowMB', + '0'); + for (let i of array) { + let stats = new Stats(); + stats.showPanel(i); // 0: fps, 1: ms, 2: mb, 3+: custom + stats.dom.style.position = 'relative'; + stats.dom.style.float = 'right'; + stats.dom.style.marginLeft = '10px'; + stats.dom.style.marginBottom = '10px'; + stats.dom.style.pointerEvents = 'none'; + stats.dom.className = 'statsPanel'; + document.getElementById('panelCtl').appendChild(stats.dom); + newPanels.push(stats); + } + + function animate() { + for (let i = 0; i < newPanels.length; i++) { + let stats = newPanels[i]; + stats.update(); + } + requestAnimationFrame(animate); + } + animate(); + }, + handleChangeTheme(isDark) { + isDark ? this.addDarkTheme() : this.removeDarkTheme(); + }, + // 添加暗色主题 + addDarkTheme() { + let link = document.createElement('link'); + link.type = 'text/css'; + link.id = "theme-css-dark"; + link.rel = 'stylesheet'; + link.href = 'app/editor/static/preview-templates/ccc-devtools/css/themes/dark.css'; + document.getElementsByTagName("head")[0].appendChild(link); + localStorage.setItem('isDarkTheme', 1); + }, + // 删除暗色主题 + removeDarkTheme() { + document.getElementById('theme-css-dark').remove(); + localStorage.setItem('isDarkTheme', 0); + }, + fitFullScreen() { + document.getElementsByClassName('toolbar')[0].style.display = 'none'; + let gameDiv = document.getElementById('GameDiv'); + let gameContainer = document.getElementById('Cocos2dGameContainer'); + let gameCanvas = document.getElementById('GameCanvas'); + gameDiv.style.width = '100%'; + gameDiv.style.height = '100%'; + gameCanvas.style.width = '100%'; + gameCanvas.style.height = '100%'; + // document.body.style.cssText+="-webkit-transform: rotate(-90deg);-moz-transform: rotate(-90deg)"; + }, + initProfiler() { + let profiler = cc.find('PROFILER-NODE'); + if (profiler) { + cc.log(profiler); + } + }, + initConsoleUtil() { + 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.x.toFixed(0) + ',' + node.y.toFixed(0) + ',' + node.width.toFixed(0) + ',' + node.height.toFixed(0) + ',' + node.scale.toFixed(1)}` + let indexValue = `%c${index++}`; + if (node.childrenCount > 0) { + console.groupCollapsed(nameValue + propValue + indexValue, nameStyle, + propStyle, indexStyle); + for (let i = 0; i < node.childrenCount; 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.childrenCount > 0) { + for (let i = 0; i < node.childrenCount; 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.childrenCount > 0) { + for (let i = 0; i < node.childrenCount; 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 = target.getBoundingBoxToWorld(); + let borderNode = new cc.Node(); + let bgNode = new cc.Node(); + let graphics = bgNode.addComponent(cc.Graphics); + let canvas = cc.find('Canvas'); + canvas.addChild(bgNode); + bgNode.addChild(borderNode); + bgNode.position = canvas.convertToNodeSpaceAR(rect.center); + let isZeroSize = rect.width === 0 || rect.height === 0; + if (isZeroSize) { + graphics.circle(0, 0, 100); + graphics.fillColor = cc.Color.GREEN; + graphics.fill(); + } else { + bgNode.width = rect.width; + bgNode.height = rect.height; + graphics.rect(-bgNode.width / 2, -bgNode.height / 2, bgNode.width, bgNode.height); + graphics.strokeColor = cc.Color.RED; + graphics.lineWidth = 10; + graphics.stroke() + } + setTimeout(() => { + if (cc.isValid(bgNode)) { + bgNode.destroy(); + } + }, 2000); + return target; + } + } + }, + watch: { + filterText(val) { + this.$refs.sceneTree.filter(val); + // console.log(val); + } + }, + created: function () { + this.checkVersion(); + document.body.insertBefore(document.getElementById('app'), document.body.firstChild); + + let onCCInit = () => { + this.initProfiler(); + this.initConsoleUtil(); + if (cc.sys.isMobile) { + this.fitFullScreen(); + } + if (localStorage.getItem('isDevMode') === '1') { + this.$data.isDevMode = true; + this.openDevMode(); + } else { + this.$data.isDevMode = false; + } + if (localStorage.getItem('isDarkTheme') === '1') { + this.$data.isDarkTheme = true; + this.addDarkTheme(); + } else { + this.$data.isDarkTheme = false; + } + this.$data.isShowFPS = localStorage.getItem('isShowFPS') === '1'; + this.$data.isShowMS = localStorage.getItem('isShowMS') === '1'; + this.$data.isShowMB = localStorage.getItem('isShowMB') === '1'; + setTimeout(() => { + this.handleChangeStats(); + }, 0); + } + let checkCC = () => { + if (window.cc) { + onCCInit(); + clearInterval(this.$data.intervalId); + } else { + // console.log('cc is not init'); + } + } + this.$data.intervalId = setInterval(checkCC, 500); + setTimeout(checkCC, 0); + }, +}); \ No newline at end of file diff --git a/css/element-ui.css b/libs/element/css/element-ui.css similarity index 100% rename from css/element-ui.css rename to libs/element/css/element-ui.css diff --git a/css/fonts/element-icons.ttf b/libs/element/css/fonts/element-icons.ttf similarity index 100% rename from css/fonts/element-icons.ttf rename to libs/element/css/fonts/element-icons.ttf diff --git a/css/fonts/element-icons.woff b/libs/element/css/fonts/element-icons.woff similarity index 100% rename from css/fonts/element-icons.woff rename to libs/element/css/fonts/element-icons.woff diff --git a/js/element-ui.js b/libs/element/js/element-ui.js similarity index 100% rename from js/element-ui.js rename to libs/element/js/element-ui.js diff --git a/css/fonts/ionicons.ttf b/libs/iview/css/fonts/ionicons.ttf similarity index 100% rename from css/fonts/ionicons.ttf rename to libs/iview/css/fonts/ionicons.ttf diff --git a/css/fonts/ionicons.woff b/libs/iview/css/fonts/ionicons.woff similarity index 100% rename from css/fonts/ionicons.woff rename to libs/iview/css/fonts/ionicons.woff diff --git a/css/iview.css b/libs/iview/css/iview.css similarity index 100% rename from css/iview.css rename to libs/iview/css/iview.css diff --git a/js/iview.js b/libs/iview/js/iview.js similarity index 100% rename from js/iview.js rename to libs/iview/js/iview.js diff --git a/js/stats.min.js b/libs/stats/stats.min.js similarity index 100% rename from js/stats.min.js rename to libs/stats/stats.min.js diff --git a/css/vue-beauty.min.css b/libs/vue-beauty/css/vue-beauty.min.css similarity index 100% rename from css/vue-beauty.min.css rename to libs/vue-beauty/css/vue-beauty.min.css diff --git a/js/vue-beauty.min.js b/libs/vue-beauty/js/vue-beauty.min.js similarity index 100% rename from js/vue-beauty.min.js rename to libs/vue-beauty/js/vue-beauty.min.js diff --git a/js/vue.js b/libs/vue/vue.js similarity index 100% rename from js/vue.js rename to libs/vue/vue.js diff --git a/js/vue.min.js b/libs/vue/vue.min.js similarity index 100% rename from js/vue.min.js rename to libs/vue/vue.min.js diff --git a/version.json b/version.json index 708cebb..5677a61 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "name": "ccc-devtools", - "version": "2.1.2", + "version": "2.2.0", "author": "Next", "repo": "https://github.com/potato47/ccc-devtools.git" } \ No newline at end of file