threepipe
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

DropzonePlugin.ts 4.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import {AViewerPluginSync} from '../../viewer/AViewerPlugin'
  2. import {type ThreeViewer} from '../../viewer/'
  3. import {Dropzone} from '../../utils'
  4. import {uiButton, uiConfig, uiFolderContainer, UiObjectConfig, uiToggle} from 'uiconfig.js'
  5. import type {AddAssetOptions, ImportFilesOptions, ImportResult} from '../../assetmanager'
  6. import {serialize} from 'ts-browser-helpers'
  7. export interface DropzonePluginOptions {
  8. domElement?: HTMLElement
  9. allowedExtensions?: string[]
  10. autoImport?: boolean
  11. autoAdd?: boolean
  12. importOptions?: ImportFilesOptions
  13. addOptions?: AddAssetOptions
  14. }
  15. @uiFolderContainer('Dropzone')
  16. export class DropzonePlugin extends AViewerPluginSync<'drop'> {
  17. static readonly PluginType = 'Dropzone'
  18. uiConfig!: UiObjectConfig
  19. @uiToggle() @serialize() enabled = true
  20. private _inputEl?: HTMLInputElement
  21. private _dropzone?: Dropzone
  22. private _allowedExtensions: string[]|undefined = undefined // undefined and empty array is different.
  23. /**
  24. * Automatically import assets when dropped.
  25. */
  26. @serialize() autoImport = true
  27. /**
  28. * Automatically add dropped and imported assets to the scene.
  29. Works only if {@link autoImport} is true.
  30. */
  31. @uiToggle() @serialize() autoAdd = true
  32. /**
  33. * Import options for the {@link AssetImporter.importFiles}
  34. */
  35. @uiConfig() @serialize() importOptions: ImportFilesOptions = {
  36. autoImportZipContents: true,
  37. forceImporterReprocess: false,
  38. }
  39. /**
  40. * Add options for the {@link RootScene.addObject}
  41. */
  42. @uiConfig() @serialize() addOptions: AddAssetOptions = {
  43. autoCenter: true,
  44. importConfig: true,
  45. autoScale: true,
  46. autoScaleRadius: 2,
  47. license: '',
  48. clearSceneObjects: false,
  49. disposeSceneObjects: false,
  50. autoSetBackground: false,
  51. autoSetEnvironment: true,
  52. }
  53. /**
  54. * Allowed file extensions. If undefined, all files are allowed.
  55. */
  56. get allowedExtensions(): string[] | undefined {
  57. return this._allowedExtensions
  58. }
  59. set allowedExtensions(value: string[] | undefined) {
  60. this._allowedExtensions = value
  61. if (this._inputEl) this._inputEl.accept = value ? value.map(v=>'.' + v).join(', ') : ''
  62. }
  63. /**
  64. * Prompt for file selection using the browser file dialog.
  65. */
  66. @uiButton('Select files')
  67. public promptForFile(): void {
  68. if (!this.enabled) return
  69. this.allowedExtensions = this._allowedExtensions
  70. this._inputEl?.click()
  71. }
  72. private _domElement?: HTMLElement
  73. constructor(options?: DropzonePluginOptions) {
  74. super()
  75. if (!options) return
  76. this._domElement = options.domElement
  77. this.allowedExtensions = options.allowedExtensions
  78. this.autoImport = options.autoImport ?? this.autoImport
  79. this.autoAdd = options.autoAdd ?? this.autoAdd
  80. this.importOptions = {...this.importOptions, ...options.importOptions}
  81. this.addOptions = {...this.addOptions, ...options.addOptions}
  82. }
  83. onAdded(viewer: ThreeViewer) {
  84. super.onAdded(viewer)
  85. this._inputEl = document.createElement('input')!
  86. this._inputEl.type = 'file'
  87. if (!this._domElement) this._domElement = viewer.canvas
  88. this._dropzone = new Dropzone(this._domElement, this._inputEl, {
  89. drop: this._onFileDrop.bind(this),
  90. })
  91. this.allowedExtensions = this._allowedExtensions
  92. }
  93. onRemove(viewer: ThreeViewer) {
  94. super.onRemove(viewer)
  95. this._dropzone?.destroy()
  96. this._dropzone = undefined
  97. this._inputEl = undefined
  98. }
  99. private async _onFileDrop({files, nativeEvent}: {files: Map<string, File>, nativeEvent: DragEvent}) {
  100. if (!files) return
  101. if (!this.enabled) return
  102. const viewer = this._viewer
  103. if (!viewer) return
  104. if (this._allowedExtensions !== undefined) {
  105. for (const file of files.keys()) {
  106. if (!this._allowedExtensions.includes(file.split('.').pop()?.toLowerCase() ?? '')) {
  107. files.delete(file)
  108. }
  109. }
  110. }
  111. if (files.size < 1) return
  112. const manager = viewer.assetManager
  113. let imported: Map<string, (ImportResult | undefined)[]>|undefined
  114. let assets: (ImportResult | undefined)[]|undefined
  115. if (this.autoImport) {
  116. imported = await manager.importer.importFiles(files, {
  117. allowedExtensions: this.allowedExtensions, ...this.importOptions,
  118. })
  119. if (this.autoAdd) {
  120. const toAdd = [...imported?.values() ?? []].flat(2).filter(v=>!!v) ?? []
  121. assets = await manager.loadImported(toAdd, {...this.addOptions})
  122. }
  123. }
  124. this.dispatchEvent({type: 'drop', files, imported, assets, nativeEvent})
  125. }
  126. }