Add a 3D model viewer
This commit is contained in:
parent
43e0d4a856
commit
a36b4e0631
@ -175,6 +175,9 @@ func (i *FileInfo) detectType(modify, saveContent bool) error {
|
||||
case strings.HasPrefix(mimetype, "image"):
|
||||
i.Type = "image"
|
||||
return nil
|
||||
case strings.HasPrefix(mimetype, "application/object"):
|
||||
i.Type = "3dobject"
|
||||
return nil
|
||||
case isBinary(buffer[:n], n) || i.Size > 10*1024*1024: // 10 MB
|
||||
i.Type = "blob"
|
||||
return nil
|
||||
|
||||
13
frontend/package-lock.json
generated
13
frontend/package-lock.json
generated
@ -12399,6 +12399,11 @@
|
||||
"neo-async": "^2.6.0"
|
||||
}
|
||||
},
|
||||
"three": {
|
||||
"version": "0.118.3",
|
||||
"resolved": "https://registry.npmjs.org/three/-/three-0.118.3.tgz",
|
||||
"integrity": "sha512-ijECXrNzDkHieoeh2H69kgawTGH8DiamhR4uBN8jEM7VHSKvfTdEvOoHsA8Aq7dh7PHAxhlqBsN5arBI3KixSw=="
|
||||
},
|
||||
"through": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||
@ -12991,6 +12996,14 @@
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.10.tgz",
|
||||
"integrity": "sha512-ImThpeNU9HbdZL3utgMCq0oiMzAkt1mcgy3/E6zWC/G6AaQoeuFdsl9nDhTDU3X1R6FK7nsIUuRACVcjI+A2GQ=="
|
||||
},
|
||||
"vue-3d-model": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-3d-model/-/vue-3d-model-1.3.1.tgz",
|
||||
"integrity": "sha512-md+ZgtcSa0NcwhEWYG/uWgmtQjkAaUb+9jaHmZYdsnaPEBlT9IW3ptVQJRiA7VOGvobXaHy72ASCFaQJf08/rA==",
|
||||
"requires": {
|
||||
"three": "^0.118.3"
|
||||
}
|
||||
},
|
||||
"vue-eslint-parser": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.0.0.tgz",
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
"noty": "^3.2.0-beta",
|
||||
"qrcode.vue": "^1.7.0",
|
||||
"vue": "^2.6.10",
|
||||
"vue-3d-model": "^1.3.1",
|
||||
"vue-i18n": "^8.15.3",
|
||||
"vue-lazyload": "^1.3.3",
|
||||
"vue-router": "^3.1.3",
|
||||
|
||||
@ -38,6 +38,7 @@
|
||||
<template v-if="!loading">
|
||||
<div class="preview">
|
||||
<ExtendedImage v-if="req.type == 'image'" :src="raw"></ExtendedImage>
|
||||
<ThreeViewer v-else-if="req.type == '3dobject'" :src="raw"></ThreeViewer>
|
||||
<audio v-else-if="req.type == 'audio'" :src="raw" autoplay controls></audio>
|
||||
<video v-else-if="req.type == 'video'" :src="raw" autoplay controls>
|
||||
<track
|
||||
@ -72,8 +73,10 @@ import DeleteButton from '@/components/buttons/Delete'
|
||||
import RenameButton from '@/components/buttons/Rename'
|
||||
import DownloadButton from '@/components/buttons/Download'
|
||||
import ExtendedImage from './ExtendedImage'
|
||||
import ThreeViewer from './ThreeViewer'
|
||||
|
||||
const mediaTypes = [
|
||||
"3dobject",
|
||||
"image",
|
||||
"video",
|
||||
"audio",
|
||||
@ -88,7 +91,8 @@ export default {
|
||||
DeleteButton,
|
||||
RenameButton,
|
||||
DownloadButton,
|
||||
ExtendedImage
|
||||
ExtendedImage,
|
||||
ThreeViewer
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
|
||||
75
frontend/src/components/files/ThreeViewer.vue
Normal file
75
frontend/src/components/files/ThreeViewer.vue
Normal file
@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<div class="3d-preview">
|
||||
<div class="loading" v-if="loadingPreview">
|
||||
<div class="spinner">
|
||||
<div class="bounce1"></div>
|
||||
<div class="bounce2"></div>
|
||||
<div class="bounce3"></div>
|
||||
</div>
|
||||
</div>
|
||||
<model-obj v-if="req.extension.match(/\.obj$/i)" :src="raw" :backgroundAlpha="0" :rotation="rotation" @on-mousedown="onMousedown" @on-load="onLoad" @on-error="onError"></model-obj>
|
||||
<model-stl v-else-if="req.extension.match(/\.stl$/i)" :src="raw" :backgroundAlpha="0" :rotation="rotation" @on-mousedown="onMousedown" @on-load="onLoad" @on-error="onError"></model-stl>
|
||||
<model-ply v-else-if="req.extension.match(/\.ply$/i)" :src="raw" :backgroundAlpha="0" :rotation="rotation" @on-mousedown="onMousedown" @on-load="onLoad" @on-error="onError"></model-ply>
|
||||
<model-fbx v-else-if="req.extension.match(/\.fbx$/i)" :src="raw" :backgroundAlpha="0" :rotation="rotation" @on-mousedown="onMousedown" @on-load="onLoad" @on-error="onError"></model-fbx>
|
||||
<model-gltf v-else-if="req.extension.match(/\.gltf$/i)" :src="raw" :backgroundAlpha="0" :rotation="rotation" @on-mousedown="onMousedown" @on-load="onLoad" @on-error="onError"></model-gltf>
|
||||
<model-collada v-else-if="req.extension.match(/\.dae$/i)" :src="raw" :backgroundAlpha="0" :rotation="rotation" @on-mousedown="onMousedown" @on-load="onLoad" @on-error="onError"></model-collada>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
import { ModelCollada, ModelFbx, ModelGltf, ModelObj, ModelPly, ModelStl } from 'vue-3d-model';
|
||||
import { mapState } from 'vuex'
|
||||
import url from '@/utils/url'
|
||||
import { baseURL } from '@/utils/constants'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ModelCollada,
|
||||
ModelFbx,
|
||||
ModelGltf,
|
||||
ModelObj,
|
||||
ModelPly,
|
||||
ModelStl
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loadingPreview: false,
|
||||
rotating: false,
|
||||
rotation: {
|
||||
x: -Math.PI / 2,
|
||||
y: 0,
|
||||
z: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['req', 'jwt']),
|
||||
downloadUrl () {
|
||||
return `${baseURL}/api/raw${url.encodePath(this.req.path)}?auth=${this.jwt}`
|
||||
},
|
||||
raw () {
|
||||
return `${this.downloadUrl}&inline=true`
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.loadingPreview = true
|
||||
this.rotating = true
|
||||
},
|
||||
methods: {
|
||||
onLoad () {
|
||||
this.loadingPreview = false
|
||||
this.rotate();
|
||||
},
|
||||
onError () {
|
||||
this.loadingPreview = false
|
||||
},
|
||||
onMousedown () {
|
||||
this.rotating = false
|
||||
},
|
||||
rotate () {
|
||||
this.rotation.z += 0.01;
|
||||
if (this.rotating) requestAnimationFrame( this.rotate );
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Loading…
Reference in New Issue
Block a user