| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- <template>
- <div class="pano-layout" v-loading="loading">
- <canvas ref="panoDomRef"></canvas>
- <div class="btns">
- <el-button
- size="large"
- type="primary"
- style="margin-right: 20px; width: 100px"
- @click="photo"
- >
- 屏幕拍照
- </el-button>
- <el-button
- size="large"
- style="margin-right: 20px; width: 100px"
- @click="copyGis"
- v-if="point && !noValidPoint(point)"
- >
- 复制经纬度
- </el-button>
- <el-button
- size="large"
- type="primary"
- style="width: 100px"
- @click="update = true"
- v-if="router.currentRoute.value.name === 'pano'"
- >
- 测点说明
- </el-button>
- </div>
- </div>
- <SingleInput
- v-if="point"
- :visible="update"
- isAllowEmpty
- @update:visible="update = false"
- :value="point.name || ''"
- :update-value="tex => updateScenePointName(point!, tex)"
- title="测点说明"
- placeholder="请填写测点说明"
- />
- </template>
- <script setup lang="ts">
- import SingleInput from "@/components/single-input.vue";
- import { router, setDocTitle } from "@/router";
- import { mergeFuns, round } from "@/util";
- import { computed, onMounted, onUnmounted, ref, watchEffect } from "vue";
- import { init } from "./env";
- import {
- updateScenePointName,
- getPointPano,
- ScenePoint,
- scenePoints,
- } from "@/store/scene";
- import { copyText, toDegrees, getTextBound } 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";
- type Params = { pid?: string; relicsId?: string } | null;
- const params = computed(() => router.currentRoute.value.params as Params);
- const panoDomRef = ref<HTMLCanvasElement>();
- const destroyFns: (() => void)[] = [];
- const point = ref<ScenePoint>();
- watchEffect(() => {
- if (params.value?.pid) {
- const pid = Number(params.value!.pid);
- point.value = scenePoints.value.find((scene) => scene.id === pid);
- if (!point.value) {
- initRelics(Number(params.value.relicsId)).then(() => {
- point.value = scenePoints.value.find((scene) => scene.id === pid);
- if (!point.value) {
- router.replace({ name: "relics" });
- }
- });
- }
- }
- });
- const panoUrls = computed(() => {
- return (
- point.value && getPointPano(point.value, point.value.cameraType === DeviceType.CLUNT)
- );
- });
- const update = ref(false);
- const loading = ref(false);
- const getGis = () => {
- const pos = point.value!.pos as number[];
- return `经度: ${toDegrees(pos[0])}\n纬度: ${toDegrees(pos[1])}\n高程: ${round(
- pos[2],
- 4
- )}`;
- };
- const copyGis = async () => {
- await copyText(getGis());
- 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/jpg", 1));
- };
- image.onerror = reject;
- });
- };
- const photo = async () => {
- loading.value = true;
- await new Promise((resolve) => setTimeout(resolve, 300));
- const ration = 3;
- setSize(ration, 1920, 1080);
- let dataURL = panoDomRef.value.toDataURL("image/jpg", 1);
- if (!noValidPoint(point.value)) {
- dataURL = await addWatermark(dataURL, ration);
- }
- await saveAs(dataURL, `${relics.value?.name}.jpg`);
- ElMessage.success("图片导出成功");
- setSize(devicePixelRatio);
- loading.value = false;
- };
- 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]);
- };
- onMounted(() => {
- if (!panoDomRef.value) throw "没有canvas DOM";
- pano = init(panoDomRef.value);
- const resizeHandler = () => {
- setSize(devicePixelRatio);
- };
- resizeHandler();
- window.addEventListener("resize", resizeHandler);
- destroyFns.push(
- watchEffect(() => {
- if (panoUrls.value) {
- loading.value = true;
- pano.changeUrls(panoUrls.value).then(() => (loading.value = false));
- }
- }),
- pano.destory,
- () => {
- window.removeEventListener("resize", resizeHandler);
- }
- );
- });
- onUnmounted(() => mergeFuns(...destroyFns)());
- watchEffect(() => {
- if (router.currentRoute.value.name.toString().includes("pano") && point.value) {
- setDocTitle(point.value.index.toString() || relics.value.name);
- }
- });
- </script>
- <style scoped lang="scss">
- .pano-layout,
- canvas {
- width: 100%;
- height: 100%;
- }
- .pano-layout {
- position: relative;
- .btns {
- position: absolute;
- left: 50%;
- transform: translateX(-50%);
- bottom: 40px;
- z-index: 1;
- }
- }
- </style>
|