
实现步骤:
1、新建choujiang.js,用来放抽奖代码,代码就是未压缩版GB Canvas Turntable的代码(进行了改动,将抽奖改成了异步,必须从后台获取抽奖结果后,才会停止转动),只不过将代码编程字符串,同时将调用抽奖的代码改成了函数式,在native页面,用injectedJavaScript来调用,传入goodsArr, cw, ch, headers,即奖品列表、宽度、高度和请求头,并且引入了ajax,在抽奖按钮点击时,转盘转动的过程中,去后台获取本地中奖产品的ID,然后传给回调,转盘转动结束,就会停留在后台指定的中奖区域。在中奖回调中,用
window.ReactNativeWebView.postMessage(data)的方法向native页面发送消息:
import config from '../../utils/config';
const html = `
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>抽奖</title>
<script src="https://5.jimth.com/uploads/allimg/210514/1531434960-1.jpg"></script>
<style>
.turntable-canvas {
position: relative;
}
.turntable-canvas-bg {
top: 0;
left: 0;
position: absolute;
z-index: 1;
}
.turntable-canvas-content {
top: 0;
left: 0;
position: absolute;
z-index: 2;
}
.turntable-canvas-zhizhen {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
z-index: 3;
cursor: pointer;
}
</style>
</head>
<body ontouchstart>
<div id="turntableCanvas"></div>
<script>
var TurntableCanvas = {
go(dom, config, get, cb) {
this.dom = dom;
this.width = config.width || 300;
this.height = config.width || 300;
this.boundary = config.boundary || 1;
this.zhizhen = {
width: config.zhizhen && config.zhizhen.width || 50,
src: config.zhizhen && config.zhizhen.src || 'https://5.jimth.com/view/demoimg/choujiang-arrow.png'
};
this.bg = {
width: config.bg && config.bg.width || 20,
color: config.bg && config.bg.color || 'rgb(255,185,74)',
lampNum: config.bg && config.bg.lampNum || 12,
twinkleType: config.bg && config.bg.twinkleType || 0,
lampColor: config.bg && config.bg.lampColor || ['rgb(255,255,255)', 'rgb(255,234,119)'],
lampRadius: config.bg && config.bg.lampRadius || 3,
twinkleTime: config.bg && config.bg.twinkleTime || 500
};
this.prize = {
bgColor: config.prize && config.prize.bgColor || ['rgb(255,233,204)', 'rgb(255,247,235)'],
textColor: config.prize && config.prize.textColor || 'rgb(214,155,94)',
textStyle: config.prize && config.prize.textStyle || "16px Georgia",
textTop: config.prize && config.prize.textTop || 14,
imgTop: config.prize && config.prize.imgTop || 40,
imgWidth: config.prize && config.prize.imgWidth || 32,
imgHeight: config.prize && config.prize.imgHeight || 32
};
this.prizeList = config.prizeList;
this.deviation = 0;
for (var i = 0; i < this.prizeList.length;) {
if (i * 360 / this.prizeList.length > 270) {
this.deviation = i * 360 / this.prizeList.length - 270;
break;
} else {
i++;
}
}
this.rotate = 180 / this.prizeList.length - this.deviation;
this.get = get;
this.cb = cb;
this.init();
},
init() {
this.time = new Date().getTime();
this.dom.classList.add("turntable-canvas");
this.dom.style['width'] = this.width + 'px';
this.dom.style['height'] = this.height + 'px';
this.dom.innerHTML = '<canvas class="turntable-canvas-bg ' + this.time + 'bg" width="' + this.width + '" height="' + this.height + '"></canvas><canvas class="turntable-canvas-content ' + this.time + 'content" width="' + this.width + '" height="' + this.height + '"></canvas><img class="turntable-canvas-zhizhen" width="' + this.zhizhen.width + '"src="' + this.zhizhen.src + '">';
this.bgCanvas = document.getElementsByClassName(this.time + 'bg')[0];
this.bgCanvasContext = this.bgCanvas.getContext('2d');
this.contentCanvas = document.getElementsByClassName(this.time + 'content')[0];
this.contentCanvasContext = this.contentCanvas.getContext('2d');
this.contentCanvas.style.transform = 'rotate(' + this.rotate + 'deg)';
document.getElementsByClassName('turntable-canvas-zhizhen')[0].onclick = () => {
this.start();
}
this.t = 0;
this.drawBg();
this.drawContent();
},
drawBg() {
//绘制背景
this.bgCanvasContext.clearRect(0, 0, this.width, this.height);
this.bgCanvasContext.beginPath();
this.bgCanvasContext.fillStyle = this.bg.color;
this.bgCanvasContext.arc(this.width / 2, this.height / 2, this.width / 2, 0, 2 * Math.PI);
this.bgCanvasContext.fill();
this.bgCanvasContext.closePath();
//绘制灯
// if(this.bg.twinkleType == 0){
for (var i = 0; i < this.bg.lampNum; i++) {
this.bgCanvasContext.beginPath();
this.bgCanvasContext.fillStyle = this.bg.lampColor[(i + this.t) % 2];
this.bgCanvasContext.arc(this.width / 2 + (this.width / 2 - this.bg.width / 2) * Math.cos(360 / this.bg.lampNum * i * Math.PI / 180), this.height / 2 + (this.height / 2 - this.bg.width / 2) * Math.sin(360 / this.bg.lampNum * i * Math.PI / 180), this.bg.lampRadius, 0, 2 * Math.PI);
this.bgCanvasContext.fill();
this.bgCanvasContext.closePath();
}
// }
},
drawContent() {
//奖品扇形
for (var i = 0; i < this.prizeList.length; i++) {
this.contentCanvasContext.beginPath();
this.contentCanvasContext.moveTo(this.width / 2, this.height / 2);
this.contentCanvasContext.lineTo(this.width / 2 + (this.width / 2 - this.bg.width) * Math.cos(360 / this.prizeList.length * i * Math.PI / 180), this.height / 2 + (this.height / 2 - this.bg.width) * Math.sin(360 / this.prizeList.length * i * Math.PI / 180));
this.contentCanvasContext.arc(this.width / 2, this.height / 2, this.width / 2 - this.bg.width, 360 / this.prizeList.length * Math.PI / 180 * i, 360 / this.prizeList.length * Math.PI / 180 * (i + 1));
this.contentCanvasContext.moveTo(this.width / 2, this.height / 2);
this.contentCanvasContext.lineTo(this.width / 2 + (this.width / 2 - this.bg.width) * Math.cos(360 / this.prizeList.length * (i + 1) * Math.PI / 180), this.height / 2 + (this.height / 2 - this.bg.width) * Math.sin(360 / this.prizeList.length * (i + 1) * Math.PI / 180));
this.contentCanvasContext.fillStyle = this.prize.bgColor[i % this.prize.bgColor.length];
this.contentCanvasContext.fill();
this.contentCanvasContext.closePath();
}
//绘制文字和图片
let img = [];
for (let i = 0; i < this.prizeList.length; i++) {
if (this.prizeList[i].imgurl) {
img[i] = document.createElement("img");
img[i].src = this.prizeList[i].imgurl;
img[i].onload = () => {
this.contentCanvasContext.save();
this.contentCanvasContext.beginPath();
this.contentCanvasContext.translate(this.width / 2, this.height / 2);
this.contentCanvasContext.rotate((this.deviation - 360 / (this.prizeList.length * 2) * (i * 2 + 1)) * Math.PI / 180);
this.contentCanvasContext.translate(-this.width / 2, -this.height / 2);
this.contentCanvasContext.fillStyle = this.prize.textColor;
this.contentCanvasContext.font = this.prize.textStyle;
this.contentCanvasContext.textAlign = 'center';
this.contentCanvasContext.textBaseline = 'top';
this.contentCanvasContext.fillText(this.prizeList[i].text, this.width / 2, this.bg.width + this.prize.textTop);
this.contentCanvasContext.drawImage(img[i], this.width / 2 - this.prize.imgWidth / 2, this.bg.width + this.prize.imgTop, this.prize.imgWidth, this.prize.imgHeight)
this.contentCanvasContext.closePath();
this.contentCanvasContext.restore();
}
} else {
this.contentCanvasContext.save();
this.contentCanvasContext.beginPath();
this.contentCanvasContext.translate(this.width / 2, this.height / 2);
this.contentCanvasContext.rotate((this.deviation - 360 / (this.prizeList.length * 2) * (i * 2 + 1)) * Math.PI / 180);
this.contentCanvasContext.translate(-this.width / 2, -this.height / 2);
this.contentCanvasContext.fillStyle = this.prize.textColor;
this.contentCanvasContext.font = this.prize.textStyle;
this.contentCanvasContext.textAlign = 'center';
this.contentCanvasContext.textBaseline = 'top';
this.contentCanvasContext.fillText(this.prizeList[i].text, this.width / 2, this.bg.width + (this.prize.textTop * 3));
this.contentCanvasContext.closePath();
this.contentCanvasContext.restore();
}
}
},
start() {
this.timerApi = setInterval(() => {
this.rotate += 40;
this.contentCanvas.style.transform = 'rotate(' + this.rotate + 'deg)';
}, 100)
this.timerTimeout = setInterval(() => {
this.t = this.t == 1 ? 0 : 1;
this.drawBg();
}, this.bg.twinkleTime)
this.get((i) => {
clearInterval(this.timerApi);
this.rotate = 0;
this.rotateEnd = 360 * 2 + this.rand(360 / this.prizeList.length * i + this.boundary, 360 / this.prizeList.length * (i + 1) - this.boundary) - this.deviation;
var speed = Math.ceil(((this.rotateEnd - this.rotate) / 20));
// console.log(this.rotateEnd, speed);
this.timerInterval = setInterval(() => {
speed = Math.ceil(((this.rotateEnd - this.rotate) / 20));
// console.log(this.data.rotate, speed)
if (this.rotate + speed >= this.rotateEnd) {
this.rotate = this.rotateEnd % 360;
this.rotateEnd = this.rotateEnd % 360;
var prize = (Math.floor((Math.abs(this.rotate) + this.deviation) / (360 / this.prizeList.length)));
if (prize == this.prizeList.length) {
prize = 0;
}
this.cb(this.prizeList[prize]);
clearInterval(this.timerInterval);
clearTimeout(this.timerTimeout);
} else {
this.rotate = this.rotate + speed;
}
this.contentCanvas.style.transform = 'rotate(' + this.rotate + 'deg)';
}, 100)
})
},
rand(n, m) {
var c = m - n + 1;
return Math.floor(Math.random() * c + n);
}
}
function initChoujiang(goodsArr, cw, ch, headers) {
var TurntableCanvasConfig = {
//canvas宽高
width: cw,
height: ch,
//防止停止旋转时压住扇形边线,可适当加大
boundary: 1,
//指针图片的宽及地址
zhizhen: {
width: 50,
src: ''
},
//转盘背景配置
bg: {
width: 20,
color: 'rgb(255,185,74)',
lampNum: 12,
lampColor: ['rgb(255,255,255)', 'rgb(255,234,119)'],
lampRadius: 3,
twinkleTime: 500
},
//转盘奖品配置
prize: {
bgColor: ['rgb(255,233,204)', 'rgb(255,247,235)'],
textColor: 'rgb(214,155,94)',
textStyle: "16px Georgia",
textTop: 14,
imgTop: 40,
imgWidth: 32,
imgHeight: 32
},
//奖品列表
prizeList: goodsArr
}
var goodsName = '';
TurntableCanvas.go(document.querySelector('#turntableCanvas'), TurntableCanvasConfig, (callback) => {
//请求接口
axios.get('${config.baseUrl}/turntablePrize/luckDraw', {
headers: headers
}).then(function(response) {
var res = response.data;
if (res.code === 200) {
if (res.data.code !== 200) {
//请求失败,向native页面发送通知,给用户提示
window.ReactNativeWebView.postMessage(JSON.stringify({
code: '-1',
msg: res.data.msg
}));
} else {
//请求成功,通过后台返回的中奖ID,匹配奖品在列表中的索引,通过callback回调,来实现转盘停在中奖索引指向的产品,同时给goodsName赋值
var id = res.data.content;
var idx = '';
goodsArr.forEach((item, index) => {
if (item.id === id) {
idx = index;
goodsName = item.text;
}
})
//抽奖结果
callback(idx);
}
} else {
//请求失败,向native页面发送通知,给用户提示
window.ReactNativeWebView.postMessage(JSON.stringify({
code: '-1',
msg: '获取结果失败!'
}));
}
}).catch(function(error) {
alert(JSON.stringify(error))
//接口调用失败,向native页面发送通知,给用户提示
window.ReactNativeWebView.postMessage(JSON.stringify({
code: '-1',
msg: '接口调用失败!'
}));
});
}, (res) => {
//转盘停止转动,向native页面发送通知,提示用户抽中了 goodsName。
window.ReactNativeWebView.postMessage(JSON.stringify({
code: '200',
msg: '抽奖成功!',
name: goodsName
}));
})
}
</script>
</body>
</html>
`;
export default html;
2、在需要展示抽奖的页面引入modal,引入刚才的js,在引入相关的组件方法等
import storage from '../../utils/storage';:
import {Platform, ImageBackground, ScrollView, StyleSheet, Animated, Easing, Dimensions} from 'react-native';
import Modal from 'react-native-modal';
import html from './choujiang';
import Icon from 'react-native-vector-icons/AntDesign';
3、定义抽奖弹窗showModel、获取后台的奖品列表数据、定义注入的js,来调用上面的html的js方法,传入列表、宽高和head请求头等。
/***********************接受抽奖返回的数据*******************/
// 奖品列表的格式
// [{
// "id": 1,
// "text": "1元红包",
// "img": "aa18972bd40735fa8026091694510fb30e24084e.jpg"
// }]
const [INJECTEDJAVASCRIPT, SetINJECTEDJAVASCRIPT] = useState('');
//抽奖列表
const getTurntableListData = async () => {
//获取接口
const res = await getTurntableList();
let arr = res.data.map(item => {
let temp = {};
if(item.pic){
temp = {
id: item.id,
img: item.pic
}
}else{
temp = {
id: item.id,
text: item.prizeName
}
}
return temp;
})
if(arr.length <2){
Toast.show({type: 'error', content: '没有可抽奖的产品'});
return;
}else{
var winWidth = Dimensions.get('window').width;
// 获取用户信息,得到请求头,给webview页面的axios使用
const currentUser = await storage.getCurrentUser();
const headers = {
'ibic-token': currentUser ? currentUser.accessToken : '',
'ibic-client': Platform.select({ios: 2, android: 1}),
'ibic-version': 1,
'Accept-Language': 'zh-CN', // zh-CN 中文,en-US 英文
};
SetINJECTEDJAVASCRIPT(`initChoujiang(${JSON.stringify(arr)},${winWidth-30},${winWidth-30},${JSON.stringify(headers)})`);
}
}
//调用奖品列表
useEffect(() => {
getTurntableListData();
}, [getTurntableListData]);
// 监听函数,从webview的抽奖方法返回,可能失败,可能成功
const _onMessage = (data) => {
//{"nativeEvent":{"data":"抽奖成功了!!","canGoForward":false,"loading":false,"title":"GB Canvas Turntable","canGoBack":false,"url":"about:blank","target":1777}}
console.log('抽奖结果:',JSON.stringify(data));
let res = JSON.parse(data.nativeEvent.data);
if(res.code !== '200'){
Toast.show({type: 'error', content: res.msg});
}else{
Toast.show({type: 'success', content: '恭喜,您抽中了'+res.name+'!'});
//更新用户积分
getUserInfo();
}
}
4、点击弹出抽奖的按钮
<Item assetName="jfcj" text="积分抽奖" onPress={()=> { setShowModel(true) }}/>
5、抽奖modal
<Modal isVisible={showModel} style={styles.modal}>
<View style={styles.modalWrap}>
<TouchableOpacity right marginR-30 onPress={ () =>{ setShowModel(false) } }>
<Icon name="close" size={30} style={{color: '#fff' }} />
</TouchableOpacity>
<WebView
style={{backgroundColor:'transparent'}}
originWhitelist={['*']}
source={{html}}
onMessage={_onMessage}
injectedJavaScript={INJECTEDJAVASCRIPT}
/>
</View>
</Modal>
7、css样式:
const styles = StyleSheet.create({
modal: {
justifyContent: 'center',
alignItems: 'center',
margin: 0,
},
modalWrap:{
width:Dimensions.get('window').width,
height:Dimensions.get('window').width+30,
},
});
终于非常完美的实现了。 