完成canvas生成图片下载
This commit is contained in:
@@ -52,6 +52,7 @@ onMounted(() => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
width: 72vw;
|
width: 72vw;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
|
/* margin-top: var(--status-bar-height); */
|
||||||
padding-left: 15px;
|
padding-left: 15px;
|
||||||
}
|
}
|
||||||
.back-btn {
|
.back-btn {
|
||||||
|
|||||||
113
src/pages.json
113
src/pages.json
@@ -1,136 +1,73 @@
|
|||||||
{
|
{
|
||||||
"pages": [
|
"pages": [
|
||||||
{
|
{
|
||||||
"path": "pages/index",
|
"path": "pages/index"
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "首页"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/my-device",
|
"path": "pages/image-share"
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "我的设备"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/device-intro",
|
"path": "pages/my-device"
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "智能弓箭"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/user",
|
"path": "pages/device-intro"
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "用户信息"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/orders",
|
"path": "pages/user"
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "订单"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/order-detail",
|
"path": "pages/orders"
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "订单详情"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/be-vip",
|
"path": "pages/order-detail"
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "会员"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/grade-intro",
|
"path": "pages/be-vip"
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "等级介绍"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/rank-intro",
|
"path": "pages/grade-intro"
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "段位介绍"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/my-growth",
|
"path": "pages/rank-intro"
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "我的成长"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/first-try",
|
"path": "pages/my-growth"
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "新手试炼"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/practise",
|
"path": "pages/first-try"
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "个人练习"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/practise-one",
|
"path": "pages/practise"
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "个人单组练习"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/practise-two",
|
"path": "pages/practise-one"
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "个人耐力挑战"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/friend-battle",
|
"path": "pages/practise-two"
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "好友约战"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/battle-room",
|
"path": "pages/friend-battle"
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "对战房间"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/ranking",
|
"path": "pages/battle-room"
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "排行赛"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/rank-list",
|
"path": "pages/ranking"
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "排行榜"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/team-match",
|
"path": "pages/rank-list"
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "排位赛"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/melee-match",
|
"path": "pages/team-match"
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "排位赛"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/match-detail",
|
"path": "pages/melee-match"
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "排位赛"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/battle-result",
|
"path": "pages/match-detail"
|
||||||
"style": {
|
},
|
||||||
"navigationBarTitleText": "对战结果"
|
{
|
||||||
}
|
"path": "pages/battle-result"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"globalStyle": {
|
"globalStyle": {
|
||||||
|
|||||||
136
src/pages/image-share.vue
Normal file
136
src/pages/image-share.vue
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
<script setup>
|
||||||
|
import { onMounted } from "vue";
|
||||||
|
import Container from "@/components/Container.vue";
|
||||||
|
import { renderText, renderRankTitle, renderLine } from "@/util.js";
|
||||||
|
|
||||||
|
const saveImage = () => {
|
||||||
|
uni.canvasToTempFilePath({
|
||||||
|
canvasId: "shareCanvas",
|
||||||
|
success: (res) => {
|
||||||
|
const tempFilePath = res.tempFilePath;
|
||||||
|
|
||||||
|
// 保存图片到相册
|
||||||
|
uni.saveImageToPhotosAlbum({
|
||||||
|
filePath: tempFilePath,
|
||||||
|
success: () => {
|
||||||
|
uni.showToast({ title: "保存成功" });
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
uni.showToast({ title: "保存失败", icon: "error" });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
var ctx = uni.createCanvasContext("shareCanvas");
|
||||||
|
const width = 302;
|
||||||
|
const height = 535;
|
||||||
|
ctx.drawImage("../static/share-bg.png", 0, 0, 302, 534);
|
||||||
|
ctx.drawImage("../static/avatar.png", 20, 20, 35, 35);
|
||||||
|
ctx.drawImage("../static/avatar-frame.png", 15, 15, 45, 45);
|
||||||
|
renderText(ctx, "用户名称", 14, "#fff", 70, 35);
|
||||||
|
renderRankTitle(ctx, "钻石1级");
|
||||||
|
ctx.drawImage(
|
||||||
|
"../static/first-try-title.png",
|
||||||
|
(width - 200) / 2,
|
||||||
|
155,
|
||||||
|
200,
|
||||||
|
53
|
||||||
|
);
|
||||||
|
renderText(ctx, "正式开启弓箭手之路", 22, "#fff", 53, 230);
|
||||||
|
renderText(ctx, "共", 16, "#fff", 124, 300);
|
||||||
|
renderText(ctx, "50", 16, "#fed847", 144, 300);
|
||||||
|
renderText(ctx, "环", 16, "#fff", 166, 300);
|
||||||
|
renderLine(ctx, 80);
|
||||||
|
renderLine(ctx, 192);
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
|
ctx.drawImage("../static/score-bg.png", 16 + i * 46, 320, 40, 40);
|
||||||
|
renderText(ctx, "9", 25, "#fed847", 29 + i * 46, 349);
|
||||||
|
}
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
|
ctx.drawImage("../static/score-bg.png", 16 + i * 46, 366, 40, 40);
|
||||||
|
renderText(ctx, "9", 25, "#fed847", 29 + i * 46, 395);
|
||||||
|
}
|
||||||
|
ctx.drawImage(
|
||||||
|
"../static/device-icon.png",
|
||||||
|
width * 0.06,
|
||||||
|
height * 0.87,
|
||||||
|
55,
|
||||||
|
55
|
||||||
|
);
|
||||||
|
renderText(ctx, "射灵平台", 16, "#fff", width * 0.28, height * 0.9);
|
||||||
|
renderText(
|
||||||
|
ctx,
|
||||||
|
"快加入我们一起玩吧~",
|
||||||
|
11,
|
||||||
|
"rgba(255, 255, 255, 0.5)",
|
||||||
|
width * 0.28,
|
||||||
|
height * 0.93
|
||||||
|
);
|
||||||
|
renderText(
|
||||||
|
ctx,
|
||||||
|
"后羿就是这样练成的",
|
||||||
|
11,
|
||||||
|
"rgba(255, 255, 255, 0.5)",
|
||||||
|
width * 0.28,
|
||||||
|
height * 0.96
|
||||||
|
);
|
||||||
|
ctx.drawImage("../static/qr-code.png", width * 0.75, height * 0.86, 60, 60);
|
||||||
|
ctx.draw();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Container>
|
||||||
|
<view class="content">
|
||||||
|
<canvas
|
||||||
|
style="width: 302px; height: 534px"
|
||||||
|
canvas-id="shareCanvas"
|
||||||
|
id="firstCanvas"
|
||||||
|
></canvas>
|
||||||
|
<view class="footer">
|
||||||
|
<view>
|
||||||
|
<image src="../static/wechat-outline.png" mode="widthFix" />
|
||||||
|
<text>微信好友</text>
|
||||||
|
</view>
|
||||||
|
<view>
|
||||||
|
<image src="../static/wechat-moment.png" mode="widthFix" />
|
||||||
|
<text>朋友圈</text>
|
||||||
|
</view>
|
||||||
|
<view @click="saveImage">
|
||||||
|
<image src="../static/download.png" mode="widthFix" />
|
||||||
|
<text>保存到相册</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</Container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.content {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
margin-top: 50px;
|
||||||
|
}
|
||||||
|
.footer > view {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.footer > view > image {
|
||||||
|
width: 45px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 8.5 KiB |
BIN
src/static/first-try-title.png
Normal file
BIN
src/static/first-try-title.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.6 KiB |
BIN
src/static/qr-code.png
Normal file
BIN
src/static/qr-code.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
51
src/util.js
Normal file
51
src/util.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
export function renderLine(ctx, from) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.strokeStyle = "rgba(255, 255, 255, 0.3)";
|
||||||
|
const length = 35;
|
||||||
|
ctx.moveTo(from, 293);
|
||||||
|
ctx.lineTo(from + length, 293);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function renderText(ctx, text, size, color, x, y) {
|
||||||
|
ctx.setFontSize(size);
|
||||||
|
ctx.setFillStyle(color);
|
||||||
|
ctx.fillText(text, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function renderRankTitle(ctx, text) {
|
||||||
|
const fontSize = 10;
|
||||||
|
const textWidth = ctx.measureText(text).width;
|
||||||
|
const padding = 8; // 文字与背景边缘的间距
|
||||||
|
const radius = 10; // 圆角半径
|
||||||
|
const textX = 75;
|
||||||
|
const textY = 55;
|
||||||
|
const x = textX - padding; // 文字 x 坐标减去内边距
|
||||||
|
const y = textY - fontSize - padding / 2 + 1; // 文字 y 坐标减去字体大小和内边距
|
||||||
|
const width = textWidth + padding * 2 - 15; // 背景宽度
|
||||||
|
const height = fontSize + padding + 1; // 背景高度
|
||||||
|
|
||||||
|
// 开始绘制圆角矩形
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x + radius, y);
|
||||||
|
// 上边框
|
||||||
|
ctx.lineTo(x + width - radius, y);
|
||||||
|
ctx.arcTo(x + width, y, x + width, y + radius, radius);
|
||||||
|
// 右边框
|
||||||
|
ctx.lineTo(x + width, y + height - radius);
|
||||||
|
ctx.arcTo(x + width, y + height, x + width - radius, y + height, radius);
|
||||||
|
// 下边框
|
||||||
|
ctx.lineTo(x + radius, y + height);
|
||||||
|
ctx.arcTo(x, y + height, x, y + height - radius, radius);
|
||||||
|
// 左边框
|
||||||
|
ctx.lineTo(x, y + radius);
|
||||||
|
ctx.arcTo(x, y, x + radius, y, radius);
|
||||||
|
|
||||||
|
// 设置背景颜色并填充
|
||||||
|
ctx.fillStyle = "#5F51FF";
|
||||||
|
ctx.fill();
|
||||||
|
ctx.setFontSize(fontSize);
|
||||||
|
ctx.setFillStyle("rgba(255, 255, 255, 0.9)");
|
||||||
|
ctx.fillText(text, textX, textY); // 绘制文字
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user