幸运轮 HTML5 画布游戏
本演示展示了如何使用 Konva 创建一个互动的幸运轮游戏。轮子可以通过鼠标或触摸输入进行旋转,并会因角摩擦逐渐减速。当它停止时,将显示你的奖品!
import Konva from 'konva'; Konva.angleDeg = false; let angularVelocity = 6; const angularVelocities = []; let lastRotation = 0; let controlled = false; const numWedges = 25; const angularFriction = 0.2; let target, activeWedge, stage, layer, wheel, pointer; let finished = false; function getAverageAngularVelocity() { const total = angularVelocities.reduce((sum, vel) => sum + vel, 0); return angularVelocities.length ? total / angularVelocities.length : 0; } function purifyColor(color) { const randIndex = Math.round(Math.random() * 3); color[randIndex] = 0; return color; } function getRandomColor() { const r = 100 + Math.round(Math.random() * 55); const g = 100 + Math.round(Math.random() * 55); const b = 100 + Math.round(Math.random() * 55); return purifyColor([r, g, b]); } function getRandomReward() { const mainDigit = Math.round(Math.random() * 9); return mainDigit + '\n0\n0'; } function addWedge(n) { const s = getRandomColor(); const reward = getRandomReward(); const [r, g, b] = s; const angle = (2 * Math.PI) / numWedges; const endColor = `rgb(${r},${g},${b})`; const startColor = `rgb(${r + 100},${g + 100},${b + 100})`; const wedge = new Konva.Group({ rotation: (2 * n * Math.PI) / numWedges, }); const wedgeBackground = new Konva.Wedge({ radius: 400, angle: angle, fillRadialGradientStartPoint: 0, fillRadialGradientStartRadius: 0, fillRadialGradientEndPoint: 0, fillRadialGradientEndRadius: 400, fillRadialGradientColorStops: [0, startColor, 1, endColor], fill: '#64e9f8', fillPriority: 'radial-gradient', stroke: '#ccc', strokeWidth: 2, }); wedge.add(wedgeBackground); const text = new Konva.Text({ text: reward, fontFamily: 'Calibri', fontSize: 50, fill: 'white', align: 'center', stroke: 'yellow', strokeWidth: 1, rotation: (Math.PI + angle) / 2, x: 380, y: 30, listening: false, }); wedge.add(text); text.cache(); wedge.startRotation = wedge.rotation(); wheel.add(wedge); } function animate(frame) { // 处理轮子旋转 const angularVelocityChange = (angularVelocity * frame.timeDiff * (1 - angularFriction)) / 1000; angularVelocity -= angularVelocityChange; // 根据点的相交激活/停用楔 const shape = stage.getIntersection({ x: stage.width() / 2, y: 100, }); if (controlled) { if (angularVelocities.length > 10) { angularVelocities.shift(); } angularVelocities.push( ((wheel.rotation() - lastRotation) * 1000) / frame.timeDiff ); } else { const diff = (frame.timeDiff * angularVelocity) / 1000; if (diff > 0.0001) { wheel.rotate(diff); } else if (!finished && !controlled) { if (shape) { const text = shape.getParent().findOne('Text').text(); const price = text.split('\n').join(''); alert('你的奖品是 ' + price); } finished = true; } } lastRotation = wheel.rotation(); if (shape && (!activeWedge || shape._id !== activeWedge._id)) { pointer.y(20); new Konva.Tween({ node: pointer, duration: 0.3, y: 30, easing: Konva.Easings.ElasticEaseOut, }).play(); if (activeWedge) { activeWedge.fillPriority('radial-gradient'); } shape.fillPriority('fill'); activeWedge = shape; } } stage = new Konva.Stage({ container: 'container', width: window.innerWidth, height: 400, }); layer = new Konva.Layer(); wheel = new Konva.Group({ x: stage.width() / 2, y: 410, }); for (let n = 0; n < numWedges; n++) { addWedge(n); } pointer = new Konva.Wedge({ fillRadialGradientStartPoint: 0, fillRadialGradientStartRadius: 0, fillRadialGradientEndPoint: 0, fillRadialGradientEndRadius: 30, fillRadialGradientColorStops: [0, 'white', 1, 'red'], stroke: 'white', strokeWidth: 2, lineJoin: 'round', angle: 1, radius: 30, x: stage.width() / 2, y: 33, rotation: -90, shadowColor: 'black', shadowOffsetX: 3, shadowOffsetY: 3, shadowBlur: 2, shadowOpacity: 0.5, }); // 将组件添加到舞台 layer.add(wheel); layer.add(pointer); stage.add(layer); // 绑定事件 wheel.on('mousedown touchstart', function (evt) { angularVelocity = 0; controlled = true; target = evt.target; finished = false; }); stage.on('mouseup touchend', function () { controlled = false; angularVelocity = getAverageAngularVelocity() * 5; if (angularVelocity > 20) { angularVelocity = 20; } else if (angularVelocity < -20) { angularVelocity = -20; } angularVelocities.length = 0; }); stage.on('mousemove touchmove', function () { const mousePos = stage.getPointerPosition(); if (controlled && mousePos && target) { const x = mousePos.x - wheel.getX(); const y = mousePos.y - wheel.getY(); const atan = Math.atan(y / x); const rotation = x >= 0 ? atan : atan + Math.PI; wheel.rotation(rotation); } }); // 创建动画 const anim = new Konva.Animation(animate, layer); anim.start();