实现截图分享深渊数据,其他部分的截图分享待定

This commit is contained in:
BTMuli
2023-06-09 23:37:02 +08:00
parent 03dfdc26e1
commit f74c803b5d
4 changed files with 147 additions and 29 deletions

45
package-lock.json generated
View File

@@ -12,6 +12,7 @@
"@mdi/font": "7.2.96", "@mdi/font": "7.2.96",
"@tauri-apps/api": "^1.3.0", "@tauri-apps/api": "^1.3.0",
"clipboard": "^2.0.11", "clipboard": "^2.0.11",
"html2canvas": "^1.4.1",
"js-md5": "^0.7.3", "js-md5": "^0.7.3",
"pinia": "^2.0.36", "pinia": "^2.0.36",
"pinia-plugin-persistedstate": "^3.1.0", "pinia-plugin-persistedstate": "^3.1.0",
@@ -1029,6 +1030,14 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true "dev": true
}, },
"node_modules/base64-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/base64id": { "node_modules/base64id": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmmirror.com/base64id/-/base64id-2.0.0.tgz", "resolved": "https://registry.npmmirror.com/base64id/-/base64id-2.0.0.tgz",
@@ -1436,6 +1445,14 @@
"node": ">=12.22" "node": ">=12.22"
} }
}, },
"node_modules/css-line-break": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
"dependencies": {
"utrie": "^1.0.2"
}
},
"node_modules/css-tree": { "node_modules/css-tree": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-2.3.1.tgz", "resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-2.3.1.tgz",
@@ -3094,6 +3111,18 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/html2canvas": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
"dependencies": {
"css-line-break": "^2.1.0",
"text-segmentation": "^1.0.3"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/htmlparser2": { "node_modules/htmlparser2": {
"version": "8.0.2", "version": "8.0.2",
"resolved": "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-8.0.2.tgz", "resolved": "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-8.0.2.tgz",
@@ -5536,6 +5565,14 @@
"@tauri-apps/api": "^1.2.0" "@tauri-apps/api": "^1.2.0"
} }
}, },
"node_modules/text-segmentation": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
"dependencies": {
"utrie": "^1.0.2"
}
},
"node_modules/text-table": { "node_modules/text-table": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz", "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz",
@@ -5791,6 +5828,14 @@
"node": ">= 0.4.0" "node": ">= 0.4.0"
} }
}, },
"node_modules/utrie": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
"dependencies": {
"base64-arraybuffer": "^1.0.2"
}
},
"node_modules/v8-compile-cache": { "node_modules/v8-compile-cache": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmmirror.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", "resolved": "https://registry.npmmirror.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",

View File

@@ -45,6 +45,7 @@
"@mdi/font": "7.2.96", "@mdi/font": "7.2.96",
"@tauri-apps/api": "^1.3.0", "@tauri-apps/api": "^1.3.0",
"clipboard": "^2.0.11", "clipboard": "^2.0.11",
"html2canvas": "^1.4.1",
"js-md5": "^0.7.3", "js-md5": "^0.7.3",
"pinia": "^2.0.36", "pinia": "^2.0.36",
"pinia-plugin-persistedstate": "^3.1.0", "pinia-plugin-persistedstate": "^3.1.0",

View File

@@ -6,6 +6,10 @@
{{ item.id }} {{ item.id }}
</v-tab> </v-tab>
<div class="ua-tab-bottom"> <div class="ua-tab-bottom">
<v-btn class="ua-btn" @click="shareAbyss">
<v-icon>mdi-share</v-icon>
<span>分享</span>
</v-btn>
<v-btn class="ua-btn" @click="getAbyssData"> <v-btn class="ua-btn" @click="getAbyssData">
<v-icon>mdi-refresh</v-icon> <v-icon>mdi-refresh</v-icon>
<span>刷新</span> <span>刷新</span>
@@ -14,35 +18,37 @@
</v-tabs> </v-tabs>
<v-window v-model="userTab" class="ua-window"> <v-window v-model="userTab" class="ua-window">
<v-window-item v-for="item in localAbyss" :key="item.id" :value="item.id" class="ua-window-item"> <v-window-item v-for="item in localAbyss" :key="item.id" :value="item.id" class="ua-window-item">
<div class="uaw-title"> <div :ref="getAbyssRef">
<span>挑战回顾{{ user.gameUid }}</span> <div class="uaw-title">
<span>更新于 {{ item.updated }}</span> <span> {{ item.id }} 挑战回顾{{ user.gameUid }}</span>
</div> <span>更新于 {{ item.updated }}</span>
<div class="uaw-sub-title"> </div>
<img src="/src/assets/icons/arrow-right.svg" alt="character"> <div class="uaw-sub-title">
<span>统计周期 {{ item.startTime }} ~ {{ item.endTime }}</span> <img src="/src/assets/icons/arrow-right.svg" alt="character">
</div> <span>统计周期 {{ item.startTime }} ~ {{ item.endTime }}</span>
<div class="uaw-o-box"> </div>
<TuaOverview title="战斗次数" :val-text="item.totalBattleTimes" /> <div class="uaw-o-box">
<TuaOverview title="获得渊星" :val-text="item.totalStar" /> <TuaOverview title="战斗次数" :val-text="item.totalBattleTimes" />
<TuaOverview title="最深抵达" :val-text="item.maxFloor" /> <TuaOverview title="获得渊星" :val-text="item.totalStar" />
</div> <TuaOverview title="最深抵达" :val-text="item.maxFloor" />
<div class="uaw-o-box"> </div>
<TuaOverview title="最多击破" :val-icons="item.defeatRank" /> <div class="uaw-o-box">
<TuaOverview title="最多承伤" :val-icons="item.takeDamageRank" /> <TuaOverview title="最多击破" :val-icons="item.defeatRank" />
<TuaOverview title="最强一击" :val-icons="item.damageRank" /> <TuaOverview title="最多承伤" :val-icons="item.takeDamageRank" />
</div> <TuaOverview title="最强一击" :val-icons="item.damageRank" />
<div class="uaw-o-box"> </div>
<TuaOverview title="元素战技" :val-icons="item.normalSkillRank" /> <div class="uaw-o-box">
<TuaOverview title="出战次数" :val-icons="item.revealRank" :icon-num="4" /> <TuaOverview title="元素战技" :val-icons="item.normalSkillRank" />
<TuaOverview title="元素爆发" :val-icons="item.energySkillRank" /> <TuaOverview title="出战次数" :val-icons="item.revealRank" :icon-num="4" />
</div> <TuaOverview title="元素爆发" :val-icons="item.energySkillRank" />
<div class="uaw-sub-title"> </div>
<img src="/src/assets/icons/arrow-right.svg" alt="character"> <div class="uaw-sub-title">
<span>详情</span> <img src="/src/assets/icons/arrow-right.svg" alt="character">
</div> <span>详情</span>
<div class="uaw-d-box"> </div>
<TuaDetail v-for="floor in JSON.parse(item.floors) as TGApp.Sqlite.Abyss.Floor[]" :model-value="floor" /> <div class="uaw-d-box">
<TuaDetail v-for="floor in JSON.parse(item.floors) as TGApp.Sqlite.Abyss.Floor[]" :model-value="floor" />
</div>
</div> </div>
</v-window-item> </v-window-item>
</v-window> </v-window>
@@ -63,6 +69,8 @@ import { useUserStore } from "../../store/modules/user";
// utils // utils
import TGRequest from "../../web/request/TGRequest"; import TGRequest from "../../web/request/TGRequest";
import TGSqlite from "../../plugins/Sqlite"; import TGSqlite from "../../plugins/Sqlite";
import html2canvas from "html2canvas";
import { saveCanvasImg } from "../../utils/saveImg";
// store // store
const userStore = useUserStore(); const userStore = useUserStore();
@@ -79,6 +87,7 @@ const user = computed(() => userStore.getCurAccount());
const localAbyss = ref([] as TGApp.Sqlite.Abyss.SingleTable[]); const localAbyss = ref([] as TGApp.Sqlite.Abyss.SingleTable[]);
const localAbyssID = ref([] as number[]); const localAbyssID = ref([] as number[]);
const curAbyss = ref({} as TGApp.Sqlite.Abyss.SingleTable); const curAbyss = ref({} as TGApp.Sqlite.Abyss.SingleTable);
const abyssRef = ref(null as unknown as HTMLElement);
onMounted(async () => { onMounted(async () => {
loadingTitle.value = "正在加载深渊数据"; loadingTitle.value = "正在加载深渊数据";
@@ -120,6 +129,39 @@ async function getAbyssData (): Promise<void> {
function toAbyss (id: number): void { function toAbyss (id: number): void {
curAbyss.value = localAbyss.value.find((item) => item.id === id)!; curAbyss.value = localAbyss.value.find((item) => item.id === id)!;
} }
function getAbyssRef (el: HTMLElement): void {
abyssRef.value = el;
}
async function shareAbyss (): Promise<void> {
const canvas = document.createElement("canvas");
// 获取 dom 宽高
const width = abyssRef.value.scrollWidth + 50;
const height = abyssRef.value.scrollHeight + 50;
// 设置 canvas 宽高
canvas.width = width * 1.2;
canvas.height = height * 1.2;
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
// 图片压缩
canvas.getContext("2d")!.scale(1.2, 1.2);
// 设置 html2canvas 参数
const options = {
backgroundColor: "#ece5d8",
windowHeight: height,
width,
height,
useCORS: true,
canvas,
// 因为有放大,所以需要计算偏移量
x: -25,
y: -25,
};
const canvasData = await html2canvas(abyssRef.value, options);
const fileName = `深渊${curAbyss.value.id}-${user.value.gameUid}-${new Date().getTime()}.png`;
await saveCanvasImg(canvasData, fileName);
}
</script> </script>
<style lang="css" scoped> <style lang="css" scoped>
.ua-box { .ua-box {

30
src/utils/saveImg.ts Normal file
View File

@@ -0,0 +1,30 @@
/**
* @file utils saveImg.ts
* @description 用于保存图片的工具模块
* @author BTMuli <bt-muli@outlook.com>
* @since Alpha v0.2.0
*/
// tauri
import { dialog, fs } from "@tauri-apps/api";
/**
* @description 保存图片-canvas
* @since Alpha v0.2.0
* @param {HTMLCanvasElement} canvas - canvas元素
* @param {string} filename - 文件名
* @returns {Promise<void>} 无返回值
*/
export async function saveCanvasImg(canvas: HTMLCanvasElement, filename: string): Promise<void> {
const buffer = new Uint8Array(atob(canvas.toDataURL("image/png").split(",")[1]).split("").map((item) => item.charCodeAt(0)));
await dialog.save({
defaultPath: filename,
filters: [{ name: "图片", extensions: ["png"] }]
}).then(async (res) => {
if (res === null) return;
await fs.writeBinaryFile({
path: res,
contents: buffer
});
});
}