You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular 4/Ionic 3幸运轮盘指定位置停止及不重复抽取需求

解决Ionic 3/Angular 4幸运轮盘的精准停位与不重复抽取问题

嘿,我来帮你搞定这两个核心问题:让轮盘精准停在指定位置,以及确保抽到的物品在全部被抽完前不会重复出现。咱们一步步来改代码:

核心需求拆解

你需要实现两个关键功能:

  • 精准停位:替代随机减速逻辑,计算目标物品对应的角度,让轮盘刚好停在该位置
  • 无重复抽取:维护一个可用物品列表,每次抽取后移除已选中的,耗尽后自动重置

修改后的完整组件代码

import { Component, ViewChild, ElementRef } from '@angular/core';
import { NavController, NavParams } from 'ionic-angular';

@Component({
  selector: 'page-wheel',
  templateUrl: 'wheel.html',
})
export class WheelPage {
  @ViewChild('myCanvas') myCanvas: ElementRef;
  @ViewChild('spin') spin: ElementRef;
  
  public ctx: CanvasRenderingContext2D;
  color = ['#fbc', '#f88', '#fbc', '#f88', '#fbc', '#f88', "#fbc", "#f67"];
  originalLabels = ['Beer', 'Peg', 'Shot', 'Bottle', 'Food', 'Beer1', 'Bomb', "Fire"];
  availableLabels: string[]; // 存储当前可抽取的物品
  slices: number;
  sliceDeg: number;
  deg: number = 0;
  public speed: number = 0;
  isSpinning: boolean = false;
  isStopping: boolean = false;
  width: number;
  center: number;
  targetDeg: number; // 目标停止角度

  constructor(public navCtrl: NavController, public navParams: NavParams) {
    this.slices = this.originalLabels.length;
    this.sliceDeg = 360 / this.slices;
    this.resetAvailableItems(); // 初始化可用物品列表
    
    // 设置画布尺寸(修复原代码的画布初始化问题)
    this.width = 300;
    this.center = this.width / 2;
  }

  ngAfterViewInit(): void {
    this.ctx = (<HTMLCanvasElement>this.myCanvas.nativeElement).getContext('2d');
    // 明确设置画布宽高
    (<HTMLCanvasElement>this.myCanvas.nativeElement).width = this.width;
    (<HTMLCanvasElement>this.myCanvas.nativeElement).height = this.width;
    this.drawImg();
  }

  // 重置可用物品列表(全部物品重新可抽)
  resetAvailableItems() {
    this.availableLabels = [...this.originalLabels];
  }

  // 随机选择一个可用物品,并计算其对应的目标角度
  selectTargetItem(): string {
    // 如果所有物品都被抽过,重置列表
    if (this.availableLabels.length === 0) {
      this.resetAvailableItems();
    }
    const randomIndex = Math.floor(Math.random() * this.availableLabels.length);
    const selectedItem = this.availableLabels[randomIndex];
    
    // 计算目标角度:让顶部指针刚好指向物品的中间位置
    const originalIndex = this.originalLabels.indexOf(selectedItem);
    // 指针默认在顶部(90度位置),调整角度对齐物品
    this.targetDeg = (360 - (originalIndex * this.sliceDeg + this.sliceDeg/2) + 90) % 360;
    // 让轮盘至少转2圈再停,增强视觉体验
    this.targetDeg += 720;
    
    // 从可用列表中移除已选中的物品
    this.availableLabels.splice(randomIndex, 1);
    return selectedItem;
  }

  deg2rad(deg: number): number {
    return deg * Math.PI / 180;
  }

  drawSlice(deg: number, color: string) {
    this.ctx.beginPath();
    this.ctx.fillStyle = color;
    this.ctx.moveTo(this.center, this.center);
    this.ctx.arc(this.center, this.center, this.width / 2, this.deg2rad(deg), this.deg2rad(deg + this.sliceDeg));
    this.ctx.lineTo(this.center, this.center);
    this.ctx.fill();
  }

