不依赖任何库:实现推广转盘三连中
一个乱写的推广转盘,很乱但是效果还行
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> * { margin: 0; padding: 0; } html, body { width: 100%; height: 100%; overflow: hidden; } .container { width: 100%; height: 100%; overflow-x: hidden; overflow-y: scroll; } .canvas-wrapper { position: relative; } .button { background: transparent; outline: none; border: none; display: block; position: absolute; left: 0; right: 0; top: 56vh; border-radius: 50%; margin: 0 auto; width: 100px; height: 100px; } #canvas { display: block; } </style> </head> <body> <div class="container"> <div class="canvas-wrapper"> <button class="button"></button> <canvas id="canvas"></canvas> </div> <div>信息</div> </div> </body> <script> // 获取像素比 高清cavas function getPixelRatio(context) { var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1; return (window.devicePixelRatio || 1) / backingStore; }; var wheel = new Wheel(); // 公共类 function Wheel() { let cav = document.getElementById('canvas'); this.pointerRange = []; this.pointerIndex = 0; this.turned = []; //已经转中的下标数组 this.turnTargetRotate = []; //要转的目的数组 this.turnedCount = 0; // 转盘次数 this.startTurn = false; // 开始抽 this.delay = 20; this.speed = 1; //转速 this.ctx = cav.getContext('2d'); this.ratio = getPixelRatio(this.ctx); this.w = window.innerWidth; this.h = window.innerHeight; cav.style.width = this.w + 'px'; cav.style.height = this.h + 'px'; cav.width = this.w * this.ratio; cav.height = this.h * this.ratio; } Wheel.prototype.updatePointerIndex = function (rotate) { let rotateV = rotate % 360; let index = this.pointerRange.findIndex(d => rotateV < d); this.pointerIndex = index == -1 ? 0 : index; } // 转盘类 function AwardRound({ cx, cy, cr = 100, data = [] }) { let angleStore = 0; let sum = 0; this.cx = cx * wheel.ratio; this.cy = cy * wheel.ratio; this.cr = cr * wheel.ratio; this.count = data.length; this.data = data.map((d, i) => { sum += d.angle; let prevData = data[i - 1]; angleStore += d.angle + (i - 1 > 0 ? prevData.angle : 0); let startAngle = - 90 - (d.angle / 2); d.beginAngle = startAngle / 180 * Math.PI; d.endAngle = (startAngle + d.angle) / 180 * Math.PI; if (i == 0) { d.rotateAngle = 0; } else { d.rotateAngle = angleStore; } wheel.pointerRange.push(sum - (data[0].angle / 2)); if (d.isTurn) { let prevItem = wheel.pointerRange[i - 1]; wheel.turnTargetRotate.push({ rotate: ((wheel.pointerRange[i] - prevItem) / 2 + prevItem) + 3 * 360, index: i }) } return d; }); } AwardRound.prototype.render = function () { let iconW = 36 * wheel.ratio; this.data.forEach((d, i) => { wheel.ctx.save(); wheel.ctx.translate(this.cx, this.cy); wheel.ctx.rotate((d.rotateAngle) * Math.PI / 360); wheel.ctx.beginPath(); // 画底盘 wheel.ctx.fillStyle = d.background || '#fdf1e7'; wheel.ctx.arc(0, 0, this.cr, d.beginAngle, d.endAngle); wheel.ctx.lineTo(0, 0); wheel.ctx.fill() // 名字 wheel.ctx.fillStyle = d.color || '#000'; wheel.ctx.font = `${d.fontSize ? d.fontSize * wheel.ratio : 16}px 微软雅黑`; wheel.ctx.textAlign = 'center'; wheel.ctx.fillText(d.name || '', 0, - this.cr + 20 * wheel.ratio); // 图片 if (d.icon) { wheel.ctx.drawImage(d.icon, -iconW / 2, -this.cr + 40 * wheel.ratio, iconW, iconW); } // 浮层 wheel.ctx.fillStyle = '#000'; wheel.ctx.globalAlpha = i == wheel.pointerIndex || wheel.turned.includes(i) ? 0 : 0.5; wheel.ctx.arc(0, 0, this.cr, d.beginAngle, d.endAngle); wheel.ctx.lineTo(0, 0); wheel.ctx.fill(); wheel.ctx.restore(); }) } // 按钮类 function Button({ cx, cy }) { this.cx = cx * wheel.ratio; this.cy = cy * wheel.ratio; this.rotate = 0; } Button.prototype.render = function () { let tmpW = 25 * wheel.ratio; wheel.ctx.save() wheel.ctx.translate(this.cx, this.cy); wheel.ctx.rotate(this.rotate * Math.PI / 180); wheel.ctx.beginPath() wheel.ctx.fillStyle = '#FF910B'; wheel.ctx.arc(0, 0, 50 * wheel.ratio, 0, Math.PI * 2); wheel.ctx.fill(); wheel.ctx.moveTo(0, 0); wheel.ctx.lineTo(tmpW, 0); wheel.ctx.lineTo(0, -80 * wheel.ratio); wheel.ctx.lineTo(-tmpW, 0); wheel.ctx.fill(); wheel.ctx.restore(); } Button.prototype.update = function () { this.rotate += wheel.speed; if (wheel.startTurn) { // 开始转了 if (this.rotate >= wheel.turnTargetRotate[wheel.turnedCount].rotate) { // 达到阈值 wheel.delay--; //延时器 if (wheel.delay > 0) { // 休息一下 this.rotate = wheel.turnTargetRotate[wheel.turnedCount].rotate; // 保持抽到位置 wheel.speed = 0; //停止转动 } else { // 转下一次 wheel.turnedCount++; // 中次+1 wheel.turned.push(wheel.turnTargetRotate[wheel.turnedCount - 1].index); wheel.speed = 20; wheel.delay = 20; if (wheel.turnedCount < 3) { this.rotate = 0; } } } } wheel.updatePointerIndex(this.rotate); } // 背景类 function Background() { let img = new Image() img.src = './bg.png'; this.bgImg = img; } Background.prototype.render = function () { let iconW = (wheel.w - 10) * wheel.ratio; let iconH = iconW * (846 / 720); wheel.ctx.drawImage(this.bgImg, 5 * wheel.ratio, wheel.h * wheel.ratio - iconH, iconW, iconH) } var awardData = [ { icon: 'http://mdhw.oss-cn-hangzhou.aliyuncs.com/goods/1652065297220_maYiZc27.jpg', name: '感谢参与', background: '#fdf1e7', color: '#C24603', fontSize: '13', angle: 45, }, { icon: '', name: 'AirJordan1', background: '#FFB876', color: '#C24603', fontSize: '11', angle: 25 }, { icon: 'http://mdhw.oss-cn-hangzhou.aliyuncs.com/goods/1652065313260_JDMZ2GTW.jpg', name: 'iPhone手机', background: '#BD1600', color: '#FEE28A', fontSize: '11', angle: 25, isTurn: true, }, { icon: '', name: '蓝牙音箱', background: '#FFDAB2', color: '#C24603', fontSize: '11', angle: 25 }, { icon: 'http://mdhw.oss-cn-hangzhou.aliyuncs.com/goods/1652065297220_maYiZc27.jpg', name: '感谢参与', background: '#fdf1e7', color: '#C24603', fontSize: '13', angle: 45, }, { icon: '', name: 'AirJordan1', background: '#FFB876', color: '#C24603', fontSize: '11', angle: 25 }, { icon: 'http://mdhw.oss-cn-hangzhou.aliyuncs.com/goods/1652065313260_JDMZ2GTW.jpg', name: 'iPhone手机', background: '#BD1600', color: '#FEE28A', fontSize: '11', angle: 25, isTurn: true, }, { icon: '', name: '蓝牙音箱', background: '#FFDAB2', color: '#C24603', fontSize: '11', angle: 25 }, { icon: 'http://mdhw.oss-cn-hangzhou.aliyuncs.com/goods/1652065297220_maYiZc27.jpg', name: '感谢参与', background: '#fdf1e7', color: '#C24603', fontSize: '13', angle: 45, }, { icon: '', name: 'AirJordan1', background: '#FFB876', color: '#C24603', fontSize: '11', angle: 25 }, { icon: 'http://mdhw.oss-cn-hangzhou.aliyuncs.com/goods/1652065313260_JDMZ2GTW.jpg', name: 'iPhone手机', background: '#BD1600', color: '#FEE28A', fontSize: '11', angle: 25, isTurn: true, }, { icon: '', name: '蓝牙音箱', background: '#FFDAB2', color: '#C24603', fontSize: '11', angle: 25 }, ]; awardData = awardData.map(d => { if (d.icon) { let img = new Image(); img.src = d.icon; d.icon = img } return d }) window.onload = function () { let iconH = (wheel.w - 10) * (846 / 720); let cx = wheel.w / 2; let cr = cx - 30; let cy = wheel.h - iconH + cr + 25; var btnDom = document.querySelector('.button') var background = new Background(); var awardRound = new AwardRound({ cx: cx, cy: cy, cr: cr, data: awardData }); var button = new Button({ cx, cy, }); const clickDown = function () { if (wheel.startTurn) { btnDom.removeEventListener('click', clickDown); return; } button.rotate = 0; wheel.startTurn = true; wheel.speed = 20; } btnDom.addEventListener('click', clickDown) function doAnimate() { // 清除画布 wheel.ctx.clearRect(0, 0, 10000, 10000) // 渲染转盘背景 background.render(); // 渲染转盘 awardRound.render(); // 渲染按钮 button.update(); button.render(); if (wheel.turnedCount < 3) { requestAnimationFrame(() => { doAnimate(); }) } } doAnimate(); } </script> </html>