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

1.需求

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

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 (
         
         
    )
}
 
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(
        () => (
            
{borderActive == 1 ? (
    {[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 (
  • ); })}
): (
    {[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 (
  • ); })}
)}
), [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 (
{midActiveLight ? (
) : (
)} {midActiveLight ? (
) : (
)}
{ // 点击抽奖按钮会触发star回调 onStart(); }} onEnd={() => { // 抽奖结束会触发end回调 onEnd(); }} />
); }; export default LuckyCanvs;

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

你可能感兴趣的:(前端,#,js,react.js,前端,lucky-canvs,大转盘抽奖)