export-view.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. /**
  2. * 导出动画弹出框
  3. */
  4. class ExportView {
  5. constructor() {
  6. this.canvas = null;
  7. this.ctx = null;
  8. this.previewPlaceholder = null;
  9. this.referenceInput = null;
  10. this.referencePreview = null;
  11. this.referenceUpload = null;
  12. this.cancelBtn = null;
  13. this.confirmBtn = null;
  14. this.referenceImage = null;
  15. this.spritesheetData = null;
  16. this.init();
  17. }
  18. init() {
  19. this.canvas = document.getElementById('spritesheetCanvas');
  20. this.ctx = this.canvas?.getContext('2d');
  21. this.previewPlaceholder = document.getElementById('previewPlaceholder');
  22. this.referenceInput = document.getElementById('referenceInput');
  23. this.referencePreview = document.getElementById('referencePreview');
  24. this.referenceUpload = document.getElementById('referenceUpload');
  25. this.cancelBtn = document.getElementById('exportCancelBtn');
  26. this.confirmBtn = document.getElementById('exportConfirmBtn');
  27. this.bindEvents();
  28. // 初始时禁用确定按钮
  29. if (this.confirmBtn) {
  30. this.confirmBtn.disabled = true;
  31. }
  32. }
  33. bindEvents() {
  34. // 取消按钮
  35. this.cancelBtn?.addEventListener('click', () => {
  36. this.close();
  37. });
  38. // 确定按钮
  39. this.confirmBtn?.addEventListener('click', () => {
  40. this.downloadSpritesheet();
  41. });
  42. // 参考图上传区点击
  43. this.referenceUpload?.addEventListener('click', () => {
  44. this.referenceInput?.click();
  45. });
  46. // 参考图选择
  47. this.referenceInput?.addEventListener('change', (e) => {
  48. const file = e.target.files[0];
  49. if (file) {
  50. this.loadReferenceImage(file);
  51. }
  52. });
  53. // 拖拽上传参考图
  54. this.referenceUpload?.addEventListener('dragover', (e) => {
  55. e.preventDefault();
  56. e.stopPropagation();
  57. this.referenceUpload.style.borderColor = '#667eea';
  58. });
  59. this.referenceUpload?.addEventListener('dragleave', (e) => {
  60. e.preventDefault();
  61. e.stopPropagation();
  62. this.referenceUpload.style.borderColor = '';
  63. });
  64. this.referenceUpload?.addEventListener('drop', (e) => {
  65. e.preventDefault();
  66. e.stopPropagation();
  67. this.referenceUpload.style.borderColor = '';
  68. const file = e.dataTransfer.files[0];
  69. if (file && file.type.startsWith('image/')) {
  70. this.loadReferenceImage(file);
  71. }
  72. });
  73. // 监听来自父窗口的消息
  74. window.addEventListener('message', (event) => {
  75. if (event.data && event.data.type === 'generate-spritesheet') {
  76. // console.log('[ExportView] 收到生成Spritesheet消息:', event.data);
  77. this.generateSpritesheet(event.data.folderName, event.data.frames);
  78. }
  79. });
  80. }
  81. loadReferenceImage(file) {
  82. const reader = new FileReader();
  83. reader.onload = (e) => {
  84. this.referenceImage = new Image();
  85. this.referenceImage.onload = () => {
  86. this.referencePreview.innerHTML = '';
  87. this.referencePreview.classList.add('has-image');
  88. this.referencePreview.appendChild(this.referenceImage);
  89. // console.log('[ExportView] 参考图已加载');
  90. };
  91. this.referenceImage.src = e.target.result;
  92. };
  93. reader.readAsDataURL(file);
  94. }
  95. async generateSpritesheet(folderName, frames) {
  96. // console.log('[ExportView] 开始生成Spritesheet');
  97. // console.log('[ExportView] 文件夹:', folderName);
  98. // console.log('[ExportView] 帧数:', frames?.length);
  99. if (!frames || frames.length === 0) {
  100. // console.error('[ExportView] 没有帧数据');
  101. return;
  102. }
  103. try {
  104. // 加载所有帧
  105. const images = await Promise.all(
  106. frames.map(frameUrl => {
  107. return new Promise((resolve, reject) => {
  108. const img = new Image();
  109. img.crossOrigin = 'anonymous';
  110. img.onload = () => resolve(img);
  111. img.onerror = reject;
  112. img.src = frameUrl;
  113. });
  114. })
  115. );
  116. // console.log('[ExportView] 所有帧已加载');
  117. // 计算spritesheet尺寸
  118. const frameWidth = images[0].width;
  119. const frameHeight = images[0].height;
  120. const cols = Math.ceil(Math.sqrt(images.length));
  121. const rows = Math.ceil(images.length / cols);
  122. this.canvas.width = frameWidth * cols;
  123. this.canvas.height = frameHeight * rows;
  124. // console.log('[ExportView] 画布尺寸:', this.canvas.width, 'x', this.canvas.height);
  125. // 绘制所有帧
  126. images.forEach((img, index) => {
  127. const col = index % cols;
  128. const row = Math.floor(index / cols);
  129. const x = col * frameWidth;
  130. const y = row * frameHeight;
  131. this.ctx.drawImage(img, x, y);
  132. });
  133. // 保存数据用于下载
  134. this.spritesheetData = {
  135. folderName: folderName,
  136. canvas: this.canvas
  137. };
  138. // 显示预览
  139. this.canvas.classList.add('show');
  140. this.previewPlaceholder.classList.add('hide');
  141. // 启用确定按钮
  142. if (this.confirmBtn) {
  143. this.confirmBtn.disabled = false;
  144. }
  145. // console.log('[ExportView] ✓ Spritesheet生成完成');
  146. } catch (error) {
  147. // console.error('[ExportView] 生成Spritesheet失败:', error);
  148. this.previewPlaceholder.textContent = '生成失败';
  149. }
  150. }
  151. downloadSpritesheet() {
  152. if (!this.spritesheetData) {
  153. // console.warn('[ExportView] 没有可下载的数据');
  154. return;
  155. }
  156. // console.log('[ExportView] 开始下载Spritesheet');
  157. this.canvas.toBlob((blob) => {
  158. const url = URL.createObjectURL(blob);
  159. const a = document.createElement('a');
  160. a.href = url;
  161. a.download = `${this.spritesheetData.folderName}_spritesheet.png`;
  162. a.click();
  163. URL.revokeObjectURL(url);
  164. // console.log('[ExportView] ✓ 下载已触发');
  165. // 下载后关闭弹出框
  166. this.close();
  167. }, 'image/png');
  168. }
  169. close() {
  170. // console.log('[ExportView] 关闭导出弹出框');
  171. // 通知父窗口关闭
  172. if (window.parent && window.parent !== window) {
  173. window.parent.postMessage({
  174. type: 'close-export-view'
  175. }, '*');
  176. }
  177. }
  178. }
  179. // 初始化
  180. window.ExportView = new ExportView();