Add a 3D model viewer

This commit is contained in:
Vincent Lark 2021-01-04 18:06:15 +01:00
parent 43e0d4a856
commit a36b4e0631
5 changed files with 97 additions and 1 deletions

View File

@ -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

View File

@ -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",

View File

@ -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",

View File

@ -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 {

View 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>