123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417 |
- "use client";
- import { getGiveReceiveApi, SlotParams, SlotType } from "@/api/slots";
- import { flatPoint } from "@/utils/methods";
- import { SlotMachine } from "@lucky-canvas/react";
- import { Mask, Toast } from "antd-mobile";
- import clsx from "clsx";
- import { useTranslations } from "next-intl";
- import { FC, forwardRef, memo, useEffect, useImperativeHandle, useRef, useState } from "react";
- import animation from "../animations.module.scss";
- interface Props {
- onAfterHandler: () => void;
- }
- const getRandomRedColor = () => {
- const r = 255;
- const g = 255;
- const b = 255;
- return `rgba(${r},${g},${b},${Math.random().toFixed(1)})`;
- };
- const defaultConfig = {
- rowSpacing: "0.4861rem",
- direction: -1,
- };
- const prizes = [
- {
- imgs: [
- {
- width: "80%",
- height: "100%",
- src: "/slots/0.png",
- },
- ],
- },
- {
- imgs: [
- {
- width: "80%",
- height: "100%",
- src: "/slots/1.png",
- },
- ],
- },
- {
- imgs: [
- {
- width: "80%",
- height: "100%",
- src: "/slots/2.png",
- },
- ],
- },
- {
- imgs: [
- {
- width: "80%",
- height: "100%",
- src: "/slots/3.png",
- },
- ],
- },
- {
- imgs: [
- {
- width: "80%",
- height: "100%",
- src: "/slots/4.png",
- },
- ],
- },
- {
- imgs: [
- {
- width: "80%",
- height: "100%",
- src: "/slots/5.png",
- },
- ],
- },
- {
- imgs: [
- {
- width: "80%",
- height: "100%",
- src: "/slots/6.png",
- },
- ],
- },
- {
- imgs: [
- {
- width: "80%",
- height: "100%",
- src: "/slots/7.png",
- },
- ],
- },
- {
- imgs: [
- {
- width: "80%",
- height: "100%",
- src: "/slots/8.png",
- },
- ],
- },
- {
- imgs: [
- {
- width: "80%",
- height: "100%",
- src: "/slots/9.png",
- },
- ],
- },
- ];
- const slots = [
- { order: [9, 7, 6, 8, 2, 5, 4, 3, 1, 0], speed: 30 },
- { order: [9, 6, 5, 3, 8, 7, 4, 0, 1, 2], speed: 26 },
- { order: [9, 2, 4, 7, 3, 1, 8, 5, 0, 6], speed: 22 },
- { order: [9, 3, 8, 5, 0, 1, 2, 4, 6, 7], speed: 18 },
- ];
- const numberPadding = (n: number, index = 4): number[] => {
- const s = `${n}`.padStart(index, "0");
- return s.split("").map((v, i) => Number(v));
- };
- const ColorWill = () => {
- const rows = 9; // 总行数
- const cols = 22; // 总列数
- const [blocks, setBlocks] = useState<string[][]>(
- Array.from({ length: rows }, () => Array.from({ length: cols }, () => getRandomRedColor()))
- );
- useEffect(() => {}, []);
- return (
- <>
- <div className={`relative flex flex-col`}>
- <div
- className={`absolute -z-[1] h-[100%] w-[100%] bg-[url(/slots/color-bg.png)] bg-cover bg-repeat-x ${animation.moveAnimation}`}
- ></div>
- {/*<img*/}
- {/* src="/slots/color-bg.png"*/}
- {/* className={`absolute -z-[1] h-[100%] w-[10000px] ${animation.moveAnimation}`}*/}
- {/* alt=""*/}
- {/*/>*/}
- {blocks.map((row, rowIndex) => (
- <div key={rowIndex} className={"flex justify-between"}>
- {row.map((n: string, index: number) => {
- return (
- <div
- key={index}
- style={{ background: n, transition: "background-color 0.2s" }}
- className={"h-[0.0694rem] w-[0.0694rem] " + " rounded-[50%]"}
- ></div>
- );
- })}
- </div>
- ))}
- </div>
- </>
- );
- };
- interface SlotsClientProps {
- slotSource: SlotType;
- onRotateAfter?: () => void;
- }
- const SlotsClient: FC<SlotsClientProps> = (props) => {
- const { slotSource, onRotateAfter } = props;
- const slotsRef = useRef<any>(null);
- const t = useTranslations();
- const rotating = useRef<boolean>(false);
- const buttonRef = useRef<HTMLImageElement | null>(null);
- // const ratio = `${slotSource?.rollover.reduce(
- // (pre, next) => (pre > next.weight ? pre : next.weight),
- // 0
- // )}`
- // .split("")
- // .map((n) => Number(n)) || [9, 9];
- const [ratio, setRatio] = useState([9, 9]);
- const params = {
- activity_id: slotSource?.id,
- start_time: slotSource?.times[0].start_time,
- end_time: slotSource?.times[0].end_time,
- };
- useEffect(() => {
- // getSlots();
- }, []);
- // 开始旋转
- const handler = () => {
- // 按下动画
- if (params && !params.activity_id && !params.start_time) return;
- if (rotating.current) return;
- // slotsRef.current?.play();
- // setTimeout(() => {
- // slotsRef.current?.stop(numberPadding(Math.floor(Math.random() * 9999)));
- // }, 2500);
- // return;
- // 图片按下修改高度
- let height = buttonRef.current!.clientHeight;
- buttonRef.current!.style.transition = "height 0.3s";
- buttonRef.current!.style.height = `${height * 0.8}px`;
- rotating.current = true;
- setTimeout(() => {
- buttonRef.current!.style.height = `${height}px`;
- }, 200);
- slotsRef.current?.play();
- const Interval = setInterval(() => {
- const num = Math.floor(Math.random() * 99) + 1;
- setRatio(() => numberPadding(num, 2));
- }, 500);
- // 数据获取
- getGiveReceiveApi(params as SlotParams)
- .then((res) => {
- setTimeout(() => {
- Interval && clearInterval(Interval);
- const rollover = flatPoint(res.data.rollover / 100);
- slotsRef.current?.stop(numberPadding(res.data.amount));
- setRatio(numberPadding(rollover > 100 ? 99 : rollover, 2));
- }, 3000);
- })
- .catch((error) => {
- Interval && clearInterval(Interval);
- slotsRef.current.init();
- rotating.current = false;
- Toast.show(t(`code.${error.data.code}`));
- });
- };
- // 结束旋转
- const endHandler = (prize: any) => {
- // rotating.current = false;
- onRotateAfter && onRotateAfter();
- };
- return (
- <>
- <div className={"w-[100%]"}>
- <div className={"relative w-[100%]"}>
- <img src="/slots/aura.png" alt="" className={"absolute -top-[23%]"} />
- <img
- src={"/slots/slots-bg.png"}
- className={"h-[3.6333rem] w-[100%]" + "object-cover"}
- alt={""}
- />
- {/*light*/}
- <div className={"absolute top-0 h-[2.69rem] w-[100%]"}>
- <img
- src={"/slots/light-1.png"}
- alt={""}
- className={`h-[100%] w-[100%] ${animation.flashingAnimation}`}
- />
- <img
- src="/slots/light-2.png"
- alt=""
- className={`absolute top-0 h-[100%] w-[100%] ${animation.antiFlashingAnimation} `}
- />
- </div>
- {/*header*/}
- <div
- className={
- "absolute left-[50%] top-[0.1875rem] -translate-x-1/2" +
- " ml-[0.0694rem] h-[0.6111rem] w-[1.48rem] sm:w-[1.58rem]" +
- " overflow-hidden rounded-tl-[10px] rounded-tr-[10px] border-[1px]" +
- " border-[transparent]"
- }
- >
- <div
- className={
- "h-[100%] w-[100%] " + " relative flex items-center justify-center"
- }
- >
- <div
- className={
- "absolute left-0 top-0 flex w-[100%] flex-col" +
- " -z-1 h-[100%] flex-wrap justify-between overflow-hidden"
- }
- >
- <ColorWill />
- </div>
- <div className={"z-10 flex h-[100%] items-center"}>
- <img src="/slots/ratio/x.png" alt="" className={"h-[0.4167rem]"} />
- {ratio.map((n, index) => (
- <img
- key={index}
- src={`/slots/ratio/${n}.png`}
- alt={""}
- className={"h-[0.4514rem]"}
- />
- ))}
- </div>
- </div>
- </div>
- {/*slots*/}
- <div
- className={
- "absolute bottom-[51%] left-0 flex w-[100%] translate-y-1/2 " +
- " justify-center"
- }
- >
- <div className={""}>
- <SlotMachine
- slots={slots}
- ref={slotsRef}
- width="3.1944rem"
- onEnd={endHandler}
- height="1.5625rem"
- prizes={prizes}
- defaultConfig={defaultConfig}
- ></SlotMachine>
- </div>
- </div>
- <div
- className={
- "absolute bottom-[0.2083rem] left-0 flex w-[100%] justify-center"
- }
- >
- <img
- onClick={handler}
- ref={buttonRef}
- src={"/slots/button.png"}
- className={"h-[0.6944rem] w-[1.25rem]" + " object-fill"}
- />
- </div>
- </div>
- </div>
- </>
- );
- };
- export type SlotModalRefProps = {
- onClose: () => void;
- onOpen: (value: SlotType) => void;
- };
- const SlotsModal = forwardRef<SlotModalRefProps, Props>(function SlotsModal(props, ref) {
- const [visible, setVisible] = useState(false);
- const { onAfterHandler } = props;
- const [willStatus, setWillStatus] = useState(false);
- const [slotSource, setSlotSource] = useState<SlotType | null>(null);
- useImperativeHandle(ref, () => {
- return {
- onClose: () => setVisible(false),
- onOpen: (source: SlotType) => {
- setVisible(true);
- setSlotSource(source);
- },
- };
- });
- const globalCls = "absolute z-[8] transition transform duration-1000 ease-in ";
- const light1 = clsx(globalCls, willStatus ? `${animation.expandAnimation} ` : "hidden", {});
- const light2 = clsx(globalCls, willStatus ? `${animation.expandAnimation} ` : "hidden", {});
- const light3 = clsx(
- " object-contain absolute z-[8] ",
- willStatus ? ` ${animation.scaleLxAnimation}` : "hidden"
- );
- const light4 = clsx(
- " object-contain absolute z-[8] ",
- willStatus ? `${animation.scaleRxAnimation}` : "hidden"
- );
- const will = clsx("", willStatus ? "opacity-1" : "opacity-0");
- const handler = () => {
- setWillStatus(true);
- onAfterHandler();
- };
- return (
- <>
- <Mask visible={visible} destroyOnClose={true} getContainer={null}>
- <div
- className={"absolute right-[0.2083rem] top-[18%] z-50"}
- onClick={() => setVisible(false)}
- >
- <span className={"iconfont icon-guanbi"}></span>
- </div>
- {/*body*/}
- <div className={`absolute top-[8%] w-[100%]`}>
- <div className={`h-[1.1111rem]`}>
- <div
- className={`relative flex h-[100%] items-center justify-center transition-[all] duration-500 ease-in ${will}`}
- >
- <img
- src="/slots/will-bg.png"
- className={"z-10 h-[0.6944rem] object-contain"}
- alt=""
- />
- <img src="/slots/will-light1.png" alt="" className={light1} />
- <img src="/slots/will-light2.png" alt="" className={light2} />
- <img src="/slots/will-light3.png" alt="" className={light3} />
- <img src="/slots/will-light3.png" alt="" className={light4} />
- </div>
- </div>
- {slotSource && <SlotsClient onRotateAfter={handler} slotSource={slotSource} />}
- </div>
- </Mask>
- </>
- );
- });
- export default memo(SlotsModal);
|