有时候在项目开发时,要实现一个大转盘抽奖功能,该如何实现呢?效果图如下:
说明:
点击"抽奖"按钮,弹出转盘抽奖弹框,弹框说明如下:
- 标题(弹框最上面)
- 弹框渲染图片(如上图$图片)
- 弹框背景图(两张:一张底图一张跟随奖励转动的图)
- 奖励以及图片(奖励从后台获取,并配置到对应的奖励图片数组中)
- 可抽奖次数(转盘中间示意数字)
- 转盘高亮图片(当抽奖完成后,高亮某个中奖图片)
- 跑马灯(转盘周边跑马灯效果)
- 中奖后的弹框(中奖高亮后弹出中奖说明)
把上面需要实现的功能写成一个组件,页面直接调用该组件即可,这样就可以在很多页面上实现大转盘功能,功能说明如下:
- 点击"抽奖按钮",弹出抽奖转盘弹框
- 点击弹框隐藏按钮,隐藏弹框
- 判断用户是否登录,并获取转盘次数
- 构建转盘(需使用大转盘抽奖(lucky canvs)插件)
- 弹框标题
- 弹框转盘背景图以及渲染效果图
- 转盘周边跑马灯效果渲染
- 转盘背景图渲染
- 转盘次数渲染(从主页传入,主页的也是从后台获取的)
- 转盘奖项以及图片渲染(从后台获取)
- 中间后中奖项高亮渲染
- 点击转盘抽奖按钮进行中奖逻辑操作(lucky canvs插件功能)
- 中奖完成后高亮以及减少抽奖次数等后续逻辑操作
代码如下:
要引入组件页面的代码:
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;
好了,大转盘抽奖大致就是这样