update bi改动添加组件
parent
e066d17483
commit
5c6a78a3bd
@ -0,0 +1,5 @@
|
||||
:root,
|
||||
html,
|
||||
html.dark {
|
||||
@include varELColor($el-colors, -$w-step);
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
:root,html {
|
||||
@include varELColor($el-colors, $w-step);
|
||||
}
|
||||
@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="WColorHsl" :style="{ '--h': h, '--s': s + '%', '--l': l + '%' }">
|
||||
<w-color-loop v-model="h1" />
|
||||
<w-color-loop v-model="s1" />
|
||||
<w-color-loop v-model="l1" />
|
||||
<div style="width: 3em;
|
||||
height: 3em ;
|
||||
border: solid .15em #FFF; border-radius: 3em;
|
||||
box-shadow: 0 0 .5em #0009 inset, 0 0 .5em #0009;
|
||||
background-color: hsl(var(--h),var(--s),var(--l));
|
||||
">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div style="width: 2rem; height: 2rem;" :style="{ 'background-color': `hsl(${deg},100%,50%)` }"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
const h = defineModel('h', { type: Number, default: 0 });
|
||||
const s = defineModel('s', { type: Number, default: 100 });
|
||||
const l = defineModel('l', { type: Number, default: 50 });
|
||||
const h1 = computed({
|
||||
get() {
|
||||
return h.value;
|
||||
},
|
||||
set(val) {
|
||||
h.value = Math.round(val);
|
||||
}
|
||||
});
|
||||
const s1 = computed({
|
||||
get() {
|
||||
return s.value * 3.6;
|
||||
},
|
||||
set(val) {
|
||||
s.value = Math.round(val / 3.6);
|
||||
}
|
||||
});
|
||||
const l1 = computed({
|
||||
get() {
|
||||
return l.value * 3.6;
|
||||
},
|
||||
set(val) {
|
||||
l.value = Math.round(val / 3.6);
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.WColorHsl {
|
||||
width: 15em;
|
||||
height: 15em;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
|
||||
&>div {
|
||||
position: absolute;
|
||||
|
||||
&:nth-child(1) {
|
||||
width: 15em;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
width: 11em;
|
||||
--colors: conic-gradient(hsl(var(--h), 0%, 50%),
|
||||
hsl(var(--h), 100%, 50%));
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
width: 7em;
|
||||
--colors: conic-gradient(hsl(var(--h), 100%, 0%),
|
||||
hsl(var(--h), 100%, 50%),
|
||||
hsl(var(--h), 100%, 100%));
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div class="color-loop" :class="{ disabled: props.disabled }" :style="{ '--color-offset': 360 - deg + 'deg' }"
|
||||
@click.stop.prevent="changeDeg" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
|
||||
const deg = defineModel({ type: Number, default: 0 });
|
||||
|
||||
const props = defineProps({
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const changeDeg = (event) => {
|
||||
if (props.disabled) {
|
||||
return;
|
||||
}
|
||||
const m = event.target.clientWidth / 2;
|
||||
const x = event.offsetX - m;
|
||||
const y = event.offsetY - m;
|
||||
const temp = Math.round(Math.atan2(x, -y) * (180 / Math.PI));
|
||||
deg.value = (deg.value + temp + 360) % 360;
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@layer {
|
||||
.color-loop {
|
||||
width: 10em;
|
||||
--width-loop: 2em;
|
||||
--color-offset: 0deg;
|
||||
aspect-ratio: 1/1;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 100vw;
|
||||
cursor: crosshair;
|
||||
border: solid .15em #FFF;
|
||||
box-shadow: 0 0 .5em #0009 inset, 0 0 .5em #0009;
|
||||
|
||||
&.disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
|
||||
background: var(--colors, conic-gradient(hsl(0, 100%, 50%),
|
||||
hsl(60, 100%, 50%),
|
||||
hsl(120, 100%, 50%),
|
||||
hsl(180, 100%, 50%),
|
||||
hsl(240, 100%, 50%),
|
||||
hsl(300, 100%, 50%),
|
||||
hsl(360, 100%, 50%)));
|
||||
|
||||
transform: rotate(var(--color-offset));
|
||||
transition: transform .5s;
|
||||
border-radius: 100vw;
|
||||
mask-image: radial-gradient(circle closest-side,
|
||||
#0000 0%,
|
||||
#0000 calc(100% - var(--width-loop) - 1px),
|
||||
#000 calc(100% - var(--width-loop)));
|
||||
pointer-events: none;
|
||||
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
width: var(--width-loop);
|
||||
height: var(--width-loop);
|
||||
position: absolute;
|
||||
background: #FFF;
|
||||
box-shadow: 0 0 .5em #0009;
|
||||
pointer-events: none;
|
||||
left: 50%;
|
||||
top: 0;
|
||||
transform: translate(-50%, -95%) rotate(45deg);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<div class="WDragZoom" @wheel.stop.prevent="handleWheel">
|
||||
<w-move v-model="xy" :show="false" style="width: 100%; height: 100%; box-shadow:none; opacity: 1; transition: all .3s;">
|
||||
<div class="WDragZoom-zoom" :style="{ '--zoom': vzoom }">
|
||||
<slot :x="vx" :y="vy" :zoom="vzoom">
|
||||
<div class="WDragZoom-demo">x={{ vx }} | y={{ vy }} | zoom={{ vzoom }}</div>
|
||||
</slot>
|
||||
</div>
|
||||
</w-move>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useTemplateRef, watch, computed } from "vue";
|
||||
|
||||
const el = useTemplateRef("WDragZoomRef");
|
||||
|
||||
const vx = defineModel("x", {
|
||||
type: Number,
|
||||
default: 0,
|
||||
});
|
||||
const vy = defineModel("y", {
|
||||
type: Number,
|
||||
default: 0,
|
||||
});
|
||||
const vzoom = defineModel("zoom", {
|
||||
type: Number,
|
||||
default: 1,
|
||||
});
|
||||
|
||||
const xy = computed({
|
||||
get() {
|
||||
return { x: vx.value, y: vy.value };
|
||||
},
|
||||
set(v) {
|
||||
vx.value = v.x;
|
||||
vy.value = v.y;
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
max: {
|
||||
type: Number,
|
||||
default: 4,
|
||||
},
|
||||
min: {
|
||||
type: Number,
|
||||
default: 0.2,
|
||||
},
|
||||
show: {
|
||||
type: Boolean,
|
||||
defalut: false
|
||||
}
|
||||
});
|
||||
|
||||
const handleWheel = (event) => {
|
||||
console.debug(event);
|
||||
if (event.deltaY < 0) {
|
||||
vzoom.value = vzoom.value + (props.max - vzoom.value) / 10;
|
||||
} else {
|
||||
vzoom.value = vzoom.value - (vzoom.value - props.min) / 10;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@layer {
|
||||
.WDragZoom {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
cursor: move;
|
||||
|
||||
&-zoom {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transform: scale(var(--zoom, 1));
|
||||
transition: transform 0.5s;
|
||||
}
|
||||
|
||||
&-demo {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
background-image: repeating-linear-gradient(45deg,
|
||||
#0000 0,
|
||||
#0000 20px,
|
||||
#f005 20px,
|
||||
#f005 30px),
|
||||
repeating-linear-gradient(-45deg,
|
||||
#0000 0,
|
||||
#0000 20px,
|
||||
#00f5 20px,
|
||||
#00f5 30px);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,38 +1,41 @@
|
||||
<template>
|
||||
<w-text am time="15s" style="--move: 15em" class="main-title">
|
||||
<div :contenteditable="editable" @dblclick="edit">
|
||||
{{ poStore.po.mainTitle || title }}
|
||||
</div>
|
||||
</w-text>
|
||||
<w-text am time="15s" class="main-title">
|
||||
<div :contenteditable="editable" @dblclick="edit">
|
||||
{{ poStore.po.mainTitle || configStore.config.title || title }}
|
||||
</div>
|
||||
</w-text>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { usePoStore } from '@/store'
|
||||
import { usePoStore, useConfigStore } from '@/store'
|
||||
const title = import.meta.env.VITE_APP_NAME
|
||||
const poStore = usePoStore()
|
||||
const configStore = useConfigStore()
|
||||
|
||||
const editable = ref(false)
|
||||
|
||||
const edit = (event) => {
|
||||
if (editable.value == false) {
|
||||
editable.value = true
|
||||
} else {
|
||||
editable.value = false
|
||||
poStore.po.mainTitle = event.target.textContent;
|
||||
}
|
||||
if (editable.value == false) {
|
||||
editable.value = true
|
||||
} else {
|
||||
editable.value = false
|
||||
poStore.po.mainTitle = event.target.textContent;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.main-title {
|
||||
color: var(--w-main-light-3);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: "黑体";
|
||||
font-weight: bold;
|
||||
font-size: 2.5rem;
|
||||
letter-spacing: 0.2rem;
|
||||
// transform: scale(1, 1.1);
|
||||
|
||||
color: var(--w-main-dark-3);
|
||||
--c: var(--w-text-light);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: "黑体";
|
||||
font-weight: bold;
|
||||
font-size: 2.5rem;
|
||||
letter-spacing: 0.2rem;
|
||||
--move: 15em;
|
||||
--c-l: var(--w-text-dark-5);
|
||||
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<div class="w-tabs" :class="{ up: props.up }">
|
||||
<div v-for="(name, index) in list" :key="index" :class="{ 'selected': tabIndex == index }"
|
||||
@click.stop.prevent="handleClick(index)" @dblclick.stop.prevent="emit('dblclick', index)">{{ name }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ClickOutside } from 'element-plus';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const tabIndex = defineModel({ type: Number, default: 0 });
|
||||
|
||||
const emit = defineEmits(['change', 'dblclick'])
|
||||
|
||||
const props = defineProps({
|
||||
names: { type: [String, Array], default: [] },
|
||||
up: { type: Boolean, default: false },
|
||||
})
|
||||
|
||||
const list = computed(() => {
|
||||
if (Array.isArray(props.names)) {
|
||||
return props.names;
|
||||
} else {
|
||||
return props.names.split(/[\/,;]/);
|
||||
}
|
||||
})
|
||||
|
||||
const handleClick = (index) => {
|
||||
if (tabIndex.value != index) {
|
||||
tabIndex.value = index;
|
||||
emit('change', index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@layer {
|
||||
.w-tabs {
|
||||
display: flex;
|
||||
font-size: 1rem;
|
||||
font-weight: normal;
|
||||
transform: scale(.75);
|
||||
position: relative;
|
||||
|
||||
|
||||
&>div {
|
||||
padding: .5rem .8rem;
|
||||
border: solid .01rem #0005;
|
||||
background-color: #0002;
|
||||
cursor: pointer;
|
||||
transition: all .3s;
|
||||
font-weight: 300;
|
||||
|
||||
|
||||
&:not(:first-child) {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
padding-left: 1rem;
|
||||
border-top-left-radius: 2rem;
|
||||
border-bottom-left-radius: 2rem;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-right: 1rem;
|
||||
border-top-right-radius: 2rem;
|
||||
border-bottom-right-radius: 2rem;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
// background-image: linear-gradient(to top, var(--w-main-light-5) 0%, var(--w-main) 70%, var(--w-main) 100%);
|
||||
background-color: var(--w-selected-color,var(--w-main-light-3));
|
||||
position: relative;
|
||||
border-top-color: var(--w-selected-color,var(--w-main-light-3));
|
||||
|
||||
&:not(:first-child) {
|
||||
border-top-left-radius: .1rem;
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
border-top-right-radius: .1rem;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
width: 0;
|
||||
height: 0;
|
||||
border: solid .5rem #0000;
|
||||
border-top-color: var(--w-selected-color,var(--w-main-light-3));
|
||||
border-bottom-color: #0000;
|
||||
position: absolute;
|
||||
bottom: -1rem;
|
||||
top: unset;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.up>div.selected::before {
|
||||
border-top-color: #0000;
|
||||
border-bottom-color: var(--w-selected-color,var(--w-main-light-3));
|
||||
position: absolute;
|
||||
top: -1rem;
|
||||
bottom: unset;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,307 @@
|
||||
<template>
|
||||
<div ref="container" class="WThree">
|
||||
<div v-if="loading" class="loading">
|
||||
<div>模型加载中...</div>
|
||||
<div class="ps" :style="{ '--ps': ps + '%' }"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import * as THREE from 'three';
|
||||
import gsap from 'gsap';
|
||||
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
||||
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
||||
import { onMounted, getCurrentInstance, onUnmounted, ref, toRef } from 'vue'
|
||||
import { useResizeObserver, useRafFn } from '@vueuse/core';
|
||||
import { bigDataStore } from '@/store'
|
||||
import axios from 'axios'
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
const props = defineProps({
|
||||
/**
|
||||
* 需要加载的glb模型列表
|
||||
*/
|
||||
models: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
/**
|
||||
* 是否自由视角控制
|
||||
*/
|
||||
controls: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/**
|
||||
* 是否自动加载glb中的模型
|
||||
*/
|
||||
auto: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/** 视角 */
|
||||
fov: {
|
||||
type: Number,
|
||||
default: 50
|
||||
},
|
||||
/** 近视距 */
|
||||
near: {
|
||||
type: Number,
|
||||
default: 0.1
|
||||
},
|
||||
/** 远视距 */
|
||||
far: {
|
||||
type: Number,
|
||||
default: 2000
|
||||
},
|
||||
/** 场景是否透明 */
|
||||
alpha: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/** 抗锯齿,值越大,效果越好,开销越大 */
|
||||
samples: {
|
||||
type: Number,
|
||||
default: 5
|
||||
}
|
||||
})
|
||||
|
||||
const loading = ref(true);
|
||||
|
||||
const emit = defineEmits(['success', 'error', 'init', 'update', 'resize']);
|
||||
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const exposes = { THREE, gsap, GLTFLoader, width: window.innerWidth, height: window.innerHeight };
|
||||
|
||||
const width = toRef(exposes, 'width');
|
||||
const height = toRef(exposes, 'height');
|
||||
|
||||
const ps = ref(0);
|
||||
const computedPs = () => {
|
||||
let models = exposes.models || [];
|
||||
let total = models.reduce((a, b) => a + (b.total || 0), 0);
|
||||
if (total === 0) {
|
||||
ps.value = 0;
|
||||
}
|
||||
let loaded = models.reduce((a, b) => a + (b.loaded || 0), 0);
|
||||
ps.value = loaded / total * 100;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 视角调整给模型
|
||||
* @param model 模型对象
|
||||
*/
|
||||
exposes.lookAt = (model) => {
|
||||
model.getWorldPosition(new THREE.Vector3());
|
||||
exposes.camera.lookAt(model.position);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 加载gltf模型
|
||||
* @param url 模型地址
|
||||
* @param ps 进度回调
|
||||
* @returns Promise
|
||||
*/
|
||||
exposes.loadGLTFModel = (url, ps = () => { }) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const loader = new GLTFLoader();
|
||||
bigDataStore.get(url).then(dbData => {
|
||||
if (dbData) {
|
||||
console.debug("加载缓存中的模型");
|
||||
loader.parse(
|
||||
dbData.data,
|
||||
'',
|
||||
(gltf) => resolve(gltf),
|
||||
(error) => {
|
||||
ElMessage.error('模型解析失败');
|
||||
reject(error);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
axios.get(url, {
|
||||
responseType: 'arraybuffer',
|
||||
onDownloadProgress: function (progressEvent) {
|
||||
ps(progressEvent.loaded, progressEvent.total);
|
||||
}
|
||||
})
|
||||
.then((response)=> {
|
||||
bigDataStore.add(url, response.data);
|
||||
console.debug("response",response);
|
||||
loader.parse(
|
||||
response.data,
|
||||
'',
|
||||
(gltf) => resolve(gltf),
|
||||
(error) => {
|
||||
ElMessage.error('模型解析失败');
|
||||
reject(error);
|
||||
}
|
||||
);
|
||||
})
|
||||
.catch(function (error) {
|
||||
ElMessage.error('模型下载失败');
|
||||
reject(error)
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Renderer
|
||||
* @param canvas
|
||||
*/
|
||||
exposes.newRenderer = (canvas) => {
|
||||
return new THREE.WebGLRenderer({ canvas: canvas, alpha: props.alpha, antialias: true, samples: props.samples });
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 添加环境光
|
||||
* @param color 颜色
|
||||
* @param intensity 强度
|
||||
*/
|
||||
exposes.addAmbientLight = (color, intensity) => {
|
||||
exposes.scene.add(new THREE.AmbientLight(color, intensity));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加点源光
|
||||
* @param x x
|
||||
* @param y y
|
||||
* @param z z
|
||||
* @param color 颜色
|
||||
* @param intensity 强度
|
||||
* @param distance 光照范围,默认:0,表示无穷远
|
||||
* @param decay 削减强度,默认: 2
|
||||
*/
|
||||
exposes.addPointLight = (x, y, z, color, intensity, distance = 0, decay = 2) => {
|
||||
const pointLight = new THREE.PointLight(color, intensity, distance, decay);
|
||||
pointLight.position.set(x, y, z);
|
||||
exposes.scene.add(pointLight);
|
||||
}
|
||||
|
||||
|
||||
|
||||
onMounted(async () => {
|
||||
await proxy.$nextTick();
|
||||
// 创建场景、相机和渲染器
|
||||
exposes.container = proxy.$refs.container;
|
||||
const w = exposes.container.clientWidth;
|
||||
const h = exposes.container.clientHeight;
|
||||
width.value = w;
|
||||
height.value = h;
|
||||
|
||||
exposes.scene = new THREE.Scene();
|
||||
exposes.camera = new THREE.PerspectiveCamera(props.fov, w / h, props.near, props.far);
|
||||
exposes.renderer = new THREE.WebGLRenderer({ alpha: props.alpha, antialias: true, samples: props.samples });
|
||||
if (props.controls) {
|
||||
exposes.controls = new OrbitControls(exposes.camera, exposes.renderer.domElement);
|
||||
}
|
||||
exposes.renderer.setSize(w, h);
|
||||
exposes.container.appendChild(exposes.renderer.domElement);
|
||||
|
||||
|
||||
|
||||
useResizeObserver(exposes.container, (es) => {
|
||||
const rect = es[0].contentRect;
|
||||
exposes.camera.aspect = rect.width / rect.height;
|
||||
exposes.camera.updateProjectionMatrix();
|
||||
exposes.renderer.setSize(rect.width, rect.height);
|
||||
width.value = rect.width;
|
||||
height.value = rect.height;
|
||||
emit('resize', exposes);
|
||||
});
|
||||
|
||||
// 渲染循环
|
||||
const { pause, resume } = useRafFn(() => {
|
||||
if (exposes.controls) {
|
||||
exposes.controls.update();
|
||||
}
|
||||
exposes.renderer.render(exposes.scene, exposes.camera);
|
||||
emit('update', exposes);
|
||||
});
|
||||
exposes.pause = pause;
|
||||
exposes.resume = resume;
|
||||
|
||||
emit('init', exposes);
|
||||
|
||||
if (props.models.length == 0) {
|
||||
emit('success', exposes);
|
||||
} else {
|
||||
exposes.models = props.models.map(a => ({ url: a }));
|
||||
Promise.all(exposes.models.map(async (a) => {
|
||||
const gltf = await exposes.loadGLTFModel(a.url, (loaded, total) => {
|
||||
a.loaded = loaded;
|
||||
a.total = total;
|
||||
computedPs();
|
||||
});
|
||||
a.model = gltf;
|
||||
if (props.auto) {
|
||||
const model = a.model.scene;
|
||||
exposes.scene.add(model);
|
||||
}
|
||||
})).then(() => {
|
||||
if (props.auto && !props.controls) {
|
||||
const model = exposes.models[0].model.scene;
|
||||
model.getWorldPosition(new THREE.Vector3());
|
||||
exposes.camera.lookAt(model.position);
|
||||
}
|
||||
emit('success', exposes);
|
||||
}).catch((e) => {
|
||||
console.error(e);
|
||||
emit('error', exposes, e);
|
||||
}).finally(() => {
|
||||
loading.value = false;
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
|
||||
defineExpose(exposes);
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.WThree {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
.loading {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-color: #00000001;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
.ps {
|
||||
width: 50%;
|
||||
height: 2em;
|
||||
border: .1em solid #fff2;
|
||||
background-color: #fff1;
|
||||
border-radius: 2em;
|
||||
margin: .5em;
|
||||
overflow: hidden;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
display: flex;
|
||||
overflow: visible;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: var(--ps);
|
||||
height: 100%;
|
||||
background-color: #fff5;
|
||||
border-radius: 2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue