react结合lucky-canvas组件实现大转盘抽奖功能

1.需求

有时候在项目开发时,要实现一个大转盘抽奖功能,该如何实现呢?效果图如下:

说明:

     点击"抽奖"按钮,弹出转盘抽奖弹框,弹框说明如下:

  • 标题(弹框最上面)
  • 弹框渲染图片(如上图$图片)
  • 弹框背景图(两张:一张底图一张跟随奖励转动的图)
  • 奖励以及图片(奖励从后台获取,并配置到对应的奖励图片数组中)
  • 可抽奖次数(转盘中间示意数字)
  • 转盘高亮图片(当抽奖完成后,高亮某个中奖图片)
  • 跑马灯(转盘周边跑马灯效果)
  • 中奖后的弹框(中奖高亮后弹出中奖说明)

2.实现

把上面需要实现的功能写成一个组件,页面直接调用该组件即可,这样就可以在很多页面上实现大转盘功能,功能说明如下:

  1. 点击"抽奖按钮",弹出抽奖转盘弹框
  2. 点击弹框隐藏按钮,隐藏弹框
  3. 判断用户是否登录,并获取转盘次数
  4. 构建转盘(需使用大转盘抽奖(lucky canvs)插件)
    1. 弹框标题
    2. 弹框转盘背景图以及渲染效果图
    3. 转盘周边跑马灯效果渲染
    4. 转盘背景图渲染
    5. 转盘次数渲染(从主页传入,主页的也是从后台获取的)
    6. 转盘奖项以及图片渲染(从后台获取)
    7. 中间后中奖项高亮渲染
    8. 点击转盘抽奖按钮进行中奖逻辑操作(lucky canvs插件功能)
    9. 中奖完成后高亮以及减少抽奖次数等后续逻辑操作

代码如下:  

要引入组件页面的代码: 

import React, {useState} from "react";
import axios from "axios";
import {useHistory} from "react-router-dom";
import LuckyCanvs from "../components/LuckyCanvs";
 
const Index = () => {
    let history = useHistory();
    const {getPersist, setPersist} = React.useContext(PersistContext);
    const userInfo = getPersist("user_info");
    //可转次数
    const [num, setNum] = React.useState(1);
    
     useEffect(() => {
        if (!userInfo) { //用户没登录,跳转到登录页面
            history.replace("/login");
            return;
        }

        //获取当前已登录玩家的转盘状态
        SendCMD("getLuckyCanvs", {token: userInfo.token}).then((res) => {
            if (!res.status) {
                console.log(res.status);
                return;
            }
            //设置可转次数:从后台获取返回的次数结果
            setNum(res.data.num ? res.data.num : 0);
        });

    }, [userInfo]);

 return (
         <!--调用转盘组件-->
         <LuckyCanvs setCash={setCash} num={num} />
    )
}
 
export default Index;

 LuckyCancs组件:   

import style from "./style.less";
import React, {useState, useRef, useEffect} from 'react'
import { LuckyWheel } from '@lucky-canvas/react'
import {PersistContext} from "../../data/PersistProvider";
import {SendCMD} from "../../utils/HTTPRequest";

//边框高亮
const BorderLight =  () => {
    //边框高亮
    const [borderActive, setBorderActive] = useState(1);
    const lightTimer = useRef(null);
    useEffect(() => {
        if (lightTimer.current != null) {
            clearTimeout(lightTimer.current);
            lightTimer.current = null;
        }
        lightTimer.current = setTimeout(() => {
            if (borderActive == 2) {
                setBorderActive(1);
                return;
            }
            setBorderActive(() => borderActive + 1);
        }, 500);
        return () => {
            clearTimeout(lightTimer.current);
        };
    }, [borderActive]);

    return React.useMemo(
        () => (
            <div class={style.border_light}>
                {borderActive == 1 ? (
                    <ul>
                        {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16].map((index) => {
                            let num = index % 2 == 0 ? 0 : 1;
                            return (
                                <li>
    
                                    <img src={`../../assets/images/lucky_wheel/border_light${num}.png`}
                                        alt=""
                                    />
                                </li>
                            );
                        })}
                    </ul>
                ): (
                    <ul>
                        {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16].map((index) => {
                            let num = index % 2 == 0 ? 1 : 0;
                            return (
                                <li>
                                   
                                    <img
                                        src={`../../assets/images/lucky_wheel/border_light${num}.png`}
                                        alt=""
                                    />
                                </li>
                            );
                        })}
                    </ul>
                )}
            </div>
        ),
        [borderActive]
    );
};

const LuckyCanvs = (props) => {
    const {getPersist, setPersist} = React.useContext(PersistContext);
    const userInfo = getPersist("user_info");  
    //次数
    const [num, setNum] = useState(1);
    //抽奖结果展示
    const [resText, setResText] = useState(0);
    //中奖部分边框高亮
    const [midActiveLight, setMidActiveLight] = useState(false);

    useEffect(() => {
        setNum(props.Num);
    }, [props.Num]);

    //初始化抽奖按钮
    const [buttonState, setButtonState] = useState("init");
    
    //转盘背景
    //下面所有的百分比都可以用rem表示,这样可以然转盘图片大小随着不同浏览器放大或缩小,兼容不同浏览器
    const [blocks] = useState([
        {padding: '1.05rem', imgs :[{src: "../../assets/images/lucky_wheel/mfzp_1_8.png", width: "100%", height:"100%"}]},
        {padding: '0.1rem',imgs :[{src: "../../assets/images/lucky_wheel/mfzp_1_9.png", width: "100%", height:"100%", rotate: true}]},
    ]);

    //奖项
  const [prizes] = useState([
      {imgs: [{ src: "../../assets/images/lucky_wheel/mfzp_1_13_4.png", width:"57%", top: "19%"}]},
      {imgs: [{ src: "../../assets/images/lucky_wheel/mfzp_1_13_7.png", width:"29%", top: "19%"}]},
      {imgs: [{ src: "../../assets/images/lucky_wheel/mfzp_1_13_2.png", width:"55%", top: "19%"}]},
      {imgs: [{ src: "../../assets/images/lucky_wheel/mfzp_1_13_1.png", width:"45%", top: "19%"}]},
      {imgs: [{ src: "../../assets/images/lucky_wheel/mfzp_1_13_3.png", width:"34%", top: "19%"}]},
      {imgs: [{ src: "../../assets/images/lucky_wheel/mfzp_1_13_5.png", width:"44%", top: "19%"}]},
      {imgs: [{ src: "../../assets/images/lucky_wheel/mfzp_1_13_6.png", width:"44%", top: "19%"}]},
      {imgs: [{ src: "../../assets/images/lucky_wheel/mfzp_1_13_1.png", width:"45%", top: "19%"}]},
  ]);

    //默认配置
    const defaultConfig = {
      offsetDegree: -22.7,  //转盘偏移角度
    };

    const myLucky = useRef()

    //转盘抽奖开始:请求服务端,获取抽奖结果
    const onStart = () => {
      //判断抽奖次数
        if (spinNum < 1) {
            if (props.showShare) {  //是否展示分享底部弹框
                props.showShare();
            }
            return;
        }
        myLucky.current.play();//play(): 调用该方法时, 游戏才会开始
        setMidActiveLight(false);
        if (!userInfo) {
            SendCMD("guestSpin", {}).then((res) => {  //游客转转盘: 请求服务端接口,获取奖项
                if (!res.status) { //请求失败
                    console.log(res)
                    myLucky.current.init(); //重新初始化
                    return;
                }

                setSpinNum(spinNum - 1); //更新spin次数

                //从结果中获取奖项数据:奖项索引,价格
                setTimeout(() => {
                    const index = res.prize_index
                    myLucky.current.stop(index)  //stop:调用该方法时, 才会缓慢停止, 参数就是中奖奖品的索引
                }, 2000);

                //保存返回的guid: 已经保存过guid的玩家不能再转,并且注册的时候,向服务器传字段invite_wheel_guid,以便关联玩家
                setPersist("guid", res.guid);
                if (res.cash > 0) {  // 判断返回金额是否大于0
                    setCash(res.cash / 100);
                    setResText(res.cash / 100);
                } else {
                    setResText(res.cash);
                }
            });
        } else {
            SendCMD("normalSpin",{token: userInfo.token}).then((res) => {  //登录过的用户抽奖接口
                if (!res.status) { //请求失败
                    console.log(res)
                    myLucky.current.init(); //重新初始化
                    return;
                }

                setSpinNum(spinNum - 1); //更新spin次数

                //从结果中获取奖项数据:奖项索引,价格
                setTimeout(() => {
                    const index = res.spin_result.prize_index
                    myLucky.current.stop(index)  //stop:调用该方法时, 才会缓慢停止, 参数就是中奖奖品的索引
                }, 2000);

                if (res.spin_result.cash > 0) {  // 判断返回金额是否大于0
                    setCash(res.data.cash / 100);
                    setResText(res.spin_result.cash / 100);
                } else {
                    setResText(res.cash);
                }
            });
        }
    }

    //抽奖结果触发
    const onEnd = () => {
        //抽奖结果动态展示
        let target = resText;
        setResText(0);
        let startTime = Date.now();
        let duration = 2000;
        // const step = target <= 500 ? 31.25 : Math.ceil(target / 500) * 16;  // 两种情况:1.总数小于time时,就以每毫秒递增1的形式增加;2.总数达于1000,计算出每毫秒至少要递增多少
        const timer = setInterval(() => {
            setResText((pre) => {
                let after = Math.ceil((Date.now()-startTime)/duration*target*100)/100;
                if(after > target) {
                    clearInterval(timer);
                    after = target;
                }
                return after;
            });
            setButtonState("end");
        }, 16);

        //展示中奖高亮效果
        setMidActiveLight(true);

        //2秒后展示spin抽奖按钮
        setTimeout(() => {
            //设置中奖金额,为下面进度条弹框做准备
            if (cash > 0 && props.setCash) {  //设置当前中奖金额,以便: 1. 进度条弹框显示 2. 转盘主页金额展示
                props.setCash(cash);
            }
        }, 2500);

        //2秒后展示进度条
        setTimeout(() => {
            if (cash > 0 && props.setInviteWheelDialogVisible) {  //判断是否展示进度条弹框(适用于首页弹框)
                props.setInviteWheelDialogVisible(true)
            }
        }, 3000)

        //2秒后展示spin抽奖按钮
        setTimeout(() => {
            //展示spin抽奖按钮
            setButtonState("init");
        }, 3000);
    }

    const WheelButtons = (state,spinNum,resText)=> {
        switch (state){
            case "init":
                return [
                    {
                        radius: '47%',
                        imgs: [{
                            src: '../../assets/images/lucky_wheel/mfzp_1_10.png',
                            width: '90%',
                            top: '-130%'
                        }]
                    },
                    {
                        radius: '45%',
                        fonts: [{ text: spinNum, top: '-2.35rem', fontColor: "#ffffff", fontSize: "2.4rem", fontWeight:"1000",left: "-0.15rem"  }]
                    },
                    {
                        radius: '25%',
                        fonts: [{ text: "Times", top: '-0.8rem', fontColor: "#ffffff", fontSize: "0.7rem", fontWeight:"1000", left: "1.55rem"  }]
                    },
                    {
                        radius: '25%',
                        fonts: [{ text: "SPIN", top: '0.3rem', fontColor: "#ffffff", fontSize: "1.4rem", fontWeight:"1000" }]
                    },
                ];
            case "end":
                return [
                    {
                        radius: '47%',
                        imgs: [{
                            src: '../../assets/images/lucky_wheel/mfzp_1_10.png',
                            width: '90%',
                            top: '-130%'
                        }]
                    },
                    {
                        radius: '45%',
                        fonts: [{ text: "+" + resText.toFixed(2), top: '-1.7rem', fontColor: "#ffffff", fontSize: "2.3rem", fontWeight:"1000", className: "test"}]
                    },
                ];
        }
    }

  return (
      <div class={style.wheel}>
          <div class={style.wheel_img1}>
              <img src={"../assets/images/lucky_wheel/mfzp_1_3.png"} style={
   
   {width: "55%"}}/>
          </div>
          <div class={style.wheel_img2}>
              <img src={"../assets/images/lucky_wheel/mfzp_1_5.png"} style={
   
   {width: "55%"}}/>
          </div>
          <div class={style.wheel_img3}>
              <img src={"../assets/images/lucky_wheel/mfzp_1_4.png"} style={
   
   {width: "55%"}}/>
          </div>
          {midActiveLight ? (
                  <div class={style.wheel_img4}>
                      <img src={"../assets/images/lucky_wheel/mfzp_1_12.png"} />
                  </div>
          ) : (
              <div class={style.wheel_img4}>
                  <img src={""} style={
   
   {width: "60%"}}/>
              </div>
              )}


          {midActiveLight ? (
              <div class={style.wheel_img5}>
                  <img src={"../assets/images/lucky_wheel/mfzp_1_12_1.png"}/>
              </div>
              ) : (
              <div class={style.wheel_img5}>
              <img src={""} style={
   
   {width: "65%"}}/>
              </div>
              )}

          <BorderLight />

          <div class={style.wheel_canvas}>
              <LuckyWheel
                  ref={myLucky}
                  width="20rem"
                  height="20rem"
                  defaultConfig={defaultConfig}
                  blocks={blocks}
                  style={
   
   {margin: "0 auto", zIndex:"-3"}}
                  prizes={prizes}
                  buttons={WheelButtons(buttonState,spinNum,resText)}
                  onStart={() => { // 点击抽奖按钮会触发star回调
                      onStart();
                  }}
                  onEnd={() => { // 抽奖结束会触发end回调
                      onEnd();
                  }}
              />
          </div>
      </div>
  );
};

export default LuckyCanvs;

好了,大转盘抽奖大致就是这样

猜你喜欢

转载自blog.csdn.net/zhoupenghui168/article/details/133753306