Merge branch 'feat/liujie'
This commit is contained in:
@@ -192,6 +192,17 @@ const NTRPEvaluatePopup = (props: NTRPEvaluatePopupProps, ref) => {
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
<View className={styles.picker}>
|
||||
<Picker
|
||||
visible={visible}
|
||||
options={options}
|
||||
value={ntrp}
|
||||
onChange={(val) => {
|
||||
console.log(val);
|
||||
setNtrp(val.values);
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</CommonPopup>
|
||||
|
||||
355
src/components/Poster/index.tsx
Normal file
355
src/components/Poster/index.tsx
Normal file
@@ -0,0 +1,355 @@
|
||||
import { useEffect, useImperativeHandle, forwardRef } from "react";
|
||||
import { Canvas } from "@tarojs/components";
|
||||
import Taro from "@tarojs/taro";
|
||||
|
||||
function getImageWh(src): Promise<{ width: number; height: number }> {
|
||||
return new Promise((resolve) => {
|
||||
Taro.getImageInfo({
|
||||
src,
|
||||
success: ({ width, height }) => resolve({ width, height }),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const qrCodeUrl =
|
||||
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/5e013195-fc79-4082-bf06-9aa79aea65ae.png";
|
||||
|
||||
const ringUrl =
|
||||
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/b635164f-ecec-434a-a00b-69614a918f2f.png";
|
||||
|
||||
const dateIcon =
|
||||
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/1b49476e-0eda-42ff-b08c-002ce510df82.jpg";
|
||||
|
||||
const mapIcon =
|
||||
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/06b994fa-9227-4708-8555-8a07af8d0c3b.jpg";
|
||||
|
||||
// const logo = "http://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/fb732da6-11b9-4022-a524-a377b17635eb.jpg"
|
||||
|
||||
const logoText =
|
||||
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/9d8cbc9d-9601-4e2d-ab23-76420a4537d6.png";
|
||||
|
||||
const Poster = (props, ref) => {
|
||||
const { data } = props;
|
||||
const {
|
||||
playType,
|
||||
ntrp,
|
||||
mainCoursal,
|
||||
nickname,
|
||||
avatarUrl,
|
||||
title,
|
||||
locationName,
|
||||
date,
|
||||
time,
|
||||
} = data;
|
||||
useEffect(() => {
|
||||
drawCard();
|
||||
}, []);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
generateImage: () =>
|
||||
new Promise((resolve, reject) => {
|
||||
const query = Taro.createSelectorQuery();
|
||||
query
|
||||
.select("#cardCanvas")
|
||||
.fields({ node: true, size: true })
|
||||
.exec((res) => {
|
||||
const canvas = res[0].node;
|
||||
// ⚠️ 关键:传 canvas,而不是 canvasId
|
||||
Taro.canvasToTempFilePath({
|
||||
canvas,
|
||||
success: (res) => resolve(res.tempFilePath),
|
||||
fail: (err) => reject(err),
|
||||
});
|
||||
});
|
||||
}),
|
||||
}));
|
||||
|
||||
const drawCard = async () => {
|
||||
const query = Taro.createSelectorQuery();
|
||||
query
|
||||
.select("#cardCanvas")
|
||||
.fields({ node: true, size: true })
|
||||
.exec(async (res) => {
|
||||
const canvas = res[0].node;
|
||||
const ctx = canvas.getContext("2d");
|
||||
const dpr = Taro.getSystemInfoSync().pixelRatio;
|
||||
const width = 600; // px
|
||||
const height = 1000;
|
||||
|
||||
canvas.width = width * dpr;
|
||||
canvas.height = height * dpr;
|
||||
ctx.scale(dpr, dpr);
|
||||
|
||||
// 背景卡片
|
||||
// roundRect(ctx, 0, 0, width, height, 20, "#fff");
|
||||
|
||||
// 整体背景
|
||||
roundRectGradient(ctx, 0, 0, width, height, 24, "#BFFFEF", "#F2FFFC");
|
||||
|
||||
// 顶部图片
|
||||
const img = await loadImage(canvas, mainCoursal);
|
||||
// roundRect(ctx, 20, 20, width - 40, width - 40, 20, "#fff");
|
||||
await drawCoverImage(
|
||||
ctx,
|
||||
mainCoursal,
|
||||
img,
|
||||
10,
|
||||
10,
|
||||
width - 20,
|
||||
width - 20,
|
||||
20
|
||||
);
|
||||
|
||||
// 标签
|
||||
let left = drawTag(ctx, playType, 18, 18);
|
||||
drawTag(ctx, ntrp, left + 4, 18);
|
||||
|
||||
let top = width - 10;
|
||||
left = 16;
|
||||
|
||||
top += 16;
|
||||
|
||||
// 用户头像(圆形)
|
||||
const avatar = await loadImage(canvas, avatarUrl);
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.arc(left + 30, top + 30, 30, 0, Math.PI * 2);
|
||||
ctx.clip();
|
||||
ctx.drawImage(avatar, left, top, 60, 60);
|
||||
ctx.restore();
|
||||
|
||||
// 头像右边 6
|
||||
left += 66;
|
||||
|
||||
top += 40;
|
||||
|
||||
// 用户名 + 邀请
|
||||
ctx.fillStyle = "#333";
|
||||
ctx.font = "bold 28px sans-serif";
|
||||
// ctx.fillText("华巴轮卡 邀你加入球局", 100, 370);
|
||||
const nickNameText = `${nickname} 邀你加入`;
|
||||
ctx.fillText(nickNameText, left, top);
|
||||
let textW = ctx.measureText(nickNameText).width;
|
||||
left += textW;
|
||||
ctx.fillStyle = "#00B578";
|
||||
ctx.fillText("球局", left, top);
|
||||
const ringImg = await loadImage(canvas, ringUrl);
|
||||
ctx.drawImage(ringImg, left - 10, top - 30, 80, 36);
|
||||
|
||||
left = 16;
|
||||
top += 60;
|
||||
|
||||
// 活动标题
|
||||
ctx.fillStyle = "#333";
|
||||
ctx.font = "bold 34px sans-serif";
|
||||
let r = drawTextWrap(ctx, title, left, top, width - 32, 40);
|
||||
|
||||
top = r.top + 40;
|
||||
left = 16;
|
||||
const dateImg = await loadImage(canvas, dateIcon);
|
||||
await drawCoverImage(ctx, dateIcon, dateImg, left, top, 40, 40, 8);
|
||||
|
||||
left += 40 + 8;
|
||||
|
||||
top += 30;
|
||||
|
||||
// 时间
|
||||
ctx.font = "26px sans-serif";
|
||||
ctx.fillStyle = "#00B578";
|
||||
ctx.fillText(date, left, top);
|
||||
textW = ctx.measureText(date).width;
|
||||
left += 8 + textW;
|
||||
ctx.fillStyle = "#333";
|
||||
ctx.fillText(time, left, top);
|
||||
|
||||
left = 16;
|
||||
top += 24;
|
||||
const mapImg = await loadImage(canvas, mapIcon);
|
||||
await drawCoverImage(ctx, dateIcon, mapImg, left, top, 40, 40, 8);
|
||||
|
||||
left += 40 + 8;
|
||||
top += 30;
|
||||
|
||||
// 地址
|
||||
ctx.fillStyle = "#666";
|
||||
ctx.font = "26px sans-serif";
|
||||
r = drawTextWrap(ctx, locationName, left, top, width - 32 - left, 34);
|
||||
|
||||
left = 16;
|
||||
top = r.top + 60;
|
||||
|
||||
const logoWh = await getImageWh(logoText);
|
||||
console.log(logoWh);
|
||||
const logoTextImg = await loadImage(canvas, logoText);
|
||||
ctx.drawImage(
|
||||
logoTextImg,
|
||||
left,
|
||||
top,
|
||||
400,
|
||||
// 56
|
||||
400 / (logoWh.width / logoWh.height)
|
||||
);
|
||||
|
||||
const qrImg = await loadImage(canvas, qrCodeUrl);
|
||||
ctx.drawImage(qrImg, width - 12 - 150, top - 50, 160, 160);
|
||||
|
||||
left = 16;
|
||||
top += 400 / (logoWh.width / logoWh.height) + 30;
|
||||
|
||||
// 底部文字
|
||||
ctx.fillStyle = "#333";
|
||||
ctx.font = "20px sans-serif";
|
||||
ctx.fillText("长按识别二维码,快来加入,有你就有场!", left, top);
|
||||
|
||||
// 小程序码
|
||||
// const qrcode = await loadImage(canvas, "小程序码路径");
|
||||
// ctx.drawImage(qrcode, 480, 880, 100, 100);
|
||||
|
||||
// 导出图片
|
||||
// Taro.canvasToTempFilePath({
|
||||
// canvas,
|
||||
// success: (res) => {
|
||||
// console.log("导出路径", res.tempFilePath);
|
||||
// },
|
||||
// fail: (err) => console.error(err),
|
||||
// });
|
||||
});
|
||||
};
|
||||
|
||||
const roundRectGradient = (ctx, x, y, w, h, r, color1, color2) => {
|
||||
const gradient = ctx.createLinearGradient(x, y, x, y + h);
|
||||
gradient.addColorStop(0, color1);
|
||||
gradient.addColorStop(1, color2);
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x + r, y);
|
||||
ctx.lineTo(x + w - r, y);
|
||||
ctx.arcTo(x + w, y, x + w, y + r, r);
|
||||
ctx.lineTo(x + w, y + h - r);
|
||||
ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
|
||||
ctx.lineTo(x + r, y + h);
|
||||
ctx.arcTo(x, y + h, x, y + h - r, r);
|
||||
ctx.lineTo(x, y + r);
|
||||
ctx.arcTo(x, y, x + r, y, r);
|
||||
ctx.closePath();
|
||||
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.fill();
|
||||
};
|
||||
|
||||
const drawCoverImage = async (ctx, src, img, x, y, w, h, r = 0) => {
|
||||
const { width, height } = await getImageWh(src);
|
||||
const imgW = width;
|
||||
const imgH = height;
|
||||
|
||||
// 计算缩放比例,取较大值,保证完全覆盖
|
||||
const scale = Math.max(w / imgW, h / imgH);
|
||||
|
||||
// 缩放后的宽高
|
||||
const newW = imgW * scale;
|
||||
const newH = imgH * scale;
|
||||
|
||||
// 居中偏移
|
||||
const offsetX = x + (w - newW) / 2;
|
||||
const offsetY = y + (h - newH) / 2;
|
||||
|
||||
ctx.save();
|
||||
|
||||
// 如果需要圆角
|
||||
if (r > 0) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x + r, y);
|
||||
ctx.lineTo(x + w - r, y);
|
||||
ctx.arcTo(x + w, y, x + w, y + r, r);
|
||||
ctx.lineTo(x + w, y + h - r);
|
||||
ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
|
||||
ctx.lineTo(x + r, y + h);
|
||||
ctx.arcTo(x, y + h, x, y + h - r, r);
|
||||
ctx.lineTo(x, y + r);
|
||||
ctx.arcTo(x, y, x + r, y, r);
|
||||
ctx.closePath();
|
||||
ctx.clip();
|
||||
}
|
||||
|
||||
// 绘制图片 (cover 效果)
|
||||
ctx.drawImage(img, offsetX, offsetY, newW, newH);
|
||||
|
||||
ctx.restore();
|
||||
};
|
||||
|
||||
/** 加载图片 */
|
||||
const loadImage = (canvas, src) => {
|
||||
return new Promise((resolve) => {
|
||||
const img = canvas.createImage();
|
||||
img.onload = () => resolve(img);
|
||||
img.src = src;
|
||||
});
|
||||
};
|
||||
|
||||
/** 圆角矩形 */
|
||||
const roundRect = (ctx, x, y, w, h, r, fillStyle) => {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x + r, y);
|
||||
ctx.lineTo(x + w - r, y);
|
||||
ctx.arcTo(x + w, y, x + w, y + r, r);
|
||||
ctx.lineTo(x + w, y + h - r);
|
||||
ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
|
||||
ctx.lineTo(x + r, y + h);
|
||||
ctx.arcTo(x, y + h, x, y + h - r, r);
|
||||
ctx.lineTo(x, y + r);
|
||||
ctx.arcTo(x, y, x + r, y, r);
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = fillStyle;
|
||||
ctx.fill();
|
||||
};
|
||||
|
||||
/** 绘制标签 */
|
||||
const drawTag = (ctx, text, x, y) => {
|
||||
ctx.font = "22px sans-serif";
|
||||
const padding = 12;
|
||||
const textWidth = ctx.measureText(text).width;
|
||||
roundRect(ctx, x, y, textWidth + padding * 2, 40, 20, "#fff");
|
||||
ctx.fillStyle = "#333";
|
||||
ctx.fillText(text, x + padding, y + 28);
|
||||
return x + textWidth + padding * 2;
|
||||
};
|
||||
|
||||
/** 文本换行 */
|
||||
const drawTextWrap = (ctx, text, x, y, maxWidth, lineHeight) => {
|
||||
let line = "";
|
||||
const lines = [];
|
||||
for (let char of text) {
|
||||
const testLine = line + char;
|
||||
if (ctx.measureText(testLine).width > maxWidth) {
|
||||
lines.push(line);
|
||||
line = char;
|
||||
} else {
|
||||
line = testLine;
|
||||
}
|
||||
}
|
||||
if (line) lines.push(line);
|
||||
lines.forEach((l, i) => {
|
||||
ctx.fillText(l, x, y + i * lineHeight);
|
||||
});
|
||||
const lastLineText = lines.at(-1);
|
||||
return {
|
||||
left: x + ctx.measureText(lastLineText),
|
||||
top: y + (lines.length - 1) * lineHeight,
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<Canvas
|
||||
type="2d"
|
||||
id="cardCanvas"
|
||||
style={{
|
||||
width: "600rpx",
|
||||
height: "1000rpx",
|
||||
// position: "absolute",
|
||||
// left: "-9999px",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default forwardRef(Poster);
|
||||
@@ -1,4 +1,4 @@
|
||||
@use '~@/scss/themeColor.scss' as theme;
|
||||
@use "~@/scss/themeColor.scss" as theme;
|
||||
|
||||
.upload-source-popup-text {
|
||||
width: 100%;
|
||||
@@ -131,14 +131,28 @@
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
|
||||
.upload-popup-footer-cancel, .upload-popup-footer-confirm {
|
||||
font-feature-settings: 'liga' off, 'clig' off;
|
||||
.upload-popup-footer-cancel,
|
||||
.upload-popup-footer-confirm {
|
||||
font-feature-settings: "liga" off, "clig" off;
|
||||
font-family: "PingFang SC";
|
||||
box-sizing: border-box;
|
||||
height: 44px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||
flex: 1;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
color: #000;
|
||||
font-feature-settings: "liga" off, "clig" off;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.23px;
|
||||
}
|
||||
|
||||
.upload-popup-footer-cancel {
|
||||
@@ -154,4 +168,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import GeneralNavbar from "./GeneralNavbar";
|
||||
import RadarChart from './Radar'
|
||||
import EmptyState from './EmptyState';
|
||||
import NTRPTestEntryCard from './NTRPTestEntryCard'
|
||||
import Poster from './Poster'
|
||||
|
||||
export {
|
||||
ActivityTypeSwitch,
|
||||
@@ -55,4 +56,5 @@ export {
|
||||
RadarChart,
|
||||
EmptyState,
|
||||
NTRPTestEntryCard,
|
||||
Poster,
|
||||
};
|
||||
|
||||
@@ -37,7 +37,6 @@ export default {
|
||||
ICON_CIRCLE_SELECT_ARROW: require('@/static/publishBall/icon-circle-select-arrow.svg'),
|
||||
ICON_LOGO: require('@/static/logo.svg'),
|
||||
ICON_CHANGE: require('@/static/list/icon-change.svg'),
|
||||
ICON_WEATHER_SUN: require('@/static/detail/icon-weather-sun.svg'),
|
||||
ICON_DETAIL_MAP: require('@/static/detail/icon-map.svg'),
|
||||
ICON_DETAIL_ARROW_RIGHT: require('@/static/detail/icon-arrow-right.svg'),
|
||||
ICON_DETAIL_NOTICE: require('@/static/detail/icon-notice.svg'),
|
||||
|
||||
9
src/game_pages/detail/config.ts
Normal file
9
src/game_pages/detail/config.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export const DayOfWeekMap = new Map([
|
||||
[0, "周日"],
|
||||
[1, "周一"],
|
||||
[2, "周二"],
|
||||
[3, "周三"],
|
||||
[4, "周四"],
|
||||
[5, "周五"],
|
||||
[6, "周六"],
|
||||
]);
|
||||
@@ -2,4 +2,5 @@ export default definePageConfig({
|
||||
navigationBarTitleText: '球局详情',
|
||||
navigationStyle: 'custom',
|
||||
enableShareAppMessage: true,
|
||||
enableShareTimeline: true,
|
||||
})
|
||||
|
||||
@@ -6,7 +6,6 @@ import React, {
|
||||
forwardRef,
|
||||
} from "react";
|
||||
import { View, Text, Image, Map, ScrollView, Button } from "@tarojs/components";
|
||||
// import { Avatar } from "@nutui/nutui-react-taro";
|
||||
import Taro, {
|
||||
useRouter,
|
||||
useShareAppMessage,
|
||||
@@ -23,7 +22,9 @@ import {
|
||||
NTRPEvaluatePopup,
|
||||
GameManagePopup,
|
||||
Comments,
|
||||
Poster,
|
||||
} from "@/components";
|
||||
import { generateShareImage, generatePosterImage } from "@/utils";
|
||||
import DetailService, {
|
||||
MATCH_STATUS,
|
||||
IsSubstituteSupported,
|
||||
@@ -34,6 +35,12 @@ import { getCurrentLocation, calculateDistance } from "@/utils/locationUtils";
|
||||
import { useUserInfo, useUserActions } from "@/store/userStore";
|
||||
import { EvaluateCallback, EvaluateScene } from "@/store/evaluateStore";
|
||||
import img from "@/config/images";
|
||||
import DownloadIcon from "@/static/detail/download_icon.svg";
|
||||
import WechatLogo from "@/static/detail/wechat_icon.svg";
|
||||
import WechatTimeline from "@/static/detail/wechat_timeline.svg";
|
||||
import LinkIcon from "@/static/detail/link.svg";
|
||||
import CrossIcon from "@/static/detail/cross.svg";
|
||||
import { DayOfWeekMap } from "./config";
|
||||
import styles from "./style.module.scss";
|
||||
import "./index.scss";
|
||||
|
||||
@@ -172,82 +179,281 @@ function Coursel(props) {
|
||||
);
|
||||
}
|
||||
|
||||
// 分享弹窗
|
||||
const SharePopup = forwardRef(
|
||||
({ id, from }: { id: string; from: string }, ref) => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
show: () => {
|
||||
setVisible(true);
|
||||
},
|
||||
}));
|
||||
|
||||
useShareAppMessage((res) => {
|
||||
console.log(res, "res");
|
||||
return {
|
||||
title: "分享",
|
||||
imageUrl: "https://img.yzcdn.cn/vant/cat.jpeg",
|
||||
path: `/game_pages/detail/index?id=${id}&from=share`,
|
||||
};
|
||||
});
|
||||
|
||||
// function handleShareToWechatMoments() {
|
||||
// useShareTimeline(() => {
|
||||
// return {
|
||||
// title: '分享',
|
||||
// path: `/game_pages/detail/index?id=${id}&from=share`,
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
function handleSaveToLocal() {
|
||||
Taro.showToast({ title: "not yet", icon: "error" });
|
||||
return;
|
||||
Taro.saveImageToPhotosAlbum({
|
||||
filePath: "",
|
||||
success: () => {
|
||||
Taro.showToast({ title: "保存成功", icon: "success" });
|
||||
},
|
||||
fail: () => {
|
||||
Taro.showToast({ title: "保存失败", icon: "none" });
|
||||
},
|
||||
const PosterPopup = forwardRef((props, ref) => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [posterData, setPosterData] = useState();
|
||||
const posterRef = useRef();
|
||||
useImperativeHandle(ref, () => ({
|
||||
show: (detail, user) => {
|
||||
setVisible(true);
|
||||
const {
|
||||
play_type,
|
||||
skill_level_max,
|
||||
skill_level_min,
|
||||
image_list,
|
||||
title,
|
||||
start_time,
|
||||
end_time,
|
||||
location_name,
|
||||
} = detail;
|
||||
const { avatar_url, nickname } = user;
|
||||
const startTime = dayjs(start_time);
|
||||
const endTime = dayjs(end_time);
|
||||
const dayofWeek = DayOfWeekMap.get(startTime.day());
|
||||
const gameLength = `${endTime.diff(startTime, "hour")}小时`;
|
||||
setPosterData({
|
||||
playType: play_type,
|
||||
ntrp: `NTRP ${genNTRPRequirementText(
|
||||
skill_level_min,
|
||||
skill_level_max
|
||||
)}`,
|
||||
mainCoursal:
|
||||
image_list[0] ||
|
||||
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/0621b8cf-f7d6-43ad-b852-7dc39f29a782.png",
|
||||
nickname,
|
||||
avatarUrl: avatar_url,
|
||||
title,
|
||||
locationName: location_name,
|
||||
date: `${startTime.format("M月D日")} (${dayofWeek})`,
|
||||
time: `${startTime.format("ah")}点 ${gameLength}`,
|
||||
});
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
return (
|
||||
useShareAppMessage(async () => {
|
||||
const tempFilePath = await posterRef.current.generateImage();
|
||||
return {
|
||||
// title: detail.title,
|
||||
imageUrl: tempFilePath,
|
||||
path: `/game_pages/detail/index?id=${props.id}&from=share`,
|
||||
};
|
||||
});
|
||||
|
||||
useShareTimeline(async () => {
|
||||
const tempFilePath = await posterRef.current.generateImage();
|
||||
return {
|
||||
title: "分享",
|
||||
imageUrl: tempFilePath,
|
||||
path: `/game_pages/detail/index?id=${props.id}&from=share`,
|
||||
};
|
||||
});
|
||||
|
||||
function onClose() {
|
||||
setVisible(false);
|
||||
setPosterData(undefined);
|
||||
Taro.updateShareMenu({
|
||||
isUpdatableMessage: true, // 是否是动态消息(需要服务端配置过模版)
|
||||
});
|
||||
}
|
||||
|
||||
async function handleShare() {
|
||||
const tempFilePath = await posterRef.current.generateImage();
|
||||
Taro.showShareImageMenu({
|
||||
path: tempFilePath,
|
||||
});
|
||||
}
|
||||
return (
|
||||
visible && (
|
||||
<CommonPopup
|
||||
title="分享"
|
||||
title="分享至"
|
||||
visible={visible}
|
||||
onClose={() => {
|
||||
setVisible(false);
|
||||
}}
|
||||
onClose={onClose}
|
||||
showHeader={false}
|
||||
position="center"
|
||||
hideFooter
|
||||
enableDragToClose={false}
|
||||
style={{ minHeight: "100px" }}
|
||||
zIndex={2001}
|
||||
>
|
||||
<View className={styles.shareContainer}>
|
||||
<View catchMove className={styles.title}>
|
||||
分享卡片
|
||||
<View className={styles.posterContainer}>
|
||||
<View className={styles.posterWrap}>
|
||||
{posterData && <Poster ref={posterRef} data={posterData} />}
|
||||
</View>
|
||||
<View className={styles.shareItems}>
|
||||
<Button
|
||||
className={classnames(styles.button, styles.share)}
|
||||
openType="share"
|
||||
>
|
||||
分享到聊天
|
||||
<View className={styles.sharePoster}>
|
||||
<Button className={styles.shareItem} plain={true}>
|
||||
<View className={styles.icon}>
|
||||
<Image className={styles.download} src={DownloadIcon} />
|
||||
</View>
|
||||
<Text>保存至手机</Text>
|
||||
</Button>
|
||||
<Button
|
||||
className={classnames(styles.button, styles.save)}
|
||||
onClick={handleSaveToLocal}
|
||||
className={styles.shareItem}
|
||||
plain={true}
|
||||
onClick={handleShare}
|
||||
>
|
||||
保存到本地
|
||||
<View className={classnames(styles.icon, styles.wechatIcon)}>
|
||||
<Image className={styles.wechat} src={WechatLogo} />
|
||||
</View>
|
||||
<Text>微信好友</Text>
|
||||
</Button>
|
||||
<Button className={styles.shareItem} plain={true} openType="share">
|
||||
<View className={styles.icon}>
|
||||
<Image className={styles.timeline} src={WechatTimeline} />
|
||||
</View>
|
||||
<Text>朋友圈</Text>
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
</CommonPopup>
|
||||
);
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
// 分享弹窗
|
||||
const SharePopup = forwardRef(({ id, from, detail, userInfo }, ref) => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const posterRef = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
changeMessageType();
|
||||
}, []);
|
||||
|
||||
async function changeMessageType() {
|
||||
try {
|
||||
const res = await DetailService.getActivityId({
|
||||
business_id: id,
|
||||
business_type: "game",
|
||||
is_private: false,
|
||||
});
|
||||
if (res.code === 0) {
|
||||
Taro.updateShareMenu({
|
||||
withShareTicket: false, // 是否需要返回 shareTicket
|
||||
isUpdatableMessage: true, // 是否是动态消息(需要服务端配置过模版)
|
||||
activityId: res.data.activity_id, // 动态消息的活动 id
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
Taro.showToast({ title: e.message, icon: "none" });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
show: () => {
|
||||
setVisible(true);
|
||||
},
|
||||
}));
|
||||
|
||||
useShareAppMessage(async (res) => {
|
||||
const {
|
||||
play_type,
|
||||
skill_level_max,
|
||||
skill_level_min,
|
||||
start_time,
|
||||
end_time,
|
||||
location_name,
|
||||
venue_image_list,
|
||||
} = detail || {};
|
||||
const startTime = dayjs(start_time);
|
||||
const endTime = dayjs(end_time);
|
||||
const dayofWeek = DayOfWeekMap.get(startTime.day());
|
||||
const gameLength = `${endTime.diff(startTime, "hour")}小时`;
|
||||
const url = await generateShareImage({
|
||||
userAvatar: userInfo.avatar_url,
|
||||
userNickname: userInfo.nickname,
|
||||
gameType: play_type,
|
||||
skillLevel: `NTRP ${genNTRPRequirementText(
|
||||
skill_level_min,
|
||||
skill_level_max
|
||||
)}`,
|
||||
gameDate: `${startTime.format("M月D日")} (${dayofWeek})`,
|
||||
gameTime: `${startTime.format("ah")}点 ${gameLength}`,
|
||||
venueName: location_name,
|
||||
venueImages: venue_image_list ? venue_image_list.map((c) => c.url) : [],
|
||||
});
|
||||
// console.log(res, "res");
|
||||
return {
|
||||
title: detail.title,
|
||||
imageUrl: url || "https://img.yzcdn.cn/vant/cat.jpeg",
|
||||
path: `/game_pages/detail/index?id=${id}&from=share`,
|
||||
};
|
||||
});
|
||||
|
||||
async function handlePost() {
|
||||
const {
|
||||
play_type,
|
||||
skill_level_max,
|
||||
skill_level_min,
|
||||
start_time,
|
||||
end_time,
|
||||
location_name,
|
||||
image_list,
|
||||
title,
|
||||
} = detail || {};
|
||||
const { avatar_url, nickname } = userInfo;
|
||||
const startTime = dayjs(start_time);
|
||||
const endTime = dayjs(end_time);
|
||||
const dayofWeek = DayOfWeekMap.get(startTime.day());
|
||||
const gameLength = `${endTime.diff(startTime, "hour")}小时`;
|
||||
Taro.showLoading({ title: "生成中..." });
|
||||
const url = await generatePosterImage({
|
||||
playType: play_type,
|
||||
ntrp: `NTRP ${genNTRPRequirementText(skill_level_min, skill_level_max)}`,
|
||||
mainCoursal:
|
||||
image_list[0] ||
|
||||
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/0621b8cf-f7d6-43ad-b852-7dc39f29a782.png",
|
||||
nickname,
|
||||
avatarUrl: avatar_url,
|
||||
title,
|
||||
locationName: location_name,
|
||||
date: `${startTime.format("M月D日")} (${dayofWeek})`,
|
||||
time: `${startTime.format("ah")}点 ${gameLength}`,
|
||||
});
|
||||
Taro.hideLoading();
|
||||
setVisible(false);
|
||||
Taro.showShareImageMenu({
|
||||
path: url,
|
||||
});
|
||||
}
|
||||
|
||||
function onClose() {
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<CommonPopup
|
||||
title="分享至"
|
||||
visible={visible}
|
||||
onClose={onClose}
|
||||
showHeader={false}
|
||||
hideFooter
|
||||
enableDragToClose={false}
|
||||
style={{ minHeight: "100px" }}
|
||||
zIndex={1000}
|
||||
>
|
||||
<View className={styles.shareContainer}>
|
||||
<View catchMove className={styles.title}>
|
||||
<Text>分享至</Text>
|
||||
<View className={styles.closeIconWrap} onClick={onClose}>
|
||||
<Image className={styles.closeIcon} src={CrossIcon} />
|
||||
</View>
|
||||
</View>
|
||||
<View className={styles.shareItems}>
|
||||
<Button className={styles.button} openType="share">
|
||||
<View className={classnames(styles.icon, styles.wechatIcon)}>
|
||||
<Image className={styles.wechat} src={WechatLogo} />
|
||||
</View>
|
||||
<Text>微信好友</Text>
|
||||
</Button>
|
||||
<Button className={styles.button} onClick={handlePost}>
|
||||
<View className={styles.icon}>
|
||||
<Image className={styles.download} src={DownloadIcon} />
|
||||
</View>
|
||||
<Text>生成分享图</Text>
|
||||
</Button>
|
||||
<Button className={styles.button}>
|
||||
<View className={styles.icon}>
|
||||
<Image className={styles.linkIcon} src={LinkIcon} />
|
||||
</View>
|
||||
<Text>复制链接</Text>
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
</CommonPopup>
|
||||
{/* <PosterPopup ref={posterRef} id={detail.id} /> */}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
function navto(url) {
|
||||
Taro.navigateTo({
|
||||
@@ -336,8 +542,8 @@ function StickyButton(props) {
|
||||
return;
|
||||
}
|
||||
const displayPrice = is_organizer ? 0 : price;
|
||||
// user_action_status.can_assess = true;
|
||||
// user_action_status.can_join = false;
|
||||
user_action_status.can_assess = true;
|
||||
user_action_status.can_join = false;
|
||||
// console.log(user_action_status, "user_action");
|
||||
const {
|
||||
can_assess,
|
||||
@@ -1327,6 +1533,8 @@ function Index() {
|
||||
ref={sharePopupRef}
|
||||
id={id as string}
|
||||
from={from as string}
|
||||
detail={detail}
|
||||
userInfo={userInfo}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -2,23 +2,173 @@
|
||||
.title {
|
||||
padding: 20px;
|
||||
color: #000;
|
||||
text-align: center;
|
||||
// text-align: center;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 18px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 28px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.closeIconWrap {
|
||||
display: flex;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||
background: #fff;
|
||||
box-shadow: 0 4px 36px 0 rgba(0, 0, 0, 0.06);
|
||||
|
||||
.closeIcon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.shareItems {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
justify-content: space-around;
|
||||
padding-bottom: 60px;
|
||||
|
||||
.button {
|
||||
width: 140px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
font-feature-settings: "liga" off, "clig" off;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: normal;
|
||||
|
||||
background-color: #fff;
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
line-height: normal;
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
|
||||
&:after {
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 50%;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||
box-shadow: 0 8px 64px 0 rgba(0, 0, 0, 0.1);
|
||||
|
||||
&.wechatIcon {
|
||||
background-color: #07c160;
|
||||
}
|
||||
.download {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
.wechat {
|
||||
width: 36px;
|
||||
height: 30px;
|
||||
}
|
||||
.linkIcon {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.posterContainer {
|
||||
background: linear-gradient(180deg, #fff 0%, #fafafa 100%), #fff;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.posterWrap {
|
||||
border-radius: 19.067px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||
background: linear-gradient(180deg, #bfffef 0%, #f2fffc 100%), #fff;
|
||||
box-shadow: 0 6.933px 55.467px 0 rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.sharePoster {
|
||||
margin-top: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
|
||||
.shareItem {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
font-feature-settings: "liga" off, "clig" off;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: normal;
|
||||
|
||||
background-color: #fff;
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
line-height: normal;
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
|
||||
&:after {
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 50%;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||
box-shadow: 0 8px 64px 0 rgba(0, 0, 0, 0.1);
|
||||
|
||||
&.wechatIcon {
|
||||
background-color: #07c160;
|
||||
}
|
||||
.download {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
.wechat {
|
||||
width: 36px;
|
||||
height: 30px;
|
||||
}
|
||||
.timeline {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@use '~@/scss/images.scss' as img;
|
||||
@use "~@/scss/images.scss" as img;
|
||||
|
||||
.message-container {
|
||||
width: 100%;
|
||||
@@ -6,15 +6,16 @@
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #FFFFFF;
|
||||
background: #ffffff;
|
||||
|
||||
// 顶部导航栏
|
||||
.navbar {
|
||||
height: 100px;
|
||||
background: #FFFFFF;
|
||||
height: 56px;
|
||||
background: #ffffff;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.06);
|
||||
|
||||
.navbar-content {
|
||||
height: 56px;
|
||||
@@ -34,7 +35,7 @@
|
||||
}
|
||||
|
||||
.navbar-title {
|
||||
font-family: 'PingFang SC';
|
||||
font-family: "PingFang SC";
|
||||
font-weight: 600;
|
||||
font-size: 20px;
|
||||
line-height: 1.4;
|
||||
@@ -51,13 +52,27 @@
|
||||
align-items: stretch;
|
||||
gap: 20px;
|
||||
padding: 6px 24px;
|
||||
background: #FFFFFF;
|
||||
background: #ffffff;
|
||||
|
||||
.tab-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
// 消息列表
|
||||
.message-list {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
// margin-bottom:100px;
|
||||
background-color: none !important;
|
||||
|
||||
.message-list-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 12px 12px 112px;
|
||||
gap: 8px;
|
||||
padding: 12px 15px;
|
||||
flex: 1;
|
||||
@@ -72,7 +87,7 @@
|
||||
}
|
||||
|
||||
.tab-text {
|
||||
font-family: 'PingFang SC';
|
||||
font-family: "PingFang SC";
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
@@ -98,7 +113,7 @@
|
||||
.message-scroll {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
background: #FFFFFF;
|
||||
background: #ffffff;
|
||||
|
||||
.message-cards {
|
||||
padding: 12px;
|
||||
@@ -110,7 +125,7 @@
|
||||
|
||||
// 系统消息卡片
|
||||
.message-card {
|
||||
background: #FFFFFF;
|
||||
background: #ffffff;
|
||||
border: 0.5px solid rgba(0, 0, 0, 0.08);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0px 4px 36px 0px rgba(0, 0, 0, 0.06);
|
||||
@@ -126,7 +141,7 @@
|
||||
padding: 12px 15px 0;
|
||||
|
||||
.card-title {
|
||||
font-family: 'PingFang SC';
|
||||
font-family: "PingFang SC";
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
@@ -141,7 +156,7 @@
|
||||
gap: 2px;
|
||||
|
||||
.card-time {
|
||||
font-family: 'PingFang SC';
|
||||
font-family: "PingFang SC";
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
@@ -156,7 +171,7 @@
|
||||
gap: 2px;
|
||||
|
||||
.card-content {
|
||||
font-family: 'PingFang SC';
|
||||
font-family: "PingFang SC";
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 1.43;
|
||||
@@ -182,7 +197,7 @@
|
||||
cursor: pointer;
|
||||
|
||||
.action-text {
|
||||
font-family: 'PingFang SC';
|
||||
font-family: "PingFang SC";
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
line-height: 1.43;
|
||||
@@ -195,7 +210,7 @@
|
||||
position: absolute;
|
||||
left: -16px;
|
||||
top: -9px;
|
||||
width: 14px;;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
}
|
||||
@@ -215,7 +230,7 @@
|
||||
padding: 24px 0 12px;
|
||||
|
||||
.tip-text {
|
||||
font-family: 'PingFang SC';
|
||||
font-family: "PingFang SC";
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 1.71;
|
||||
@@ -232,7 +247,12 @@
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle at 27% 8%, rgba(189, 255, 74, 1) 17%, rgba(149, 242, 62, 1) 54%, rgba(50, 216, 56, 1) 100%);
|
||||
background: radial-gradient(
|
||||
circle at 27% 8%,
|
||||
rgba(189, 255, 74, 1) 17%,
|
||||
rgba(149, 242, 62, 1) 54%,
|
||||
rgba(50, 216, 56, 1) 100%
|
||||
);
|
||||
border: 2px solid rgba(0, 0, 0, 0.06);
|
||||
box-shadow: 0px 4px 48px 0px rgba(0, 0, 0, 0.08);
|
||||
display: flex;
|
||||
@@ -253,31 +273,31 @@
|
||||
|
||||
// 加号图标 - 竖线
|
||||
&::before {
|
||||
content: '';
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 4px;
|
||||
height: 25px;
|
||||
background: #FFFFFF;
|
||||
background: #ffffff;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0px 4px 12px 0px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
// 加号图标 - 横线
|
||||
&::after {
|
||||
content: '';
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 25px;
|
||||
height: 4px;
|
||||
background: #FFFFFF;
|
||||
background: #ffffff;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0px 4px 12px 0px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,53 +1,51 @@
|
||||
// src/scss/images.scss
|
||||
// 暴露公共API (可选)
|
||||
@forward 'sass:map';
|
||||
@forward 'sass:meta';
|
||||
@use 'sass:map';
|
||||
@forward "sass:map";
|
||||
@forward "sass:meta";
|
||||
@use "sass:map";
|
||||
|
||||
// 使用私有变量命名 (前缀加 -)
|
||||
$-static-path: '~@/static/' !default;
|
||||
$-static-path: "~@/static/" !default;
|
||||
|
||||
// 图片映射表
|
||||
$-images: (
|
||||
'icon-upload': '/publishBall/icon-upload.svg',
|
||||
'icon-add': '/publishBall/icon-add.svg',
|
||||
'icon-location': '/publishBall/icon-location.svg',
|
||||
'icon-gameplay': '/publishBall/icon-gameplay.svg',
|
||||
'icon-personal': '/publishBall/icon-personal.svg',
|
||||
'icon-changda': '/publishBall/icon-changda.svg',
|
||||
'icon-cost': '/publishBall/icon-cost.svg',
|
||||
'icon-remove': '/publishBall/icon-remove.svg',
|
||||
'icon-arrow-left': '/detail/icon-arrow-left.svg',
|
||||
'icon-logo-go': '/detail/icon-logo-go.svg',
|
||||
'icon-search': '/publishBall/icon-search.svg',
|
||||
'icon-map': '/publishBall/icon-map.svg',
|
||||
'icon-stadium': '/publishBall/icon-stadium.svg',
|
||||
'icon-arrow-small': '/publishBall/icon-arrow-small.svg',
|
||||
'icon-map-search': '/publishBall/icon-map-search.svg',
|
||||
'icon-heartcircle': '/publishBall/icon-heartcircle.png',
|
||||
'icon-copy': '/publishBall/icon-arrow-right.svg',
|
||||
'icon-delete': '/publishBall/icon-delete.svg',
|
||||
'icon-circle-unselect': '/publishBall/icon-circle-unselect.svg',
|
||||
'icon-circle-select-ring': '/publishBall/icon-circle-select-ring.svg',
|
||||
'icon-circle-select-arrow': '/publishBall/icon-circle-select-arrow.svg',
|
||||
'icon-weather-sun': '/detail/icon-weather-sun.svg',
|
||||
'icon-detail-map': '/detail/icon-map.svg',
|
||||
'icon-detail-arrow-right': '/detail/icon-arrow-right.svg',
|
||||
'icon-detail-notice': '/detail/icon-notice.svg',
|
||||
'icon-detail-application-add': '/detail/icon-application-add.svg',
|
||||
'icon-detail-comment': '/detail/icon-comment.svg',
|
||||
'icon-detail-comment-dark': '/detail/icon-comment-dark.svg',
|
||||
'icon-detail-share': '/detail/icon-share-dark.svg',
|
||||
'icon-guide-bar-publish': '/common/guide-bar-publish.svg',
|
||||
'icon-navigator-back': '/common/navigator-back.svg',
|
||||
"icon-upload": "/publishBall/icon-upload.svg",
|
||||
"icon-add": "/publishBall/icon-add.svg",
|
||||
"icon-location": "/publishBall/icon-location.svg",
|
||||
"icon-gameplay": "/publishBall/icon-gameplay.svg",
|
||||
"icon-personal": "/publishBall/icon-personal.svg",
|
||||
"icon-changda": "/publishBall/icon-changda.svg",
|
||||
"icon-cost": "/publishBall/icon-cost.svg",
|
||||
"icon-remove": "/publishBall/icon-remove.svg",
|
||||
"icon-arrow-left": "/detail/icon-arrow-left.svg",
|
||||
"icon-logo-go": "/detail/icon-logo-go.svg",
|
||||
"icon-search": "/publishBall/icon-search.svg",
|
||||
"icon-map": "/publishBall/icon-map.svg",
|
||||
"icon-stadium": "/publishBall/icon-stadium.svg",
|
||||
"icon-arrow-small": "/publishBall/icon-arrow-small.svg",
|
||||
"icon-map-search": "/publishBall/icon-map-search.svg",
|
||||
"icon-heartcircle": "/publishBall/icon-heartcircle.png",
|
||||
"icon-copy": "/publishBall/icon-arrow-right.svg",
|
||||
"icon-delete": "/publishBall/icon-delete.svg",
|
||||
"icon-circle-unselect": "/publishBall/icon-circle-unselect.svg",
|
||||
"icon-circle-select-ring": "/publishBall/icon-circle-select-ring.svg",
|
||||
"icon-circle-select-arrow": "/publishBall/icon-circle-select-arrow.svg",
|
||||
"icon-detail-map": "/detail/icon-map.svg",
|
||||
"icon-detail-arrow-right": "/detail/icon-arrow-right.svg",
|
||||
"icon-detail-notice": "/detail/icon-notice.svg",
|
||||
"icon-detail-application-add": "/detail/icon-application-add.svg",
|
||||
"icon-detail-comment": "/detail/icon-comment.svg",
|
||||
"icon-detail-comment-dark": "/detail/icon-comment-dark.svg",
|
||||
"icon-detail-share": "/detail/icon-share-dark.svg",
|
||||
"icon-guide-bar-publish": "/common/guide-bar-publish.svg",
|
||||
"icon-navigator-back": "/common/navigator-back.svg",
|
||||
) !default;
|
||||
|
||||
// 图片获取函数
|
||||
@function taro-image($name) {
|
||||
@if not map.has-key($-images, $name) {
|
||||
@warn "Image `#{$name}` not found in $images map";
|
||||
@return url($-static-path + 'default.png');
|
||||
@return url($-static-path + "default.png");
|
||||
}
|
||||
@return url($-static-path + map.get($-images, $name));
|
||||
}
|
||||
|
||||
|
||||
@@ -140,6 +140,17 @@ class GameDetailService {
|
||||
showLoading: true,
|
||||
});
|
||||
}
|
||||
|
||||
async getActivityId(req: { business_type: 'game' | 'order' | 'participant', business_id: number, is_private: boolean }): Promise<ApiResponse<{
|
||||
activity_id: string,
|
||||
expiration_time: number,
|
||||
business_type: 'game' | 'order' | 'participant',
|
||||
business_id: number
|
||||
}>> {
|
||||
return httpService.post('/user/create_activity_id', req, {
|
||||
showLoading: false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 导出认证服务实例
|
||||
|
||||
4
src/static/detail/cross.svg
Normal file
4
src/static/detail/cross.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7 7L17 17" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7 17L17 7" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 307 B |
5
src/static/detail/download_icon.svg
Normal file
5
src/static/detail/download_icon.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.5 14.0048V24.5H24.5V14" stroke="black" stroke-width="2.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M19.25 13.417L14 18.667L8.75 13.417" stroke="black" stroke-width="2.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M13.9954 3.5V18.6667" stroke="black" stroke-width="2.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 477 B |
@@ -1,18 +0,0 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_3061_9153)">
|
||||
<path d="M3.81265 3.81287L4.74074 4.74096" stroke="white" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M1.25 10H2.5625" stroke="white" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3.81265 16.1873L4.74074 15.2592" stroke="white" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M16.1873 16.1873L15.2592 15.2592" stroke="white" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M18.7498 10H17.4373" stroke="white" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M16.1873 3.81287L15.2592 4.74096" stroke="white" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10 1.25V2.5625" stroke="white" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10 15C12.7614 15 15 12.7614 15 10C15 7.23858 12.7614 5 10 5C7.23858 5 5 7.23858 5 10C5 12.7614 7.23858 15 10 15Z" fill="white" stroke="white" stroke-width="1.66667" stroke-linejoin="round"/>
|
||||
<path d="M10 18.7501V17.4376" stroke="white" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_3061_9153">
|
||||
<rect width="20" height="20" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
6
src/static/detail/link.svg
Normal file
6
src/static/detail/link.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15.3067 9.55113L9.975 4.21948C8.42258 2.66703 5.87137 2.70122 4.2767 4.29586C2.68206 5.89048 2.64786 8.44175 4.20032 9.99417L8.82922 14.6231" stroke="black" stroke-width="2.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M19.1933 13.4189L23.8222 18.0479C25.3747 19.6004 25.3404 22.1516 23.7458 23.7462C22.1512 25.3409 19.6 25.3751 18.0475 23.8226L12.7158 18.4909" stroke="black" stroke-width="2.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M15.2305 15.2491C16.8251 13.6545 16.8593 11.1033 15.3069 9.55078" stroke="black" stroke-width="2.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12.716 12.7158C11.1214 14.3105 11.0872 16.8617 12.6396 18.4141" stroke="black" stroke-width="2.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 904 B |
4
src/static/detail/wechat_icon.svg
Normal file
4
src/static/detail/wechat_icon.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="30" viewBox="0 0 36 30" fill="none">
|
||||
<path d="M17.3505 9.31185C16.5014 9.31185 15.8113 8.6188 15.8113 7.76072C15.8113 6.90265 16.5014 6.20959 17.3505 6.20959C18.1995 6.20959 18.8896 6.90265 18.8896 7.76072C18.8896 8.6188 18.1995 9.31185 17.3505 9.31185ZM8.67673 9.31185C7.82766 9.31185 7.1376 8.6188 7.1376 7.76072C7.1376 6.90265 7.82766 6.20959 8.67673 6.20959C9.5258 6.20959 10.2159 6.90265 10.2159 7.76072C10.2159 8.6188 9.5258 9.31185 8.67673 9.31185ZM13.0121 0.329102C5.82649 0.329102 0 5.22251 0 11.259C0 14.5533 1.75215 17.5175 4.49738 19.5217C4.71639 19.6807 4.86041 19.9387 4.86041 20.2358C4.86041 20.3318 4.83941 20.4218 4.8154 20.5148C4.59639 21.3399 4.24536 22.657 4.23035 22.72C4.20335 22.825 4.16135 22.93 4.16135 23.041C4.16135 23.281 4.35636 23.479 4.59639 23.479C4.68939 23.479 4.7674 23.443 4.84541 23.398L7.69564 21.7389C7.90866 21.6159 8.13668 21.5379 8.3857 21.5379C8.51771 21.5379 8.64672 21.5589 8.76973 21.5949C10.0988 21.9789 11.533 22.1949 13.0181 22.1949C13.2581 22.1949 13.4951 22.1889 13.7322 22.1769C13.4501 21.3249 13.2941 20.4278 13.2941 19.4977C13.2941 13.9922 18.6076 9.52787 25.1631 9.52787C25.4001 9.52787 25.4601 9.53687 25.6942 9.54587C24.7101 4.32244 19.5046 0.329102 13.0121 0.329102Z" fill="white"/>
|
||||
<path d="M28.7725 17.9434C28.0254 17.9434 27.4194 17.3314 27.4194 16.5783C27.4194 15.8253 28.0254 15.2132 28.7725 15.2132C29.5196 15.2132 30.1256 15.8253 30.1256 16.5783C30.1256 17.3314 29.5196 17.9434 28.7725 17.9434ZM21.5419 17.9434C20.7948 17.9434 20.1888 17.3314 20.1888 16.5783C20.1888 15.8253 20.7948 15.2132 21.5419 15.2132C22.2889 15.2132 22.895 15.8253 22.895 16.5783C22.895 17.3314 22.2889 17.9434 21.5419 17.9434ZM32.2528 26.3772C34.539 24.706 36.0001 22.2368 36.0001 19.4916C36.0001 14.4602 31.1457 10.3828 25.1572 10.3828C19.1687 10.3828 14.3113 14.4602 14.3113 19.4916C14.3113 24.523 19.1657 28.6003 25.1572 28.6003C26.3933 28.6003 27.5904 28.4233 28.6975 28.1023C28.7995 28.0723 28.9045 28.0543 29.0155 28.0543C29.2225 28.0543 29.4115 28.1173 29.5916 28.2223L31.9648 29.6024C32.0308 29.6414 32.0968 29.6714 32.1748 29.6714C32.3758 29.6714 32.5378 29.5094 32.5378 29.3084C32.5378 29.2184 32.5018 29.1284 32.4808 29.0414C32.4658 28.9904 32.1748 27.8893 31.9918 27.2052C31.9708 27.1272 31.9528 27.0522 31.9528 26.9742C31.9528 26.7282 32.0728 26.5122 32.2558 26.3802" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
17
src/static/detail/wechat_timeline.svg
Normal file
17
src/static/detail/wechat_timeline.svg
Normal file
@@ -0,0 +1,17 @@
|
||||
<svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_5089_10854)">
|
||||
<path d="M12.6821 0.59089C14.0145 0.237434 15.3823 0.0595703 16.7511 0.0595703C18.891 0.0595703 20.9721 0.480501 22.9385 1.31274L22.6772 12.8663L12.6821 0.59089Z" fill="#F57659"/>
|
||||
<path d="M2.9751 7.98069C3.6713 6.78428 4.51319 5.68963 5.47929 4.72785C6.99157 3.21547 8.75994 2.04164 10.7382 1.23828L18.7262 9.59159L2.9751 7.98069Z" fill="#F0B360"/>
|
||||
<path d="M1.34058 20.071C0.988181 18.744 0.809326 17.3762 0.809326 15.9999C0.809326 13.8577 1.23128 11.7767 2.06243 9.81445L13.6171 10.0736L1.34058 20.071Z" fill="#EFDE58"/>
|
||||
<path d="M8.73089 29.7752C7.54305 29.0832 6.45061 28.2414 5.48023 27.271C3.96682 25.7608 2.79293 23.9925 1.99072 22.0121L10.3439 14.0273L8.73089 29.7752Z" fill="#BBEF5C"/>
|
||||
<path d="M16.751 31.9403C14.6109 31.9403 12.53 31.5193 10.5635 30.6893L10.8248 19.1357L20.8199 31.4112C19.4854 31.7625 18.1187 31.9403 16.751 31.9403Z" fill="#61F15E"/>
|
||||
<path d="M14.7759 22.4092L30.527 24.0201C29.8362 25.2057 28.9965 26.2982 28.0218 27.2707C26.5115 28.7852 24.7411 29.9591 22.7629 30.7624L14.7759 22.4092Z" fill="#5BE4EE"/>
|
||||
<path d="M19.885 21.928L32.1604 11.9307C32.5128 13.2588 32.6906 14.6276 32.6906 15.9996C32.6906 18.1482 32.2687 20.2272 31.4386 22.1872L19.885 21.928Z" fill="#5C9BF6"/>
|
||||
<path d="M24.7691 2.22656C25.9559 2.91528 27.0505 3.75602 28.0219 4.72858C29.5364 6.23879 30.7103 8.00923 31.5114 9.98748L23.1582 17.9744L24.7691 2.22656Z" fill="#6D58F1"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_5089_10854">
|
||||
<rect width="32" height="32" fill="white" transform="translate(0.75)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
312
src/utils/genPoster.ts
Normal file
312
src/utils/genPoster.ts
Normal file
@@ -0,0 +1,312 @@
|
||||
import Taro from "@tarojs/taro";
|
||||
|
||||
const qrCodeUrl =
|
||||
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/5e013195-fc79-4082-bf06-9aa79aea65ae.png";
|
||||
|
||||
const ringUrl =
|
||||
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/b635164f-ecec-434a-a00b-69614a918f2f.png";
|
||||
|
||||
const dateIcon =
|
||||
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/1b49476e-0eda-42ff-b08c-002ce510df82.jpg";
|
||||
|
||||
const mapIcon =
|
||||
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/06b994fa-9227-4708-8555-8a07af8d0c3b.jpg";
|
||||
|
||||
// const logo = "http://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/fb732da6-11b9-4022-a524-a377b17635eb.jpg"
|
||||
|
||||
const logoText =
|
||||
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/9d8cbc9d-9601-4e2d-ab23-76420a4537d6.png";
|
||||
|
||||
/** 获取图片宽高 */
|
||||
function getImageWh(src: string): Promise<{ width: number; height: number }> {
|
||||
return new Promise((resolve) => {
|
||||
Taro.getImageInfo({
|
||||
src,
|
||||
success: ({ width, height }) => resolve({ width, height }),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** 加载图片 */
|
||||
function loadImage(canvas: any, src: string): Promise<any> {
|
||||
return new Promise((resolve) => {
|
||||
const img = canvas.createImage();
|
||||
img.onload = () => resolve(img);
|
||||
img.src = src;
|
||||
});
|
||||
}
|
||||
|
||||
/** 圆角矩形渐变 */
|
||||
function roundRectGradient(
|
||||
ctx: any,
|
||||
x: number,
|
||||
y: number,
|
||||
w: number,
|
||||
h: number,
|
||||
r: number,
|
||||
color1: string,
|
||||
color2: string
|
||||
) {
|
||||
const gradient = ctx.createLinearGradient(x, y, x, y + h);
|
||||
gradient.addColorStop(0, color1);
|
||||
gradient.addColorStop(1, color2);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x + r, y);
|
||||
ctx.lineTo(x + w - r, y);
|
||||
ctx.arcTo(x + w, y, x + w, y + r, r);
|
||||
ctx.lineTo(x + w, y + h - r);
|
||||
ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
|
||||
ctx.lineTo(x + r, y + h);
|
||||
ctx.arcTo(x, y + h, x, y + h - r, r);
|
||||
ctx.lineTo(x, y + r);
|
||||
ctx.arcTo(x, y, x + r, y, r);
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
/** 绘制 cover 图片(支持圆角) */
|
||||
async function drawCoverImage(
|
||||
ctx: any,
|
||||
canvas: any,
|
||||
src: string,
|
||||
img: any,
|
||||
x: number,
|
||||
y: number,
|
||||
w: number,
|
||||
h: number,
|
||||
r = 0
|
||||
) {
|
||||
const { width, height } = await getImageWh(src);
|
||||
const scale = Math.max(w / width, h / height);
|
||||
const newW = width * scale;
|
||||
const newH = height * scale;
|
||||
const offsetX = x + (w - newW) / 2;
|
||||
const offsetY = y + (h - newH) / 2;
|
||||
|
||||
ctx.save();
|
||||
if (r > 0) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x + r, y);
|
||||
ctx.lineTo(x + w - r, y);
|
||||
ctx.arcTo(x + w, y, x + w, y + r, r);
|
||||
ctx.lineTo(x + w, y + h - r);
|
||||
ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
|
||||
ctx.lineTo(x + r, y + h);
|
||||
ctx.arcTo(x, y + h, x, y + h - r, r);
|
||||
ctx.lineTo(x, y + r);
|
||||
ctx.arcTo(x, y, x + r, y, r);
|
||||
ctx.closePath();
|
||||
ctx.clip();
|
||||
}
|
||||
ctx.drawImage(img, offsetX, offsetY, newW, newH);
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
/** 圆角矩形 */
|
||||
function roundRect(
|
||||
ctx: any,
|
||||
x: number,
|
||||
y: number,
|
||||
w: number,
|
||||
h: number,
|
||||
r: number,
|
||||
fillStyle: string
|
||||
) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x + r, y);
|
||||
ctx.lineTo(x + w - r, y);
|
||||
ctx.arcTo(x + w, y, x + w, y + r, r);
|
||||
ctx.lineTo(x + w, y + h - r);
|
||||
ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
|
||||
ctx.lineTo(x + r, y + h);
|
||||
ctx.arcTo(x, y + h, x, y + h - r, r);
|
||||
ctx.lineTo(x, y + r);
|
||||
ctx.arcTo(x, y, x + r, y, r);
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = fillStyle;
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
/** 绘制标签 */
|
||||
function drawTag(ctx: any, text: string, x: number, y: number) {
|
||||
ctx.font = "22px sans-serif";
|
||||
const padding = 12;
|
||||
const textWidth = ctx.measureText(text).width;
|
||||
roundRect(ctx, x, y, textWidth + padding * 2, 40, 20, "#fff");
|
||||
ctx.fillStyle = "#333";
|
||||
ctx.fillText(text, x + padding, y + 28);
|
||||
return x + textWidth + padding * 2;
|
||||
}
|
||||
|
||||
/** 文本换行 */
|
||||
function drawTextWrap(
|
||||
ctx: any,
|
||||
text: string,
|
||||
x: number,
|
||||
y: number,
|
||||
maxWidth: number,
|
||||
lineHeight: number
|
||||
) {
|
||||
let line = "";
|
||||
const lines = [];
|
||||
for (let char of text) {
|
||||
const testLine = line + char;
|
||||
if (ctx.measureText(testLine).width > maxWidth) {
|
||||
lines.push(line);
|
||||
line = char;
|
||||
} else {
|
||||
line = testLine;
|
||||
}
|
||||
}
|
||||
if (line) lines.push(line);
|
||||
lines.forEach((l, i) => {
|
||||
ctx.fillText(l, x, y + i * lineHeight);
|
||||
});
|
||||
const lastLineText = lines.at(-1);
|
||||
return {
|
||||
left: x + ctx.measureText(lastLineText).width,
|
||||
top: y + (lines.length - 1) * lineHeight,
|
||||
};
|
||||
}
|
||||
|
||||
/** 核心纯函数:生成海报图片 */
|
||||
export async function generatePosterImage(data: any): Promise<string> {
|
||||
console.log("start !!!!");
|
||||
const dpr = Taro.getSystemInfoSync().pixelRatio;
|
||||
const width = 600;
|
||||
const height = 1000;
|
||||
|
||||
const canvas = Taro.createOffscreenCanvas({ type: "2d", width: width * dpr, height: height * dpr });
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.scale(dpr, dpr);
|
||||
|
||||
// 背景渐变
|
||||
roundRectGradient(ctx, 0, 0, width, height, 24, "#BFFFEF", "#F2FFFC");
|
||||
|
||||
// 顶部图片
|
||||
const mainImg = await loadImage(canvas, data.mainCoursal);
|
||||
console.log(222);
|
||||
await drawCoverImage(
|
||||
ctx,
|
||||
canvas,
|
||||
data.mainCoursal,
|
||||
mainImg,
|
||||
10,
|
||||
10,
|
||||
width - 20,
|
||||
width - 20,
|
||||
20
|
||||
);
|
||||
|
||||
// 标签
|
||||
let left = drawTag(ctx, data.playType, 18, 18);
|
||||
drawTag(ctx, data.ntrp, left + 4, 18);
|
||||
|
||||
let top = width - 10 + 16;
|
||||
left = 16;
|
||||
|
||||
// 用户头像
|
||||
const avatarImg = await loadImage(canvas, data.avatarUrl);
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.arc(left + 30, top + 30, 30, 0, Math.PI * 2);
|
||||
ctx.clip();
|
||||
ctx.drawImage(avatarImg, left, top, 60, 60);
|
||||
ctx.restore();
|
||||
|
||||
left += 66;
|
||||
top += 40;
|
||||
|
||||
// 用户名 + 邀请
|
||||
ctx.fillStyle = "#333";
|
||||
ctx.font = "bold 28px sans-serif";
|
||||
const nickNameText = `${data.nickname} 邀你加入`;
|
||||
ctx.fillText(nickNameText, left, top);
|
||||
let textW = ctx.measureText(nickNameText).width;
|
||||
left += textW;
|
||||
ctx.fillStyle = "#00B578";
|
||||
ctx.fillText("球局", left, top);
|
||||
|
||||
const ringImg = await loadImage(canvas, ringUrl);
|
||||
ctx.drawImage(ringImg, left - 10, top - 30, 80, 36);
|
||||
|
||||
left = 16;
|
||||
top += 60;
|
||||
|
||||
// 活动标题
|
||||
ctx.fillStyle = "#333";
|
||||
ctx.font = "bold 34px sans-serif";
|
||||
let r = drawTextWrap(ctx, data.title, left, top, width - 32, 40);
|
||||
|
||||
top = r.top + 40;
|
||||
left = 16;
|
||||
|
||||
const dateImg = await loadImage(canvas, dateIcon);
|
||||
await drawCoverImage(
|
||||
ctx,
|
||||
canvas,
|
||||
dateIcon,
|
||||
dateImg,
|
||||
left,
|
||||
top,
|
||||
40,
|
||||
40,
|
||||
8
|
||||
);
|
||||
|
||||
left += 40 + 8;
|
||||
top += 30;
|
||||
|
||||
ctx.font = "26px sans-serif";
|
||||
ctx.fillStyle = "#00B578";
|
||||
ctx.fillText(data.date, left, top);
|
||||
textW = ctx.measureText(data.date).width;
|
||||
left += 8 + textW;
|
||||
ctx.fillStyle = "#333";
|
||||
ctx.fillText(data.time, left, top);
|
||||
|
||||
left = 16;
|
||||
top += 24;
|
||||
|
||||
const mapImg = await loadImage(canvas, mapIcon);
|
||||
await drawCoverImage(ctx, canvas, mapIcon, mapImg, left, top, 40, 40, 8);
|
||||
|
||||
left += 40 + 8;
|
||||
top += 30;
|
||||
|
||||
ctx.fillStyle = "#666";
|
||||
ctx.font = "26px sans-serif";
|
||||
r = drawTextWrap(ctx, data.locationName, left, top, width - 32 - left, 34);
|
||||
|
||||
left = 16;
|
||||
top = r.top + 60;
|
||||
|
||||
const logoWh = await getImageWh(logoText);
|
||||
const logoTextImg = await loadImage(canvas, logoText);
|
||||
ctx.drawImage(
|
||||
logoTextImg,
|
||||
left,
|
||||
top,
|
||||
400,
|
||||
400 / (logoWh.width / logoWh.height)
|
||||
);
|
||||
|
||||
const qrImg = await loadImage(canvas, qrCodeUrl);
|
||||
ctx.drawImage(qrImg, width - 12 - 150, top - 50, 160, 160);
|
||||
|
||||
left = 16;
|
||||
top += 400 / (logoWh.width / logoWh.height) + 30;
|
||||
|
||||
ctx.fillStyle = "#333";
|
||||
ctx.font = "20px sans-serif";
|
||||
ctx.fillText("长按识别二维码,快来加入,有你就有场!", left, top);
|
||||
|
||||
// 导出图片
|
||||
const { tempFilePath } = await Taro.canvasToTempFilePath({
|
||||
canvas,
|
||||
fileType: 'png',
|
||||
quality: 1,
|
||||
});
|
||||
return tempFilePath;
|
||||
}
|
||||
@@ -7,3 +7,5 @@ export * from "./tokenManager";
|
||||
export * from "./order.pay";
|
||||
export * from './orderActions';
|
||||
export * from './routeUtil';
|
||||
export * from './share'
|
||||
export * from './genPoster'
|
||||
|
||||
71
types/taro-canvas.d.ts
vendored
Normal file
71
types/taro-canvas.d.ts
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
declare module "@tarojs/taro" {
|
||||
interface RenderingContext {
|
||||
// 变换
|
||||
scale(x: number, y: number): void;
|
||||
rotate(angle: number): void;
|
||||
translate(x: number, y: number): void;
|
||||
save(): void;
|
||||
restore(): void;
|
||||
|
||||
// 路径
|
||||
beginPath(): void;
|
||||
closePath(): void;
|
||||
moveTo(x: number, y: number): void;
|
||||
lineTo(x: number, y: number): void;
|
||||
arc(
|
||||
x: number,
|
||||
y: number,
|
||||
radius: number,
|
||||
startAngle: number,
|
||||
endAngle: number,
|
||||
anticlockwise?: boolean
|
||||
): void;
|
||||
arcTo(
|
||||
x1: number,
|
||||
y1: number,
|
||||
x2: number,
|
||||
y2: number,
|
||||
radius: number
|
||||
): void;
|
||||
clip(): void;
|
||||
|
||||
// 绘制
|
||||
drawImage(
|
||||
image: any,
|
||||
dx: number,
|
||||
dy: number,
|
||||
dWidth?: number,
|
||||
dHeight?: number
|
||||
): void;
|
||||
fill(): void;
|
||||
stroke(): void;
|
||||
|
||||
// 样式
|
||||
fillStyle: string | CanvasGradient | CanvasPattern;
|
||||
strokeStyle: string | CanvasGradient | CanvasPattern;
|
||||
font: string;
|
||||
textAlign: CanvasTextAlign;
|
||||
textBaseline: CanvasTextBaseline;
|
||||
|
||||
// 文本
|
||||
fillText(text: string, x: number, y: number, maxWidth?: number): void;
|
||||
strokeText(text: string, x: number, y: number, maxWidth?: number): void;
|
||||
measureText(text: string): TextMetrics;
|
||||
|
||||
// 渐变
|
||||
createLinearGradient(
|
||||
x0: number,
|
||||
y0: number,
|
||||
x1: number,
|
||||
y1: number
|
||||
): CanvasGradient;
|
||||
createRadialGradient(
|
||||
x0: number,
|
||||
y0: number,
|
||||
r0: number,
|
||||
x1: number,
|
||||
y1: number,
|
||||
r1: number
|
||||
): CanvasGradient;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user