  drawText(deg: number, text: string) {
    this.ctx.save();
    this.ctx.translate(this.center, this.center);
    this.ctx.rotate(this.deg2rad(deg));
    this.ctx.textAlign = "right";
    this.ctx.fillStyle = "#fff";
    this.ctx.font = 'bold 20px sans-serif'; // 调整字体大小适配画布
    this.ctx.fillText(text, this.center - 20, 10);
    this.ctx.restore();
  }

  drawImg() {
    this.ctx.clearRect(0, 0, this.width, this.width);
    let currentDeg = this.deg % 360;
    for (var i = 0; i < this.slices; i++) {
      this.drawSlice(currentDeg, this.color[i]);
      this.drawText(currentDeg + this.sliceDeg / 2, this.originalLabels[i]);
      currentDeg += this.sliceDeg;
    }
  }

  anim() {
    if (this.isSpinning) {
      this.deg += this.speed;

      // 加速阶段:速度提升到最大值
      if (!this.isStopping && this.speed < 10) {
        this.speed += 0.5;
      }

      // 减速阶段:接近目标角度时逐步减速
      if (this.isStopping) {
        // 计算当前角度与目标角度的最小差值
        const diff = Math.abs((this.targetDeg - this.deg + 360) % 360);
        if (diff < 180) {
          // 越接近目标,减速越快
          this.speed *= 0.95;
        }

        // 当速度足够慢且角度精准对齐时,停止动画
        if (this.speed < 0.3 && diff < 1) {
          this.isSpinning = false;
          this.isStopping = false;
          this.speed = 0;
          // 获取最终选中的物品
          const finalDeg = this.deg % 360;
          const ai = Math.floor(((360 - finalDeg - 90) % 360) / this.sliceDeg);
          alert(`You got:\n${this.originalLabels[ai]}`);
          return;
        }
      }

      this.drawImg();
      window.requestAnimationFrame(this.anim.bind(this));
    }
  };

  onSpinClick() {
    if (!this.isSpinning) {
      this.isSpinning = true;
      this.isStopping = false;
      this.speed = 0;
      this.selectTargetItem(); // 选择目标物品并计算角度
      this.anim();
    }
  }

  onStopClick(){
    if (this.isSpinning && !this.isStopping) {
      this.isStopping = true;
    }
  }
}

修改后的HTML代码

<ion-header>
  <ion-navbar>
    <ion-title text-center class="fontMont-Regular">Spin Wheel</ion-title>
  </ion-navbar>
</ion-header>

<ion-content no-padding class="setBackground">
  <ion-row style="margin-top:15%">
    <ion-col col-12 text-center>
      <div id="wheel" style="position: relative;">
        <canvas #myCanvas></canvas>
        <!-- 添加顶部指针标记,让用户清晰看到停止位置 -->
        <div style="position: absolute; top: 0; left: 50%; transform: translateX(-50%); width: 0; height: 0; border-left: 15px solid transparent; border-right: 15px solid transparent; border-top: 30px solid #ff0000;"></div>
      </div>
    </ion-col>
    <ion-col offset-1 col-10 style="display: flex; gap: 10px; margin-top: 20px;">
      <button class="spinButton" (click)="onSpinClick()" [disabled]="isSpinning">Spin!</button>
      <button class="stopButton" (click)="onStopClick()" [disabled]="!isSpinning || isStopping">Stop!</button>
    </ion-col>
  </ion-row>
</ion-content>

关键改动说明

1. 精准停位实现

  • 新增targetDeg变量存储目标停止角度,通过selectTargetItem计算选中物品对应的角度
  • 重构动画逻辑:先加速到最大值,收到停止指令后,根据与目标角度的差值逐步减速,直到精准停在目标位置
  • 强制轮盘至少转2圈再停止,提升用户体验

2. 无重复抽取实现

  • 新增availableLabels数组存储当前可抽取的物品,originalLabels保留原始列表
  • selectTargetItem函数负责随机选择可用物品,移除后添加到已抽列表,当可用列表为空时自动重置
  • 确保所有物品都被抽过一次后才会重复出现

3. 其他优化

  • 修复了画布尺寸初始化问题,确保轮盘显示正常
  • 添加了顶部指针标记,让用户更清晰识别停止位置
  • 优化按钮状态,防止重复点击导致的逻辑混乱

内容的提问来源于stack exchange,提问作者chirag kataria

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 10:02:29