From b0a138dd3c99ee9f73ed233f7b617dfb4cd8c10b Mon Sep 17 00:00:00 2001 From: Knox Date: Mon, 13 Jan 2025 18:24:48 +0800 Subject: [PATCH] optimize prefab and spine view logic, add reset camera view (#18183) --- editor/i18n/en/assets.js | 1 + editor/i18n/zh/assets.js | 1 + editor/inspector/assets/prefab-preview.js | 182 ++-------------------- editor/inspector/assets/spine-preview.js | 27 +--- editor/inspector/utils/preview.js | 107 +++++++++++-- 5 files changed, 112 insertions(+), 206 deletions(-) diff --git a/editor/i18n/en/assets.js b/editor/i18n/en/assets.js index d1a1784633a..e439e3879b6 100644 --- a/editor/i18n/en/assets.js +++ b/editor/i18n/en/assets.js @@ -10,6 +10,7 @@ module.exports = { cloneToDirectoryIllegal: 'Please limit the saved path to the current project assets path', preview: { header: 'Preview', + resetCameraView: 'Reset camera view', }, spine: { skin: 'Skin', diff --git a/editor/i18n/zh/assets.js b/editor/i18n/zh/assets.js index b496908c557..b070b4dce6d 100644 --- a/editor/i18n/zh/assets.js +++ b/editor/i18n/zh/assets.js @@ -10,6 +10,7 @@ module.exports = { cloneToDirectoryIllegal: '保存路径请限制在当前项目 /assets 路径内', preview: { header: '预览', + resetCameraView: '复位摄像机视图', }, spine: { skin: '皮肤', diff --git a/editor/inspector/assets/prefab-preview.js b/editor/inspector/assets/prefab-preview.js index 369ba92aff2..6e0ab7e8f23 100644 --- a/editor/inspector/assets/prefab-preview.js +++ b/editor/inspector/assets/prefab-preview.js @@ -1,12 +1,10 @@ 'use strict'; +const { PreviewControl, hideElement } = require("../utils/preview"); + exports.template = /* html */` -
-
- -
-
+
`; @@ -15,164 +13,30 @@ exports.style = /* css */` margin-top: 0px; } .preview { } - .preview > .image { - height: var(--inspector-footer-preview-height, 200px); - overflow: hidden; - display: flex; - flex: 1; - } - .preview >.image > .canvas { - flex: 1; - } `; - exports.$ = { container: '.preview', - canvas: '.canvas', - image: '.image', }; -async function callFunction(funcName, ...args) { - return await Editor.Message.request('scene', 'call-preview-function', 'scene:prefab-preview', funcName, ...args); -} - const Elements = { preview: { - ready() { - const panel = this; - - let _isPreviewDataDirty = false; - Object.defineProperty(panel, 'isPreviewDataDirty', { - get() { - return _isPreviewDataDirty; - }, - set(value) { - if (value !== _isPreviewDataDirty) { - _isPreviewDataDirty = value; - value && panel.refreshPreview(); - } - }, - }); - panel.$.canvas.addEventListener('mousedown', async (event) => { - await callFunction('onMouseDown', { x: event.x, y: event.y, button: event.button }); - - async function mousemove(event) { - await callFunction('onMouseMove', { - movementX: event.movementX, - movementY: event.movementY, - }); - - panel.isPreviewDataDirty = true; - } - - async function mouseup(event) { - await callFunction('onMouseUp', { - x: event.x, - y: event.y, - }); - - document.removeEventListener('mousemove', mousemove); - document.removeEventListener('mouseup', mouseup); - - panel.isPreviewDataDirty = false; - } - - document.addEventListener('mousemove', mousemove); - document.addEventListener('mouseup', mouseup); - - - panel.isPreviewDataDirty = true; - }); - - panel.$.canvas.addEventListener('wheel', async (event) => { - await callFunction('onMouseWheel', { - wheelDeltaY: event.wheelDeltaY, - wheelDeltaX: event.wheelDeltaX, - }); - panel.isPreviewDataDirty = true; - }); - - - const GlPreview = Editor._Module.require('PreviewExtends').default; - panel.glPreview = new GlPreview('scene:prefab-preview', 'query-prefab-preview-data'); - - function observer() { - panel.isPreviewDataDirty = true; - } - - panel.resizeObserver = new window.ResizeObserver(observer); - panel.resizeObserver.observe(panel.$.image); - observer(); + ready(panel) { + panel.preview.init(); }, - async update() { - const panel = this; - - if (!panel.$.canvas) { - return; - } - - await panel.glPreview.init({ width: panel.$.canvas.clientWidth, height: panel.$.canvas.clientHeight }); - await callFunction('setPrefab', panel.asset.uuid); - this.isPreviewDataDirty = true; - }, - close() { - const panel = this; - - panel.resizeObserver.unobserve(panel.$.image); + async update(panel) { + await panel.preview.callPreviewFunction('setPrefab', panel.asset.uuid); }, + close(panel) {}, }, }; -exports.methods = { - async refreshPreview() { - const panel = this; - - // After await, the panel no longer exists - if (!panel.$.canvas) { - return; - } - - const doDraw = async () => { - try { - const canvas = panel.$.canvas; - const image = panel.$.image; - - const width = image.clientWidth; - const height = image.clientHeight; - if (canvas.width !== width || canvas.height !== height) { - canvas.width = width; - canvas.height = height; - - await panel.glPreview.initGL(canvas, { width, height }); - await panel.glPreview.resizeGL(width, height); - } - - const info = await panel.glPreview.queryPreviewData({ - width: canvas.width, - height: canvas.height, - }); - - panel.glPreview.drawGL(info); - } catch (e) { - console.warn(e); - } - }; - - requestAnimationFrame(async () => { - await doDraw(); - panel.isPreviewDataDirty = false; - }); - }, -}; +exports.methods = {}; exports.ready = function() { - for (const prop in Elements) { - const element = Elements[prop]; - if (element.ready) { - element.ready.call(this); - } - } + this.preview = new PreviewControl('scene:prefab-preview', 'query-prefab-preview-data', this.$.container); + + Object.values(Elements).forEach((element) => element.ready && element.ready(this)); }; exports.update = function(assetList, metaList) { @@ -182,25 +46,13 @@ exports.update = function(assetList, metaList) { this.meta = metaList[0]; // 如何多选就隐藏预览 - if (assetList.length > 1) { - this.$.container.style.display = 'none'; - } else { - this.$.container.style.display = 'block'; - } + hideElement(this.$.container, assetList.length > 1); - for (const prop in Elements) { - const element = Elements[prop]; - if (element.update) { - element.update.call(this); - } - } + Object.values(Elements).forEach((element) => element.update && element.update(this)); }; exports.close = function() { - for (const prop in Elements) { - const element = Elements[prop]; - if (element.close) { - element.close.call(this); - } - } + this.preview.close(); + + Object.values(Elements).forEach((element) => element.close && element.close(this)); }; diff --git a/editor/inspector/assets/spine-preview.js b/editor/inspector/assets/spine-preview.js index bee8da6a8b8..ba6887696ee 100644 --- a/editor/inspector/assets/spine-preview.js +++ b/editor/inspector/assets/spine-preview.js @@ -77,9 +77,6 @@ exports.template = /* html */` -
- -
`; @@ -126,18 +123,6 @@ exports.style = /* css */` } } } - & > .image { - height: var(--inspector-footer-preview-height, 200px); - overflow: hidden; - display: flex; - flex: 1; - position: relative; - - & > .canvas { - position: absolute; - inset: 0; - } - } } } `; @@ -153,8 +138,6 @@ const Properties = [...Config.CHECKBOX, ...Config.SLIDER, ...Config.OTHER, ...Co exports.$ = { container: '.preview', - canvas: '.canvas', - image: '.image', ...Object.fromEntries(Properties.map((key) => [key, `.${key}`])), @@ -167,11 +150,8 @@ const Elements = { panel.preview.init(); }, async update(panel) { - if (!panel.$.canvas) { return; } - const spineData = await panel.preview.callPreviewFunction('setSpine', panel.asset.uuid); panel.spinUpdate(panel, spineData); - panel.preview.doRefreshDirty(); }, close(panel) { panel.preview.callPreviewFunction('stop'); @@ -228,7 +208,6 @@ const Elements = { Elements.spine.updateDuration(panel, 0, Elements.spine.getDurations(panel, info)); Elements.control.update(panel, false); Elements.control.updateInfo(panel, info); - panel.preview.doRefreshDirty(); }, updateDuration(panel, time, duration) { panel.$.duration.setConfig({ @@ -249,9 +228,7 @@ const Elements = { Config.CHECKBOX.forEach((key) => { panel.$[key].addEventListener('confirm', (event) => { - panel.preview.callPreviewFunction('setProperties', key, Boolean(event.target.value)).then((() => { - panel.preview.doRefreshDirty(); - })); + panel.preview.callPreviewFunction('setProperties', key, Boolean(event.target.value)); }); }); @@ -299,7 +276,7 @@ exports.ready = function() { Editor.Message.__protected__.addBroadcastListener('scene:spine-preview-animation-time-change', this.onAnimationUpdateBind); - this.preview = new PreviewControl('scene:spine-preview', 'query-spine-preview-data', this.$.canvas, this.$.image); + this.preview = new PreviewControl('scene:spine-preview', 'query-spine-preview-data', this.$.container); Object.values(Elements).forEach((element) => element.ready && element.ready(this)); }; diff --git a/editor/inspector/utils/preview.js b/editor/inspector/utils/preview.js index bd117c23771..82ffa6bc19a 100644 --- a/editor/inspector/utils/preview.js +++ b/editor/inspector/utils/preview.js @@ -24,8 +24,13 @@ class PreviewControl { _glPreview = null; // html element - _canvasElement = null; - _imageElement = null; + _container = null; + + _image = null; + _canvas = null; + + _toolbar = null; + _resetCamera = null; _resizeObserver = null; @@ -33,28 +38,89 @@ class PreviewControl { _onMouseDownBind = this._onMouseDown.bind(this); _onMouseWheelBind = this._onMouseWheel.bind(this); - constructor(name, method, canvas, image) { - this._canvasElement = canvas; - this._imageElement = image; + _onResetCameraBind = this._onResetCamera.bind(this); + + enabledResetCamera = true; + + /** + * create + * image + * - toolbar + * - button:reset-camera + * - canvas + * @param container - preview root + * @private + */ + _createElements(container) { + this._container = container; + + this._image = document.createElement('div'); + this._image.classList.add('image'); + this._image.style = ` + height: var(--inspector-footer-preview-height, 200px); + overflow: hidden; + display: flex; + flex: 1; + `; + this._container.appendChild(this._image); + + this._toolbar = document.createElement('div'); + this._toolbar.classList.add('toolbar'); + this._toolbar.style = ` + display: flex; + `; + this._image.appendChild(this._toolbar); + + if (this.enabledResetCamera) { + this._resetCamera = document.createElement('ui-button'); + this._resetCamera.classList.add('reset-camera'); + this._resetCamera.setAttribute('type', 'icon'); + this._resetCamera.setAttribute('tooltip', 'i18n:ENGINE.inspector.preview.resetCameraView'); + this._resetCamera.style = ` + position: absolute; + right: 10px; + bottom: 10px; + `; + this._resetCamera.innerHTML = ``; + this._toolbar.appendChild(this._resetCamera); + } + + this._canvas = document.createElement('canvas'); + this._canvas.style = ` + flex: 1; + `; + this._canvas.classList.add('canvas'); + this._image.appendChild(this._canvas); + } + + constructor(name, method, container) { this._gLPreviewConfig = { name: name, method: method, }; + this._createElements(container); } async callPreviewFunction(funcName, ...args) { - return await Editor.Message.request('scene', 'call-preview-function', this._gLPreviewConfig.name, funcName, ...args); + try { + const result = await Editor.Message.request('scene', 'call-preview-function', this._gLPreviewConfig.name, funcName, ...args); + this.doRefreshDirty(); + return result; + } catch (e) { + console.error(e); + return null; + } } async init() { const GLPreview = Editor._Module.require('PreviewExtends').default; this._glPreview = new GLPreview(this._gLPreviewConfig.name, this._gLPreviewConfig.method); await this._glPreview.init({ - width: this._canvasElement.clientWidth, - height: this._canvasElement.clientHeight, + width: this._canvas.clientWidth, + height: this._canvas.clientHeight, }); this._resizeObserver = new window.ResizeObserver(this._observerBind); - this._resizeObserver.observe(this._imageElement); + this._resizeObserver.observe(this._image); this._registerEventListener(); await this._refresh(); @@ -66,19 +132,21 @@ class PreviewControl { } close() { - this._resizeObserver && this._resizeObserver.unobserve(this._imageElement); + this._resizeObserver && this._resizeObserver.unobserve(this._image); cancelAnimationFrame(this._animationId); this._unregisterEventListener(); } _registerEventListener() { - this._canvasElement.addEventListener('mousedown', this._onMouseDownBind); - this._canvasElement.addEventListener('wheel', this._onMouseWheelBind); + this._resetCamera.addEventListener('click', this._onResetCameraBind); + this._canvas.addEventListener('mousedown', this._onMouseDownBind); + this._canvas.addEventListener('wheel', this._onMouseWheelBind); } _unregisterEventListener() { - this._canvasElement.removeEventListener('mousedown', this._onMouseDownBind); - this._canvasElement.removeEventListener('wheel', this._onMouseWheelBind); + this._resetCamera.removeEventListener('click', this._onResetCameraBind); + this._canvas.removeEventListener('mousedown', this._onMouseDownBind); + this._canvas.removeEventListener('wheel', this._onMouseWheelBind); } _observer() { @@ -89,8 +157,8 @@ class PreviewControl { if (this._isDirty) { try { this._isDirty = false; - const canvas = this._canvasElement; - const image = this._imageElement; + const canvas = this._canvas; + const image = this._image; const width = image.clientWidth; const height = image.clientHeight; @@ -163,6 +231,13 @@ class PreviewControl { this._isDirty = true; } + + _onResetCamera() { + this.callPreviewFunction('resetCameraView') + .then(() => { + this.doRefreshDirty(); + }); + } } module.exports = {