diff --git a/src/components/ListCardSkeleton/index.scss b/src/components/ListCardSkeleton/index.scss index e642055..58b8842 100644 --- a/src/components/ListCardSkeleton/index.scss +++ b/src/components/ListCardSkeleton/index.scss @@ -47,7 +47,7 @@ } .location-position { - max-width: 66%; + max-width: 58%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; diff --git a/src/components/ShareCardCanvas/index.tsx b/src/components/ShareCardCanvas/index.tsx new file mode 100644 index 0000000..b8d5838 --- /dev/null +++ b/src/components/ShareCardCanvas/index.tsx @@ -0,0 +1,653 @@ +import React, { useEffect, useState } from 'react' +import { View, Canvas } from '@tarojs/components' +import Taro from '@tarojs/taro' + +// 分享卡片数据接口 +export interface ShareCardData { + // 用户信息 + userAvatar: string // 用户头像URL + userNickname: string // 用户昵称 + + // 球局信息 + gameType: string // 球局类型,如"单打" + skillLevel: string // 技能等级,如"NTRP 2.5 - 3.0" + gameDate: string // 日期,如"6月20日(周五)" + gameTime: string // 时间,如"下午5点 2小时" + venueName: string // 场地名称,如"因乐驰网球俱乐部(嘉定江桥万达店)" + venueImage: string // 场地图片URL + + // 可选信息 + playerImage?: string // 球员图片URL +} + +// 组件Props接口 +export interface ShareCardCanvasProps { + data: ShareCardData + width?: number // 卡片宽度,默认375 + height?: number // 卡片高度,默认500 + onGenerated?: (imagePath: string) => void // 生成完成回调 + className?: string +} + +const ShareCardCanvas: React.FC = ({ + data, + className = '', + onGenerated = () => { } +}) => { + const [tempImagePath, setTempImagePath] = useState('') // 存储Canvas生成的图片路径 + const [isDrawing, setIsDrawing] = useState(false) // 防止重复绘制 + + // 设计稿尺寸 + const designWidth = 500 + const designHeight = 400 + + // 获取屏幕宽度,如果没有传入width则使用屏幕宽度 + const windowWidth = Taro.getSystemInfoSync().windowWidth + + // 2. 计算缩放比例(设备宽度 / 设计稿宽度) + const scale = windowWidth / designWidth + + // 3. 计算实际显示尺寸(按比例缩放) + const canvasWidth = designWidth * scale + const canvasHeight = designHeight * scale + + // 绘制加粗文字(单行) + const drawBoldText = (ctx: any, text: string, x: number, y: number, fontSize: number, color: string, fontFamily?: string) => { + // 设置字体样式 + if (fontFamily) { + try { + // 尝试使用setFont方法(如果支持) + ctx.setFont(`${fontSize}px ${fontFamily}`) + } catch (error) { + // 如果不支持setFont,回退到setFontSize + ctx.setFontSize(fontSize) + } + } else { + ctx.setFontSize(fontSize) + } + + ctx.setFillStyle(color) + ctx.setTextAlign('left') + ctx.setTextBaseline('top') + + // 绘制加粗效果:多次绘制并偏移 + ctx.fillText(text, x, y) + ctx.fillText(text, x + 1, y) + ctx.fillText(text, x, y + 1) + ctx.fillText(text, x + 1, y + 1) + } + + // 绘制文字(支持自动换行)- 微信小程序版本 + const drawText = (ctx: any, text: string, x: number, y: number, maxWidth: number, fontSize: number, color: string, bold: boolean = false, fontFamily?: string) => { + // 设置字体样式 + if (fontFamily) { + try { + // 尝试使用setFont方法(如果支持) + ctx.setFont(`${fontSize}px ${fontFamily}`) + } catch (error) { + // 如果不支持setFont,回退到setFontSize + ctx.setFontSize(fontSize) + } + } else { + ctx.setFontSize(fontSize) + } + + ctx.setFillStyle(color) + ctx.setTextAlign('left') + ctx.setTextBaseline('top') + + const words = text.split('') + let line = '' + let lineY = y + + for (let i = 0; i < words.length; i++) { + const testLine = line + words[i] + // 微信小程序中measureText返回的是对象,需要访问width属性 + const metrics = ctx.measureText(testLine) + const testWidth = metrics.width + + if (testWidth > maxWidth && i > 0) { + if (bold) { + // 绘制加粗效果:多次绘制并偏移 + ctx.fillText(line, x, lineY) + ctx.fillText(line, x + 1, lineY) + ctx.fillText(line, x, lineY + 1) + ctx.fillText(line, x + 1, lineY + 1) + } else { + ctx.fillText(line, x, lineY) + } + line = words[i] + lineY += fontSize * 1.2 + } else { + line = testLine + } + } + + if (bold) { + // 绘制加粗效果:多次绘制并偏移 + ctx.fillText(line, x, lineY) + ctx.fillText(line, x + 1, lineY) + ctx.fillText(line, x, lineY + 1) + ctx.fillText(line, x + 1, lineY + 1) + } else { + ctx.fillText(line, x, lineY) + } + } + + // 绘制圆角矩形函数 + const drawRoundedRect = (ctx: any, x: number, y: number, width: number, height: number, radius: number) => { + 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.closePath(); + ctx.fill(); + } + + // 绘制标签函数(通用) + const drawLabel = (ctx: any, x: number, y: number, width: number, height: number, radius: number, text: string, fontSize: number, textColor: string = '#000000', bgColor: string = '#FFFFFF', borderColor: string = '#E0E0E0') => { + // 绘制背景 + ctx.setFillStyle(bgColor) + drawRoundedRect(ctx, x, y, width, height, radius) + + // 绘制边框 + ctx.setStrokeStyle(borderColor) + ctx.setLineWidth(1 * dpr) + ctx.stroke() + + // 绘制文字 + const textCenterX = x + width / 2 + const textCenterY = y + height / 2 + + ctx.setFillStyle(textColor) + ctx.setTextAlign('center') + ctx.setTextBaseline('middle') + ctx.setFontSize(fontSize) + + ctx.save() + ctx.translate(textCenterX, textCenterY) + ctx.fillText(text, 0, 0) + ctx.restore() + } + + // 加载图片 - 微信小程序版本 + const loadImage = (src: string): Promise => { + return new Promise((resolve, reject) => { + Taro.getImageInfo({ + src: src, + success: (res) => resolve(res.path), + fail: reject + }) + }) + } + + // 绘制SVG路径到Canvas + const drawSVGPathToCanvas = (ctx: any) => { + // 设置绘制样式 + ctx.setStrokeStyle('#48D800'); + ctx.setLineWidth(scale * 3 * dpr); + ctx.setLineCap('round'); + ctx.setLineJoin('round'); + + ctx.save(); + + // 移动到指定位置并缩放 + ctx.translate(scale * 210 * dpr, scale * 90 * dpr); + const scaleValue = 0.8 + ctx.scale(scaleValue, scaleValue); + + // 手动绘制SVG路径(微信小程序不支持Path2D) + ctx.beginPath(); + ctx.moveTo(109.808, 6.10642); + ctx.bezierCurveTo(89.5111, -2.79359, 33.4629, 3.77737, 7.49927, 30.2355); + ctx.bezierCurveTo(4.78286, 33.0044, 1.93351, 36.3827, 1.66282, 41.2355); + ctx.bezierCurveTo(1.21704, 49.1959, 7.56651, 55.1359, 13.1231, 57.7309); + ctx.bezierCurveTo(20.6373, 61.24, 28.4568, 62.6334, 36.2033, 63.2984); + ctx.bezierCurveTo(58.8813, 65.247, 81.6582, 60.9714, 101.719, 48.424); + ctx.bezierCurveTo(105.278, 46.1993, 108.823, 43.6365, 111.378, 39.5712); + ctx.bezierCurveTo(113.934, 35.5058, 115.361, 29.6397, 114.14, 24.1205); + ctx.bezierCurveTo(112.685, 17.5296, 107.961, 13.1328, 103.328, 10.8187); + ctx.bezierCurveTo(94.2761, 6.29607, 84.6677, 7.3453, 75.41, 8.45251); + + // 绘制路径 + ctx.stroke(); + + // 恢复上下文状态 + ctx.restore(); + } + + // 获取 DPR - 使用系统像素比确保高清显示 + // const systemDpr = Taro.getSystemInfoSync().pixelRatio + const dpr = 1 + // Math.min(systemDpr, 3) // 限制最大dpr为3,避免过度放大 + + // 绘制分享卡片 + const drawShareCard = async () => { + // 防止重复绘制 + if (isDrawing) { + console.log('正在绘制中,跳过重复绘制') + return + } + + return new Promise(async (resolve, reject) => { + console.log('开始绘制分享卡片...') + setIsDrawing(true) + + try { + // 在微信小程序中,需要使用Taro.createCanvasContext + const ctx = Taro.createCanvasContext('shareCardCanvas') + console.log('Canvas上下文创建成功:', ctx) + + // 设置Canvas的实际尺寸(使用dpr确保高清显示) + const canvasWidthPx = canvasWidth * dpr + const canvasHeightPx = canvasHeight * dpr + + // 清空画布 + ctx.clearRect(0, 0, canvasWidthPx, canvasHeightPx) + console.log('画布已清空') + + // 如果dpr大于2,进行缩放处理以避免内容过大 + if (dpr > 2) { + const scale = 2 / dpr + ctx.scale(scale, scale) + console.log('应用缩放:', scale) + } + + // 绘制背景 - 渐变色 已完成 + const gradient = ctx.createLinearGradient(0, 0, 0, canvasHeightPx) + gradient.addColorStop(0, '#D8FFE5') + gradient.addColorStop(1, '#F9FFFB') + ctx.setFillStyle(gradient) + ctx.fillRect(0, 0, canvasWidthPx, canvasHeightPx) + console.log('背景绘制完成') + + // 绘制背景条纹 已完成 + ctx.setStrokeStyle('rgba(0, 0, 0, 0.03)') + ctx.setLineWidth(2) + for (let i = 0; i < canvasWidthPx; i += 4) { + ctx.beginPath() + ctx.moveTo(i, 0) + ctx.lineTo(i, canvasHeightPx) + ctx.stroke() + } + + // 绘制用户头像(左上角) 已完成 + const avatarSize = scale * 32 * dpr // 32px * dpr + const avatarX = scale * 35 * dpr // 距离左侧35px + const avatarY = scale * 35 * dpr // 距离顶部35px + + try { + const avatarPath = await loadImage(data.userAvatar) + // 微信小程序中绘制圆形头像需要特殊处理 + ctx.save() + ctx.beginPath() + ctx.arc(avatarX + avatarSize / 2, avatarY + avatarSize / 2, avatarSize / 2, 0, 2 * Math.PI) + ctx.clip() + ctx.drawImage(avatarPath, avatarX, avatarY, avatarSize, avatarSize) + ctx.restore() + } catch (error) { + // 如果头像加载失败,绘制默认头像 + ctx.setFillStyle('#CCCCCC') + ctx.beginPath() + ctx.arc(avatarX + avatarSize / 2, avatarY + avatarSize / 2, avatarSize / 2, 0, 2 * Math.PI) + ctx.fill() + } + + // 绘制用户昵称 已完成 + const nicknameX = avatarX + avatarSize + 8 * dpr // 距离头像8px + const nicknameY = avatarY + (avatarSize - 24 * dpr) / 2 // 与头像水平居中对齐 + const nicknameFontSize = scale * 24 * dpr + drawText(ctx, data.userNickname, nicknameX, nicknameY, 200 * dpr, nicknameFontSize, '#000000', true, '"Noto Sans SC"') + + // 绘制"邀你加入球局"文案 + const inviteX = scale * 35 * dpr // 距离画布左侧35px + const inviteY = scale * 100 * dpr // 距离画布顶部79px + const inviteFontSize = scale * 44 * dpr + + // 绘制"邀你加入" + drawBoldText(ctx, '邀你加入', inviteX, inviteY, inviteFontSize, '#000000', "Noto Sans SC") + + // 绘制"球局"特殊样式 + const qiuJuX = inviteX + ctx.measureText('邀你加入').width + 5 * dpr + const qiuJuFontSize = scale * 44 * dpr + drawBoldText(ctx, '球局', qiuJuX, inviteY, qiuJuFontSize, '#48D800', '"Noto Sans SC"') + + // 测试绘制网络图片 + drawSVGPathToCanvas(ctx) + + // 绘制球员图片(右上角)已完成 + const playerImgX = scale * 340 * dpr + const playerImgY = scale * 35 * dpr + const playerImgSize = scale * 124 * dpr + const borderRadius = scale * 24 * dpr + const padding = scale * 4 * dpr + const rotation = scale * -8 // 旋转-8度 + + try { + const playerImgPath = await loadImage(data.playerImage || data.venueImage) + ctx.save() + + // 移动到旋转中心点 + const centerX = playerImgX + playerImgSize / 2 + const centerY = playerImgY + playerImgSize / 2 + ctx.translate(centerX, centerY) + + // 旋转-8度 + ctx.rotate((rotation * Math.PI) / 180) + + // 1. 先绘制白色圆角矩形背景 + ctx.setFillStyle('#FFFFFF') + ctx.beginPath() + + // 使用更精确的圆角矩形绘制 + const rectX = -playerImgSize / 2 + const rectY = -playerImgSize / 2 + const rectWidth = playerImgSize + const rectHeight = playerImgSize + + // 从左上角开始,顺时针绘制 + // 左上角圆角 + ctx.moveTo(rectX + borderRadius, rectY) + ctx.quadraticCurveTo(rectX, rectY, rectX, rectY + borderRadius) + + // 左边 + ctx.lineTo(rectX, rectY + rectHeight - borderRadius) + + // 左下角圆角 + ctx.quadraticCurveTo(rectX, rectY + rectHeight, rectX + borderRadius, rectY + rectHeight) + + // 下边 + ctx.lineTo(rectX + rectWidth - borderRadius, rectY + rectHeight) + + // 右下角圆角 + ctx.quadraticCurveTo(rectX + rectWidth, rectY + rectHeight, rectX + rectWidth, rectY + rectHeight - borderRadius) + + // 右边 + ctx.lineTo(rectX + rectWidth, rectY + borderRadius) + + // 右上角圆角 + ctx.quadraticCurveTo(rectX + rectWidth, rectY, rectX + rectWidth - borderRadius, rectY) + + // 上边 + ctx.lineTo(rectX + borderRadius, rectY) + + ctx.closePath() + ctx.fill() + + // 2. 绘制图片(带4px内边距) + const imgX = -playerImgSize / 2 + padding + const imgY = -playerImgSize / 2 + padding + const imgSize = playerImgSize - padding * 2 + + // 设置圆角裁剪区域 + ctx.beginPath() + const imgRadius = borderRadius - padding + + // 从左上角开始,顺时针绘制 + // 左上角圆角 + ctx.moveTo(imgX + imgRadius, imgY) + ctx.quadraticCurveTo(imgX, imgY, imgX, imgY + imgRadius) + + // 左边 + ctx.lineTo(imgX, imgY + imgSize - imgRadius) + + // 左下角圆角 + ctx.quadraticCurveTo(imgX, imgY + imgSize, imgX + imgRadius, imgY + imgSize) + + // 下边 + ctx.lineTo(imgX + imgSize - imgRadius, imgY + imgSize) + + // 右下角圆角 + ctx.quadraticCurveTo(imgX + imgSize, imgY + imgSize, imgX + imgSize, imgY + imgSize - imgRadius) + + // 右边 + ctx.lineTo(imgX + imgSize, imgY + imgRadius) + + // 右上角圆角 + ctx.quadraticCurveTo(imgX + imgSize, imgY, imgX + imgSize - imgRadius, imgY) + + // 上边 + ctx.lineTo(imgX + imgRadius, imgY) + + ctx.closePath() + ctx.clip() + + // 绘制图片 + ctx.drawImage(playerImgPath, imgX, imgY, imgSize, imgSize) + + ctx.restore() + } catch (error) { + // 如果图片加载失败,绘制占位符 + ctx.save() + const centerX = playerImgX + playerImgSize / 2 + const centerY = playerImgY + playerImgSize / 2 + ctx.translate(centerX, centerY) + ctx.rotate((rotation * Math.PI) / 180) + + // 绘制白色圆角矩形背景 + ctx.setFillStyle('#FFFFFF') + ctx.beginPath() + + const rectX = -playerImgSize / 2 + const rectY = -playerImgSize / 2 + const rectWidth = playerImgSize + const rectHeight = playerImgSize + + // 从左上角开始,顺时针绘制 + // 左上角圆角 + ctx.moveTo(rectX + borderRadius, rectY) + ctx.quadraticCurveTo(rectX, rectY, rectX, rectY + borderRadius) + + // 左边 + ctx.lineTo(rectX, rectY + rectHeight - borderRadius) + + // 左下角圆角 + ctx.quadraticCurveTo(rectX, rectY + rectHeight, rectX + borderRadius, rectY + rectHeight) + + // 下边 + ctx.lineTo(rectX + rectWidth - borderRadius, rectY + rectHeight) + + // 右下角圆角 + ctx.quadraticCurveTo(rectX + rectWidth, rectY + rectHeight, rectX + rectWidth, rectY + rectHeight - borderRadius) + + // 右边 + ctx.lineTo(rectX + rectWidth, rectY + borderRadius) + + // 右上角圆角 + ctx.quadraticCurveTo(rectX + rectWidth, rectY, rectX + rectWidth - borderRadius, rectY) + + // 上边 + ctx.lineTo(rectX + borderRadius, rectY) + + ctx.closePath() + ctx.fill() + + // 绘制灰色占位符(带内边距) + const imgX = -playerImgSize / 2 + padding + const imgY = -playerImgSize / 2 + padding + const imgSize = playerImgSize - padding * 2 + + ctx.setFillStyle('#E0E0E0') + ctx.beginPath() + const imgRadius = borderRadius - padding + + // 从左上角开始,顺时针绘制 + // 左上角圆角 + ctx.moveTo(imgX + imgRadius, imgY) + ctx.quadraticCurveTo(imgX, imgY, imgX, imgY + imgRadius) + + // 左边 + ctx.lineTo(imgX, imgY + imgSize - imgRadius) + + // 左下角圆角 + ctx.quadraticCurveTo(imgX, imgY + imgSize, imgX + imgRadius, imgY + imgSize) + + // 下边 + ctx.lineTo(imgX + imgSize - imgRadius, imgY + imgSize) + + // 右下角圆角 + ctx.quadraticCurveTo(imgX + imgSize, imgY + imgSize, imgX + imgSize, imgY + imgSize - imgRadius) + + // 右边 + ctx.lineTo(imgX + imgSize, imgY + imgRadius) + + // 右上角圆角 + ctx.quadraticCurveTo(imgX + imgSize, imgY, imgX + imgSize - imgRadius, imgY) + + // 上边 + ctx.lineTo(imgX + imgRadius, imgY) + + ctx.closePath() + ctx.fill() + + ctx.restore() + console.log('球员图片占位符绘制完成') + } + + // 绘制球局信息区域 + const infoStartY = scale * 192 + const infoSpacing = scale * 64 + + // 球局类型和技能等级 + const gameInfoY = infoStartY + const iconSize = scale * 40 + const iconX = scale * 35 + const textX = iconX + iconSize + 20 + + // 绘制网球图标 + const tennisBallPath = await loadImage('https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/b3eaf45e-ef28-4e45-9195-823b832e0451.jpg') + ctx.drawImage(tennisBallPath, iconX, gameInfoY, iconSize, iconSize) + + // 绘制"单打"标签 + const danDaX = scale * 100 + const danDaY = scale * 196 + const danDaWidth = scale * 76 * dpr + const danDaHeight = scale * 40 * dpr + const danDaRadius = scale * 20 * dpr + const danDaFontSize = scale * 22 * dpr + + drawLabel(ctx, danDaX, danDaY, danDaWidth, danDaHeight, danDaRadius, data.gameType, danDaFontSize) + + // 绘制技能等级标签 + const skillX = scale * 190 + const skillY = scale * 196 + const skillWidth = scale * 180 * dpr + const skillHeight = scale * 40 * dpr + const skillRadius = scale * 20 * dpr + const skillFontSize = scale * 22 * dpr + + drawLabel(ctx, skillX, skillY, skillWidth, skillHeight, skillRadius, data.skillLevel, skillFontSize) + + // 绘制日期时间 + const dateX = danDaX + const timeInfoY = infoStartY + infoSpacing + const timeInfoFontSize = scale * 24 * dpr + const calendarPath = await loadImage("https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/ea792a5d-b105-4c95-bfc4-8af558f2b33b.jpg") + ctx.drawImage(calendarPath, iconX, timeInfoY, iconSize, iconSize) + + // 绘制日期(绿色) + drawText(ctx, data.gameDate, dateX, timeInfoY + 4, 300, timeInfoFontSize, '#4CAF50') + + // 绘制时间(黑色) + const timeX = textX + ctx.measureText(data.gameDate).width + 10 * dpr + drawText(ctx, data.gameTime, timeX, timeInfoY + 4, 300, timeInfoFontSize, '#000000') + + // 绘制地点 + const locationInfoY = infoStartY + infoSpacing * 2 + const locationFontSize = scale * 22 * dpr + const locationPath = await loadImage("https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/adc9a167-2ea9-4e3b-b963-6a894a1fd91b.jpg") + ctx.drawImage(locationPath, iconX, locationInfoY, iconSize, iconSize) + drawText(ctx, data.venueName, danDaX, locationInfoY + 4, 600, locationFontSize, '#000000') + + // 绘制完成,调用draw方法 + console.log('开始调用ctx.draw()') + ctx.draw(false, () => { + console.log('Canvas绘制完成,开始生成图片...') + // 延迟一下再生成图片,确保绘制完成 + setTimeout(() => { + Taro.canvasToTempFilePath({ + canvasId: 'shareCardCanvas', + fileType: 'png', + quality: 1, + success: (res) => { + console.log('图片生成成功:', res.tempFilePath) + setIsDrawing(false) // 绘制完成,重置状态 + resolve(res.tempFilePath) + onGenerated?.(res.tempFilePath) + setTempImagePath(res.tempFilePath) + }, + fail: (error) => { + console.error('图片生成失败:', error) + setIsDrawing(false) // 绘制失败,重置状态 + reject(error) + } + }) + }, 500) // 延迟500ms确保Canvas完全渲染 + }) + console.log('Canvas绘制命令已发送') + + } catch (error) { + console.error('绘制分享卡片失败:', error) + setIsDrawing(false) // 绘制失败,重置状态 + Taro.showToast({ + title: '生成分享卡片失败', + icon: 'none' + }) + reject(error) + } + }) + } + + // 手动分享方法(已移除,由父组件处理分享) + + + // 组件挂载后绘制 + useEffect(() => { + if (data && !isDrawing && !tempImagePath) { + console.log('组件挂载,开始绘制分享卡片') + // 延迟一下确保Canvas已经渲染 + setTimeout(() => { + drawShareCard() + }, 500) + } + }, [data]) // 只依赖data,移除canvasWidth避免无限循环 + + // 暴露分享方法给父组件 + useEffect(() => { + if (onGenerated && tempImagePath) { + onGenerated(tempImagePath) + } + }, [tempImagePath]) // 只依赖tempImagePath,移除onGenerated避免无限循环 + + + return ( + + { }} + onTouchMove={() => { }} + onTouchEnd={() => { }} + onTouchCancel={() => { }} + /> + + ) +} + +export default ShareCardCanvas + diff --git a/src/components/index.ts b/src/components/index.ts index 785769a..be082a9 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -16,6 +16,7 @@ import EditModal from "./EditModal/index"; import withAuth from "./Auth"; import { CustomPicker, PopupPicker } from "./Picker"; import NTRPEvaluatePopup from "./NTRPEvaluatePopup"; +import ShareCardCanvas from "./ShareCardCanvas"; import RefundPopup from "./refundPopup"; import GameManagePopup from './GameManagePopup'; import FollowUserCard from './FollowUserCard/index'; @@ -43,6 +44,7 @@ export { CustomPicker, PopupPicker, NTRPEvaluatePopup, + ShareCardCanvas, RefundPopup, GameManagePopup, FollowUserCard, diff --git a/src/container/listContainer/index.scss b/src/container/listContainer/index.scss index 04bf2f4..89520f9 100644 --- a/src/container/listContainer/index.scss +++ b/src/container/listContainer/index.scss @@ -4,7 +4,7 @@ display: flex; flex-direction: column; gap: 5px; - padding-bottom: 34px; + padding-bottom: 80px; // min-height: 100vh; .recommendTextWrapper { diff --git a/src/container/listContainer/index.tsx b/src/container/listContainer/index.tsx index 21dbe7b..ffd3833 100644 --- a/src/container/listContainer/index.tsx +++ b/src/container/listContainer/index.tsx @@ -4,6 +4,7 @@ import ListLoadError from "@/components/ListLoadError"; import ListCardSkeleton from "@/components/ListCardSkeleton"; import { useReachBottom } from "@tarojs/taro"; import "./index.scss"; +import { useRef, useEffect } from "react"; const ListContainer = (props) => { const { @@ -14,12 +15,23 @@ const ListContainer = (props) => { // recommendList, loadMoreMatches, } = props; + const timerRef = useRef(null); useReachBottom(() => { // 加载更多方法 - loadMoreMatches(); + timerRef.current = setTimeout(() => { + loadMoreMatches(); + }, 500); }); + useEffect(() => { + return () => { + if (timerRef.current) { + clearTimeout(timerRef.current); + } + }; + }, []); + if (error) { return ; } diff --git a/src/container/listCustomNavbar/index.scss b/src/container/listCustomNavbar/index.scss index 77fb979..d95d1a0 100644 --- a/src/container/listCustomNavbar/index.scss +++ b/src/container/listCustomNavbar/index.scss @@ -142,10 +142,12 @@ .hidden { opacity: 0; transform: translateY(-5px); + pointer-events: none; } /* 可见状态 */ .visible { opacity: 1; transform: translateY(0); + pointer-events: auto; } \ No newline at end of file diff --git a/src/game_pages/list/index.module.scss b/src/game_pages/list/index.module.scss index 2c2aa68..f847fed 100644 --- a/src/game_pages/list/index.module.scss +++ b/src/game_pages/list/index.module.scss @@ -1,3 +1,9 @@ +:global { + .guide-bar { + z-index: 9999; + } +} + .listPage { background-color: #fefefe; @@ -29,7 +35,7 @@ } .listNavWrapper { - position: relative; + position: relative; } .toggleElement { @@ -39,10 +45,10 @@ left: 0; width: 100%; height: 100%; - + /* 过渡动画设置,实现平滑切换 */ transition: opacity 0.5s ease, transform 0.5s ease; - + display: flex; align-items: center; justify-content: center; @@ -75,5 +81,6 @@ .hidden { opacity: 0; transform: translateY(20px); - pointer-events: none; /* 隐藏时不响应鼠标事件 */ + pointer-events: none; + /* 隐藏时不响应鼠标事件 */ } \ No newline at end of file diff --git a/src/game_pages/list/index.tsx b/src/game_pages/list/index.tsx index f422e52..6b77a4a 100644 --- a/src/game_pages/list/index.tsx +++ b/src/game_pages/list/index.tsx @@ -1,8 +1,8 @@ import SearchBar from "@/components/SearchBar"; import FilterPopup from "@/components/FilterPopup"; import styles from "./index.module.scss"; -import { useEffect, useRef, useCallback } from "react"; -import Taro, { usePageScroll } from "@tarojs/taro"; +import { useEffect, useRef, useCallback, useState } from "react"; +import Taro, { usePageScroll, useShareAppMessage, useShareTimeline } from "@tarojs/taro"; import { useListStore } from "@/store/listStore"; import { useGlobalState } from "@/store/global"; import { View } from "@tarojs/components"; @@ -12,6 +12,7 @@ import ListContainer from "@/container/listContainer"; import DistanceQuickFilter from "@/components/DistanceQuickFilter"; import { withAuth } from "@/components"; import { updateUserLocation } from "@/services/userService"; +// import ShareCardCanvas from "@/components/ShareCardCanvas"; import { useDictionaryStore } from "@/store/dictionaryStore"; const ListPage = () => { @@ -228,6 +229,47 @@ const ListPage = () => { }); }; + // ====== 分享 测试 ====== + // const [shareImagePath, setShareImagePath] = useState('') + + // const handleShare = (imagePath: string) => { + // console.log('===imagePath', imagePath) + + // // 避免重复设置相同的图片路径 + // if (imagePath && imagePath !== shareImagePath) { + // setShareImagePath(imagePath) + + // // 图片生成完成后,显示分享菜单 + // Taro.showShareMenu({ + // withShareTicket: true, + // success: () => { + // console.log('分享菜单显示成功') + // }, + // fail: (error) => { + // console.error('分享菜单显示失败:', error) + // } + // }) + // } + // } + + // // 页面级分享钩子 + // useShareAppMessage(() => { + // console.log('页面分享给好友,图片路径:', shareImagePath) + // return { + // title: '列表页-邀你加入球局', + // path: '/game_pages/list/index', + // imageUrl: shareImagePath || '' + // } + // }) + + // useShareTimeline(() => { + // console.log('页面分享到朋友圈,图片路径:', shareImagePath) + // return { + // title: '列表页-邀你加入球局', + // query: 'from=timeline', + // imageUrl: shareImagePath || '' + // } + // }) // 初始化字典数据 const initDictionaryData = async () => { try { @@ -251,7 +293,6 @@ const ListPage = () => { }} /> - {/* */} {/* 列表内容 */} {/* 综合筛选 */} @@ -310,6 +351,22 @@ const ListPage = () => { /> + {/* 测试分享功能 */} + {/* */} ); diff --git a/src/store/listStore.ts b/src/store/listStore.ts index 94f2ce3..9bc609e 100644 --- a/src/store/listStore.ts +++ b/src/store/listStore.ts @@ -463,12 +463,14 @@ export const useListStore = create()((set, get) => ({ searchPageState: { ...searchPageStateDefaultValue }, + loading: true, }); } else { set({ listPageState: { ...listPageStateDefaultValue }, + loading: true, }); } if (!isSearchData) {