Sidebar.Project.App.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. import * as THREE from 'three';
  2. import { zipSync, strToU8 } from 'three/addons/libs/fflate.module.js';
  3. import { UIButton, UICheckbox, UIPanel, UIInput, UIRow, UIText } from './libs/ui.js';
  4. function SidebarProjectApp( editor ) {
  5. const config = editor.config;
  6. const signals = editor.signals;
  7. const strings = editor.strings;
  8. const save = editor.utils.save;
  9. const container = new UIPanel();
  10. container.setId( 'app' );
  11. const headerRow = new UIRow();
  12. headerRow.add( new UIText( strings.getKey( 'sidebar/project/app' ).toUpperCase() ) );
  13. container.add( headerRow );
  14. // Title
  15. const titleRow = new UIRow();
  16. const title = new UIInput( config.getKey( 'project/title' ) ).setLeft( '100px' ).setWidth( '150px' ).onChange( function () {
  17. config.setKey( 'project/title', this.getValue() );
  18. } );
  19. titleRow.add( new UIText( strings.getKey( 'sidebar/project/app/title' ) ).setClass( 'Label' ) );
  20. titleRow.add( title );
  21. container.add( titleRow );
  22. // Editable
  23. const editableRow = new UIRow();
  24. const editable = new UICheckbox( config.getKey( 'project/editable' ) ).setLeft( '100px' ).onChange( function () {
  25. config.setKey( 'project/editable', this.getValue() );
  26. } );
  27. editableRow.add( new UIText( strings.getKey( 'sidebar/project/app/editable' ) ).setClass( 'Label' ) );
  28. editableRow.add( editable );
  29. container.add( editableRow );
  30. // Play/Stop
  31. let isPlaying = false;
  32. const playButton = new UIButton( strings.getKey( 'sidebar/project/app/play' ) );
  33. playButton.setWidth( '170px' );
  34. playButton.setMarginLeft( '120px' );
  35. playButton.setMarginBottom( '10px' );
  36. playButton.onClick( function () {
  37. if ( isPlaying === false ) {
  38. isPlaying = true;
  39. playButton.setTextContent( strings.getKey( 'sidebar/project/app/stop' ) );
  40. signals.startPlayer.dispatch();
  41. } else {
  42. isPlaying = false;
  43. playButton.setTextContent( strings.getKey( 'sidebar/project/app/play' ) );
  44. signals.stopPlayer.dispatch();
  45. }
  46. } );
  47. container.add( playButton );
  48. // Publish
  49. const publishButton = new UIButton( strings.getKey( 'sidebar/project/app/publish' ) );
  50. publishButton.setWidth( '170px' );
  51. publishButton.setMarginLeft( '120px' );
  52. publishButton.setMarginBottom( '10px' );
  53. publishButton.onClick( function () {
  54. const toZip = {};
  55. const rendererType = config.getKey( 'project/renderer/type' );
  56. //
  57. let output = editor.toJSON();
  58. output.metadata.type = 'App';
  59. delete output.history;
  60. output = JSON.stringify( output, null, '\t' );
  61. output = output.replace( /[\n\t]+([\d\.e\-\[\]]+)/g, '$1' );
  62. toZip[ 'app.json' ] = strToU8( output );
  63. //
  64. const title = config.getKey( 'project/title' );
  65. const manager = new THREE.LoadingManager( function () {
  66. const zipped = zipSync( toZip, { level: 9 } );
  67. const blob = new Blob( [ zipped.buffer ], { type: 'application/zip' } );
  68. save( blob, ( title !== '' ? title : 'untitled' ) + '.zip' );
  69. } );
  70. const loader = new THREE.FileLoader( manager );
  71. loader.load( 'js/libs/app/index.html', function ( content ) {
  72. content = content.replace( '<!-- title -->', title );
  73. //
  74. const IMPORTMAP = {
  75. WebGLRenderer: {
  76. imports: {
  77. 'three': './js/three.module.js'
  78. }
  79. },
  80. WebGPURenderer: {
  81. imports: {
  82. 'three': './js/three.webgpu.js',
  83. 'three/webgpu': './js/three.webgpu.js'
  84. }
  85. }
  86. };
  87. const importmap = JSON.stringify( IMPORTMAP[ rendererType ], null, '\t' );
  88. content = content.replace( '<!-- importmap -->', indent( '\n' + indent( importmap, 1 ) + '\n', 2 ) );
  89. //
  90. let editButton = '';
  91. if ( config.getKey( 'project/editable' ) ) {
  92. editButton = [
  93. ' let button = document.createElement( \'a\' );',
  94. ' button.href = \'https://threejs.org/editor/#file=\' + location.href.split( \'/\' ).slice( 0, - 1 ).join( \'/\' ) + \'/app.json\';',
  95. ' button.style.cssText = \'position: absolute; bottom: 20px; right: 20px; padding: 10px 16px; color: #fff; border: 1px solid #fff; border-radius: 20px; text-decoration: none;\';',
  96. ' button.target = \'_blank\';',
  97. ' button.textContent = \'EDIT\';',
  98. ' document.body.appendChild( button );',
  99. ].join( '\n' );
  100. }
  101. content = content.replace( '\t\t\t/* edit button */', editButton );
  102. toZip[ 'index.html' ] = strToU8( content );
  103. } );
  104. loader.load( 'js/libs/app.js', function ( content ) {
  105. toZip[ 'js/app.js' ] = strToU8( content );
  106. } );
  107. loader.load( '../build/three.core.js', function ( content ) {
  108. toZip[ 'js/three.core.js' ] = strToU8( content );
  109. } );
  110. if ( rendererType === 'WebGPURenderer' ) {
  111. loader.load( '../build/three.webgpu.js', function ( content ) {
  112. toZip[ 'js/three.webgpu.js' ] = strToU8( content );
  113. } );
  114. } else {
  115. loader.load( '../build/three.module.js', function ( content ) {
  116. toZip[ 'js/three.module.js' ] = strToU8( content );
  117. } );
  118. }
  119. } );
  120. container.add( publishButton );
  121. // Signals
  122. signals.editorCleared.add( function () {
  123. title.setValue( '' );
  124. config.setKey( 'project/title', '' );
  125. } );
  126. return container;
  127. }
  128. //
  129. function indent( text, count, space = '\t' ) {
  130. return text
  131. .split( '\n' )
  132. .map( line => space.repeat( count ) + line )
  133. .join( '\n' );
  134. }
  135. //
  136. export { SidebarProjectApp };
粤ICP备19079148号