实现步骤:
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="http://www.5imoban.net/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 || 'http://www.5imoban.net/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, }, });终于非常完美的实现了。