|
@@ -1,6 +1,8 @@
|
|
|
<template>
|
|
|
<div class="pano-layout" v-loading="loading" :element-loading-text="loadingStr">
|
|
|
- <canvas ref="panoDomRef"></canvas>
|
|
|
+ <div class="canvas-layout">
|
|
|
+ <canvas ref="panoDomRef"></canvas>
|
|
|
+ </div>
|
|
|
<div class="btns">
|
|
|
<el-button
|
|
|
size="large"
|
|
@@ -10,14 +12,14 @@
|
|
|
>
|
|
|
屏幕拍照
|
|
|
</el-button>
|
|
|
- <el-input-number
|
|
|
+ <!-- <el-input-number
|
|
|
style="margin-right: 20px"
|
|
|
v-model="tempRadion"
|
|
|
:precision="2"
|
|
|
:step="0.01"
|
|
|
:min="1"
|
|
|
:max="3"
|
|
|
- />
|
|
|
+ /> -->
|
|
|
<el-button
|
|
|
size="large"
|
|
|
style="margin-right: 20px; width: 100px"
|
|
@@ -51,21 +53,34 @@ import SingleInput from "@/components/point-input.vue";
|
|
|
import { router, setDocTitle } from "@/router";
|
|
|
import { mergeFuns, round } from "@/util";
|
|
|
import { glMatrix } from "gl-matrix";
|
|
|
-import { computed, nextTick, onMounted, onUnmounted, ref, watchEffect } from "vue";
|
|
|
-import { init } from "./env";
|
|
|
+import {
|
|
|
+ computed,
|
|
|
+ onActivated,
|
|
|
+ onMounted,
|
|
|
+ onUnmounted,
|
|
|
+ ref,
|
|
|
+ shallowRef,
|
|
|
+ watch,
|
|
|
+ watchEffect,
|
|
|
+} from "vue";
|
|
|
+import { init } from "./three-env";
|
|
|
+// import { init } from "./env";
|
|
|
import {
|
|
|
updateScenePointName,
|
|
|
getPointPano,
|
|
|
ScenePoint,
|
|
|
scenePoints,
|
|
|
+ SceneType,
|
|
|
} from "@/store/scene";
|
|
|
-import { copyText, toDegrees, getTextBound } from "@/util";
|
|
|
+import { copyText, toDegrees } from "@/util";
|
|
|
import { ElMessage } from "element-plus";
|
|
|
import saveAs from "@/util/file-serve";
|
|
|
import { DeviceType } from "@/store/device";
|
|
|
import { initRelics, relics } from "@/store/relics";
|
|
|
import { noValidPoint } from "../map/install";
|
|
|
import { addWatermark } from "@/util/image";
|
|
|
+import { inRevise } from "@/lib/board/4dmap";
|
|
|
+import { Texture } from "three";
|
|
|
|
|
|
type Params = { pid?: string; relicsId?: string } | null;
|
|
|
const params = computed(() => router.currentRoute.value.params as Params);
|
|
@@ -74,27 +89,37 @@ const destroyFns: (() => void)[] = [];
|
|
|
const point = ref<ScenePoint>();
|
|
|
const tempRadion = ref(3.0);
|
|
|
|
|
|
-watchEffect(() => {
|
|
|
- if (params.value?.pid) {
|
|
|
- const pid = Number(params.value!.pid);
|
|
|
+watch(
|
|
|
+ params,
|
|
|
+ (params) => {
|
|
|
+ if (!params?.pid) return;
|
|
|
+ const pid = Number(params!.pid);
|
|
|
point.value = scenePoints.value.find((scene) => scene.id === pid);
|
|
|
-
|
|
|
+ try {
|
|
|
+ pano?.reset();
|
|
|
+ } catch {}
|
|
|
if (!point.value) {
|
|
|
- initRelics(Number(params.value.relicsId)).then(() => {
|
|
|
+ initRelics(Number(params.relicsId)).then(() => {
|
|
|
point.value = scenePoints.value.find((scene) => scene.id === pid);
|
|
|
if (!point.value) {
|
|
|
router.replace({ name: "relics" });
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
+ },
|
|
|
+ { immediate: true }
|
|
|
+);
|
|
|
+
|
|
|
+const panoUrls = ref<string | string[]>();
|
|
|
+watchEffect(() => {
|
|
|
+ if (point.value) {
|
|
|
+ panoUrls.value = getPointPano(
|
|
|
+ point.value,
|
|
|
+ [SceneType.CLUNT, SceneType.MESH].includes(point.value.sceneType)
|
|
|
+ );
|
|
|
}
|
|
|
});
|
|
|
|
|
|
-const panoUrls = computed(() => {
|
|
|
- return (
|
|
|
- point.value && getPointPano(point.value, point.value.cameraType === DeviceType.CLUNT)
|
|
|
- );
|
|
|
-});
|
|
|
const update = ref(false);
|
|
|
const loading = ref(false);
|
|
|
const loadingStr = ref("");
|
|
@@ -112,115 +137,114 @@ const copyGis = async () => {
|
|
|
ElMessage.success("经纬度高程复制成功");
|
|
|
};
|
|
|
|
|
|
-const canvas = document.createElement("canvas");
|
|
|
-// 水印添加函数
|
|
|
-const addWatermark = (imgURL: string, ration: number) => {
|
|
|
- const ctx = canvas.getContext("2d");
|
|
|
- const image = new Image();
|
|
|
- image.src = imgURL;
|
|
|
-
|
|
|
- return new Promise<string>((resolve, reject) => {
|
|
|
- image.onload = () => {
|
|
|
- canvas.width = image.width;
|
|
|
- canvas.height = image.height;
|
|
|
- ctx.drawImage(image, 0, 0, image.width, image.height);
|
|
|
-
|
|
|
- const font = `${ration * 20}px Arial`;
|
|
|
- const pos = point.value!.pos as number[];
|
|
|
- const lines = `经度: ${toDegrees(pos[0])}\n纬度: ${toDegrees(pos[1])}`.split("\n");
|
|
|
- const lineTopPadding = 5 * ration;
|
|
|
- const lineBounds = lines.map((line) =>
|
|
|
- getTextBound(line, font, [lineTopPadding, 0])
|
|
|
- );
|
|
|
- const bound = lineBounds.reduce(
|
|
|
- (t, { width, height }) => {
|
|
|
- t.width = Math.max(t.width, width);
|
|
|
- t.height += height;
|
|
|
- return t;
|
|
|
- },
|
|
|
- { width: 0, height: 0 }
|
|
|
- );
|
|
|
- const padding = 20 * ration;
|
|
|
- const margin = 80 * ration;
|
|
|
-
|
|
|
- const position = [
|
|
|
- image.width - margin - bound.width,
|
|
|
- image.height - margin - bound.height,
|
|
|
- ];
|
|
|
-
|
|
|
- ctx.rect(
|
|
|
- position[0] - padding,
|
|
|
- position[1] - padding,
|
|
|
- bound.width + 2 * padding,
|
|
|
- bound.height + 2 * padding
|
|
|
- );
|
|
|
- ctx.fillStyle = "rgba(0, 0, 0, 0.3)";
|
|
|
- ctx.fill();
|
|
|
-
|
|
|
- ctx.font = font;
|
|
|
- ctx.textBaseline = "top";
|
|
|
- ctx.fillStyle = "#fff";
|
|
|
- let itemTop = 0;
|
|
|
- lines.forEach((line, ndx) => {
|
|
|
- ctx.fillText(line, position[0], position[1] + itemTop + lineTopPadding);
|
|
|
- itemTop += lineBounds[ndx].height;
|
|
|
- });
|
|
|
- resolve(canvas.toDataURL("image/jpeg", 1));
|
|
|
- };
|
|
|
- image.onerror = reject;
|
|
|
- });
|
|
|
-};
|
|
|
-
|
|
|
+let prevBigImages: string | string[];
|
|
|
+let prevBigTexs: Texture[];
|
|
|
const photo = async () => {
|
|
|
loading.value = true;
|
|
|
+ const bigImages = getPointPano(
|
|
|
+ point.value,
|
|
|
+ [SceneType.CLUNT, SceneType.MESH].includes(point.value.sceneType),
|
|
|
+ false
|
|
|
+ );
|
|
|
+ if (
|
|
|
+ inRevise(panoUrls.value, bigImages) &&
|
|
|
+ (!prevBigTexs || inRevise(prevBigImages, bigImages))
|
|
|
+ ) {
|
|
|
+ if (prevBigTexs) {
|
|
|
+ prevBigTexs.forEach((tex) => tex.dispose());
|
|
|
+ }
|
|
|
+ prevBigTexs = await pano.getTexs(bigImages);
|
|
|
+ prevBigImages = bigImages;
|
|
|
+ pano.changeEnv(prevBigTexs);
|
|
|
+ pano.redraw();
|
|
|
+ }
|
|
|
+
|
|
|
loadingStr.value = "原图提取中";
|
|
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
|
const ration = tempRadion.value;
|
|
|
- console.log("ration", ration);
|
|
|
setSize(ration, 1920, 1080);
|
|
|
- let dataURL = panoDomRef.value.toDataURL("image/jpeg", 1);
|
|
|
+
|
|
|
+ let dataURL: Blob | string = panoDomRef.value.toDataURL("image/jpeg", 1);
|
|
|
if (!noValidPoint(point.value)) {
|
|
|
dataURL = await addWatermark(dataURL, point.value!.pos, ration);
|
|
|
}
|
|
|
|
|
|
await saveAs(dataURL, `${relics.value?.name}.jpg`);
|
|
|
ElMessage.success("图片导出成功");
|
|
|
- setSize(devicePixelRatio);
|
|
|
+ setSize(1);
|
|
|
+ pano.changeEnv(thumbnailTexs.value);
|
|
|
+ pano.redraw();
|
|
|
loading.value = false;
|
|
|
+
|
|
|
+ prevBigTexs.forEach((tex) => tex.dispose());
|
|
|
+ prevBigTexs = null;
|
|
|
+ prevBigImages = null;
|
|
|
};
|
|
|
|
|
|
let pano: ReturnType<typeof init>;
|
|
|
const setSize = (ration: number, w?: number, h?: number) => {
|
|
|
- const canvas = panoDomRef.value!;
|
|
|
- canvas.width = (w || canvas.offsetWidth) * ration;
|
|
|
- canvas.height = (h || canvas.offsetHeight) * ration;
|
|
|
- pano.setSize([canvas.width, canvas.height]);
|
|
|
+ const canvas = panoDomRef.value!.parentElement;
|
|
|
+
|
|
|
+ w = (w || canvas.offsetWidth) * ration;
|
|
|
+ h = (h || canvas.offsetHeight) * ration;
|
|
|
+ pano.setSize([w, h]);
|
|
|
+ pano.redraw();
|
|
|
};
|
|
|
|
|
|
+const thumbnailTexs = shallowRef<Texture[]>([]);
|
|
|
onMounted(() => {
|
|
|
if (!panoDomRef.value) throw "没有canvas DOM";
|
|
|
- pano = init(panoDomRef.value, 0);
|
|
|
+ pano = init(panoDomRef.value);
|
|
|
const resizeHandler = () => {
|
|
|
- setSize(devicePixelRatio);
|
|
|
+ setSize(1);
|
|
|
};
|
|
|
resizeHandler();
|
|
|
|
|
|
window.addEventListener("resize", resizeHandler);
|
|
|
- destroyFns.push(
|
|
|
- watchEffect(() => {
|
|
|
- if (panoUrls.value) {
|
|
|
+ const s1 = watch(
|
|
|
+ panoUrls,
|
|
|
+ (n, o) => {
|
|
|
+ if (inRevise(n, o) && n) {
|
|
|
loading.value = true;
|
|
|
- pano.changeUrls(panoUrls.value).then(() => (loading.value = false));
|
|
|
- pano.setYaw(
|
|
|
- point.value.cameraType === DeviceType.CLUNT ? glMatrix.toRadian(180) : 0
|
|
|
+ // getPointPano(
|
|
|
+ // point.value,
|
|
|
+ // [SceneType.CLUNT, SceneType.MESH].includes(point.value.sceneType)
|
|
|
+ // )
|
|
|
+ pano.getTexs(n).then(
|
|
|
+ (texs) => {
|
|
|
+ loading.value = false;
|
|
|
+ thumbnailTexs.value.forEach((t) => t.dispose());
|
|
|
+ thumbnailTexs.value = texs;
|
|
|
+ },
|
|
|
+ (err) => {
|
|
|
+ loading.value = false;
|
|
|
+ panoUrls.value = getPointPano(
|
|
|
+ point.value,
|
|
|
+ [SceneType.CLUNT, SceneType.MESH].includes(point.value.sceneType),
|
|
|
+ false
|
|
|
+ );
|
|
|
+ }
|
|
|
);
|
|
|
}
|
|
|
- }),
|
|
|
- pano.destory,
|
|
|
- () => {
|
|
|
- window.removeEventListener("resize", resizeHandler);
|
|
|
- }
|
|
|
+ },
|
|
|
+ { flush: "sync", immediate: true }
|
|
|
);
|
|
|
+ const s2 = watchEffect(() => {
|
|
|
+ if (point.value) {
|
|
|
+ const yaw = point.value.cameraType === DeviceType.CLUNT ? 90 : -90;
|
|
|
+ pano.setYaw(glMatrix.toRadian(yaw));
|
|
|
+ }
|
|
|
+ });
|
|
|
+ const s3 = watchEffect(() => {
|
|
|
+ if (thumbnailTexs.value.length) {
|
|
|
+ pano.changeEnv(thumbnailTexs.value);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ destroyFns.push(s1, s2, s3, pano.destory, () => {
|
|
|
+ thumbnailTexs.value.forEach((t) => t.dispose());
|
|
|
+ window.removeEventListener("resize", resizeHandler);
|
|
|
+ });
|
|
|
});
|
|
|
|
|
|
onUnmounted(() => mergeFuns(...destroyFns)());
|
|
@@ -233,6 +257,7 @@ watchEffect(() => {
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
.pano-layout,
|
|
|
+.canvas-layout,
|
|
|
canvas {
|
|
|
width: 100%;
|
|
|
height: 100%;
|