123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409 |
- "use client";
- import { receiveRedPacketApi } from "@/api/promo";
- import { useRouter } from "@/i18n/routing";
- import { getToken } from "@/utils/Cookies";
- import { Mask } from "antd-mobile";
- import clsx from "clsx";
- import { useTranslations } from "next-intl";
- import { FC, forwardRef, memo, useEffect, useImperativeHandle, useRef, useState } from "react";
- import styles from "./style.module.scss";
- const randomX = (len: number) => {
- return Math.floor(Math.random() * len);
- };
- const mockData = Array(500)
- .fill(0)
- .map((item) => {
- return {
- phone: `55****${(randomX(99) + "").padEnd(2, "0")}`,
- num: `${(Math.random() * 20).toFixed(2)}`,
- time: "11:00",
- };
- });
- function getRandom(min: number, max: number) {
- const floatRandom = Math.random();
- const difference = max - min;
- // 介于 0 和差值之间的随机数
- const random = Math.round(difference * floatRandom);
- return random + min;
- }
- /**
- * @description 描述
- */
- type DescProps = {
- onClose: () => void;
- };
- const HbyInfoDetail = (props: any) => {
- const { iconImg, onCloseHby } = props;
- const router = useRouter();
- const token = getToken();
- const handler = () => {
- if (!token) {
- router.push("/login");
- }
- onCloseHby();
- };
- const str = "12:00".split("");
- return (
- <div className={`absolute h-[100%] w-[100%]`} onClick={onCloseHby}>
- <div className={"flex h-[100%] w-[100%] items-center"}>
- <div className={"relative h-[6.2rem] w-[100%]"}>
- <img className={"h-[6.2rem] w-[100%]"} src="/redpacket/isEndBg.png" alt="" />
- {/* 下一场开始时间*/}
- <div className={"absolute top-[27%] w-[100%]"}>
- <div className={"relative h-[0.4306rem] border-primary-color"}>
- <div className="absolute left-[21%] h-[100%] w-[0.4167rem] text-center text-[0.2778rem] font-black text-[red]">
- {str[0]}
- </div>
- <div className="absolute left-[36%] h-[100%] w-[0.4167rem] text-center text-[0.2778rem] font-black text-[red]">
- {str[1]}
- </div>
- <div className="absolute left-[47%] h-[100%] w-[0.2167rem] text-center text-[0.2778rem] font-black text-[red]">
- {str[2]}
- </div>
- <div className="absolute left-[54%] h-[100%] w-[0.4167rem] text-center text-[0.2778rem] font-black text-[red]">
- {str[3]}
- </div>
- <div className="absolute left-[68%] h-[100%] w-[0.4167rem] text-center text-[0.2778rem] font-black text-[red]">
- {str[4]}
- </div>
- </div>
- </div>
- <div
- className={
- "absolute top-[53%] h-[0.6944rem] w-[100%] px-[0.5556rem]" +
- " overflow-scroll border-primary-color text-primary-color"
- }
- ></div>
- </div>
- </div>
- </div>
- );
- };
- const HbyInfo = (props: any) => {
- const { iconImg, onCloseHby, onReciveRed } = props;
- if (!iconImg) return;
- return (
- <div
- className={`absolute left-1/2 top-[50%] -translate-x-1/2 -translate-y-1/2 ${styles.redclose}`}
- >
- {/* <Image src={"/hby/close.png"} alt={"close"} width={20} height={20} onClick={onCloseHby} className={styles.closeIcon}/> */}
- <div onClick={onCloseHby} className={styles.closeIcon}></div>
- <img
- src={iconImg}
- alt={"icon"}
- width={559}
- height={687}
- onClick={onReciveRed}
- className={styles.redIcon}
- />
- {/* <div className={styles.title}>Chuva de dinheiro</div>
- <div className={styles.desc}>
- <ul className={styles.desclist}>
- <li className={styles.descitem}> Membros recarregados podem reivindicar gratuitamente. </li>
- <div className={styles.line}></div>
- <li className={styles.descitem}> Valor máximo de queda em dinheiro: R$7.777 </li>
- </ul>
- </div>
- <div className={styles.openBtn} onClick={onReciveRed}>AGARRAR</div> */}
- </div>
- // <div data-v-f333135e="" className={`absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 ${styles.redopen}`}>
- // <Image src={"/hby/close.png"} alt={"close"} width={20} height={20} onClick={onCloseHby} className={styles.closeIcon}/>
- // <div className={styles.title}>Chuva de dinheiro</div>
- // <div className={styles.cash}>1.96</div>
- // <div className={styles.tips}>Valor máximo de queda em dinheiro:Cada sessäo de chuva de dinheiro é </div>
- // </div>
- );
- };
- const HbyInfo2 = (props: any) => {
- const { iconImg, onCloseHby, redAmount } = props;
- const hbyInfoClass = clsx(
- `${styles.redopen} absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 bg-no-repeat bg-cover`
- );
- const t = useTranslations("packetsPopup");
- return (
- <div
- className={hbyInfoClass}
- style={{ background: `url(${iconImg})`, backgroundSize: "100% 100%" }}
- >
- <img
- src={"/hby/close.png"}
- alt={"close"}
- width={30}
- height={30}
- onClick={onCloseHby}
- className={styles.closeIcon}
- />
- <div className={styles.title}>{t("title")}</div>
- <div className={styles.cash}>{redAmount}</div>
- <div className={styles.tips}>
- {redAmount > 0 ? t("receiveSuccess") : t("receiveWarring")}
- </div>
- </div>
- );
- };
- /**
- * @description 动画背景 - 下雨动效
- */
- type Props = {
- onAfterHandler?: () => void;
- };
- export type RedPacketModalProps = {
- onClose: () => void;
- onOpen: (source: any[], index?: number) => void;
- };
- /**
- * @description 红包的三种状态
- * is_start 可领取 展示红包领取组件
- * is_receive 已领取 展示领取详情组件
- * is_end 可展示 展示说明页
- */
- enum Status {
- is_start,
- is_receive,
- is_end,
- }
- interface Snowflake {
- x: number; // 水平位置
- y: number; // 垂直位置
- scale: number; // 缩放比例
- speedX: number; // 水平移动速度
- speedY: number; // 垂直下落速度
- rotate: number; // 当前旋转角度
- rotateSpeed: number; // 旋转速度
- image: HTMLImageElement; // 图片路径
- }
- interface SnowfallProps {
- images: string[]; // 图片数组
- snowflakeCount?: number; // 雪花数量
- onClose: () => void;
- }
- const Snowfall: FC<SnowfallProps> = ({ images, snowflakeCount = 80, onClose = () => {} }) => {
- // canvas
- const canvasRef = useRef<HTMLCanvasElement | null>(null);
- // 预加载图片
- const imageElements = useRef<HTMLImageElement[]>([]);
- // 父元素
- const containerRef = useRef<HTMLDivElement | null>(null);
- const createSnowflakes = (count: number, width: number, height: number): Snowflake[] => {
- return Array.from({ length: count }, () => ({
- x: Math.random() * width,
- y: Math.random() * height - height,
- scale: Math.random() * (1.2 - 0.5) + 0.5,
- speedX: Math.random() * 1.5 - 0.75,
- speedY: Math.random() * 5 + 1,
- rotate: Math.random() * 180,
- rotateSpeed: Math.random() * 2 - 1,
- image: imageElements.current[Math.floor(Math.random() * imageElements.current.length)],
- }));
- };
- useEffect(() => {
- // 预加载图片
- imageElements.current = images.map((src) => {
- const img = new Image();
- img.src = src;
- return img;
- });
- const canvas = canvasRef.current;
- if (!canvas) return;
- const ctx = canvas.getContext("2d");
- if (!ctx) return;
- const parentWidth = containerRef.current?.clientWidth || 0;
- const parentHeight = (canvas.height = containerRef.current?.clientHeight || 0);
- const width = (canvas.width = parentWidth);
- const height = (canvas.height = parentHeight);
- let snowflakes: Snowflake[] = createSnowflakes(snowflakeCount, width, height);
- const imgHeight = 80;
- const imgWidth = 80;
- const animate = () => {
- ctx.clearRect(0, 0, width, height);
- snowflakes.forEach((flake) => {
- flake.y += flake.speedY;
- flake.x += flake.speedX;
- flake.rotate += flake.rotateSpeed;
- if (flake.y > height) {
- flake.y = -44;
- flake.x = Math.random() * width;
- flake.rotate = Math.random() * 360;
- }
- if (flake.x < 0 || flake.x > width) {
- flake.speedX *= -1;
- }
- ctx.save();
- ctx.globalAlpha = 1;
- ctx.translate(
- flake.x + (imgHeight * flake.scale) / 2,
- flake.y + (imgWidth * flake.scale) / 2
- );
- ctx.rotate((flake.rotate * Math.PI) / 180);
- ctx.drawImage(
- flake.image,
- (-imgHeight * flake.scale) / 2,
- (-imgWidth * flake.scale) / 2,
- imgHeight * flake.scale,
- imgWidth * flake.scale
- );
- ctx.restore();
- });
- requestAnimationFrame(animate);
- };
- animate();
- const handleResize = () => {
- canvas.width = parentWidth;
- canvas.height = parentHeight;
- snowflakes = createSnowflakes(snowflakeCount, canvas.width, canvas.height);
- };
- window.addEventListener("resize", handleResize);
- return () => {
- window.removeEventListener("resize", handleResize);
- };
- }, []);
- return (
- <div className={"absolute h-[100%] w-[100%]"} ref={containerRef} onClick={onClose}>
- <canvas ref={canvasRef} style={{ display: "block" }} />;
- </div>
- );
- };
- const RedPacketModal = forwardRef<RedPacketModalProps, Props>(function RedPacketModal(props, ref) {
- const { onAfterHandler } = props;
- const [visible, setVisible] = useState(false);
- const [iconLists, setIconLists] = useState<any>([]);
- // 初始状态为is_end, 展示活动详情页
- const [status, setStatus] = useState<Status>(Status.is_end);
- const packetCurrent = useRef<any>({});
- const packets = useRef<any[]>([]);
- const [redAmount, setRedAmount] = useState<any>(1);
- const activeIndex = useRef<number>(0);
- const token = getToken();
- const element = useRef<HTMLElement | null>(null);
- useImperativeHandle(ref, () => {
- return {
- onClose: () => setVisible(false),
- onOpen: (source, index?: number) => {
- packets.current = source;
- if (index !== null && index !== undefined) {
- activeIndex.current = index;
- }
- getRedPacketInfo().then((res) => {
- setVisible(true);
- });
- },
- };
- });
- const getRedPacketInfo = async () => {
- try {
- let actList = packets.current;
- // 是否有已开始但是没领过的红包
- let packetsFilter = actList
- .filter((aItem: any) => {
- return aItem.can_receive && aItem.is_start && !aItem.is_receive;
- })
- .sort((pre: any, next: any) => pre.end_time - next.end_time);
- // 有可领取红包
- if (packetsFilter.length > 0) {
- let current = packetsFilter[activeIndex.current];
- let iconList = JSON.parse(current.icon);
- // 红包
- packetCurrent.current = current;
- // 有可领取红包
- setIconLists(iconList);
- if (!token) {
- setStatus(Status.is_end);
- } else {
- setStatus(Status.is_start);
- }
- } else {
- // 无可领取红包
- // 展示最近可领红包详情
- let packets = actList.sort((pre: any, next: any) => pre.end_time - next.end_time);
- packetCurrent.current = packets[0];
- setIconLists(JSON.parse(packets[0].icon));
- // 无可领取红包展示详情
- setStatus(Status.is_end);
- }
- } catch (error) {
- console.log("redPacketInfo===>error:", error);
- }
- };
- const onReciveRed = async () => {
- try {
- let paramsData = {
- id: packetCurrent.current?.id,
- index: packetCurrent.current?.index,
- };
- let receiveRedPacketInfo = await receiveRedPacketApi(paramsData);
- let redNum = receiveRedPacketInfo.data;
- if (onAfterHandler) {
- onAfterHandler();
- }
- setStatus(Status.is_receive);
- setRedAmount(redNum?.amount);
- } catch (error) {
- console.log("redPacketInfo===>error:", error);
- }
- };
- useEffect(() => {
- element.current = document.getElementById("app");
- }, []);
- const ImagesData = Array.from({ length: 6 }).map((_, index) => {
- return `/9f/money${index + 1}.png`;
- });
- return (
- <Mask visible={visible} destroyOnClose={true} getContainer={element.current} opacity={0.75}>
- <Snowfall images={ImagesData} onClose={() => setVisible(false)} />
- {status === Status.is_start ? (
- <HbyInfo
- onCloseHby={() => setVisible(false)}
- onReciveRed={onReciveRed}
- iconImg={iconLists[1]}
- />
- ) : status === Status.is_receive ? (
- <HbyInfo2
- onCloseHby={() => setVisible(false)}
- redAmount={redAmount}
- iconImg={iconLists[2]}
- />
- ) : (
- <HbyInfoDetail onCloseHby={() => setVisible(false)} iconImg={iconLists[0]} />
- )}
- </Mask>
- );
- });
- export default memo(RedPacketModal);
|