Slot Machine spinning effect in angular2

2019-08-20 19:20发布

问题:

Im working on a personal project where I have a number of cards and would like to spin through them vertically multiple times and end up on a random card. I'm recreating my project in angular2 and am a little lost on how I would animate the spinning. This is what I have so far...

card.component.ts:

@Component({
    selector: 'app-card',
    styleUrls: ['./card.component.css'],
    templateUrl: './card.component.html',
    animations: [
        trigger('spinCard', [
            state('inactive', style({
                transform: 'translateY(0px)'
            })),
            state('active', style({
                transform: 'translateY(100%)'
            })),
            transition('inactive => active', animate('200ms ease-in-out')),
            transition('active => inactive', animate('200ms ease-in-out'))
        ])
    ]
})
export class CardComponent { 
    @Input()
    cardState: string;
}

card.component.html:

<div class="card {{cardStyle}}" [@spinCard]="cardState">
    <div class="inside-text" id="{{cardId}}">{{cardName}}</div>
</div>

spinner.component.html:

<div class="spinner" id="spinner">

<div class="game-container">
    <div class="game-overlay"></div>
    <div class="game-area" id="game-area">
        <app-card *ngFor="let card of (cards | spinnablePipe: true)" cardState={{cardState}} cardName={{card.name}} cardId={{cardId}} cardStyle={{cardStyle}}></app-card>
    </div>
</div>

<div class="spin-layout">
    <button type="button" class="spin-btn green-btn" aria-label="Spin" (click)="animateSpin()">
        <span>S P I N</span>
    </button>
</div>

In native JS, I simply requestAnimationFrame() and change the css and html through JS. However I'm not too sure how to do this the Angular-way.

I'd like to be able to hit a button, the cards would spin within a container and the cards would come to a stop on a random card.

My best idea so far was just to add 5ish sets of identical cards to the container and animate those. I don't think this is a correct approach tho, and also I'm not sure how I would randomize where they'd stop.

Would love some help! Thanks!

回答1:

try following example for spinning cards

app.component.html

<div class="game-area">
    <div class="card" *ngFor="let card of cards" [@cardSpinner]="card.state" [style.background]="card.color">
        <p style="font-size: 24px; text-align: center;">{{card.value}}</p>
    </div>
</div>

<button type="button" class="spin-btn green-btn" aria-label="Spin" (click)="animateSpin()">
    <span>S P I N</span>
</button>

app.component.scss

.game-area {
    box-sizing: border-box;
    position: relative;
    height: 80px;
    overflow: hidden;

    .card {
        bottom: 0;
        box-sizing: border-box;
        height: 80px;
        left: 0;
        position: absolute;
        right: 0;
        width: 80px;
    }
}

app.component.ts

import { Component } from '@angular/core';
import { trigger, state, animate, transition,
  style } from '@angular/animations';

@Component({
  selector: 'ap-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  animations: [
    trigger('cardSpinner', [
      state('in', style({ opacity: 1, transform: 'translateY(0)' })),
      state('out', style({ opacity: 0, display: 'none', transform: 'translateY(-100%)' })),
      transition('in => out', [
        style({ transform: 'translateY(0)', opacity: 1 }),
        animate('0.1s', style({ transform: 'translateY(-100%)', opacity: 0 }))
      ]),
      transition('out => in', [
        style({ transform: 'translateY(100%)', opacity: 0 }),
        animate('0.1s', style({ transform: 'translateY(0)', opacity: 1 }))
      ])
    ])
  ]
})
export class AppComponent {

  currentIndex = 0;
  intervalInstance;
  cards = [
    {value: 0, state: 'out', color: '#F44336'},
    {value: 1, state: 'out', color: '#E91E63'},
    {value: 2, state: 'out', color: '#9C27B0'},
    {value: 3, state: 'out', color: '#673AB7'},
    {value: 4, state: 'out', color: '#3F51B5'},
    {value: 5, state: 'out', color: '#2196F3'},
    {value: 6, state: 'out', color: '#03A9F4'},
    {value: 7, state: 'out', color: '#00BCD4'},
    {value: 8, state: 'out', color: '#009688'},
    {value: 9, state: 'out', color: '#4CAF50'}
  ];

  animateSpin() {
    this.cards.forEach(card => card.state = 'out');
    this.currentIndex = 0;
    this.cards[this.currentIndex].state = 'in';

    this.intervalInstance = setInterval(() => {
      this.currentIndex++;
      if (this.currentIndex === this.cards.length) {
        this.currentIndex = 0;
      }
      if (this.currentIndex !== 0 ) {
        this.cards[this.currentIndex - 1].state = 'out';
      } else {
        this.cards[this.cards.length - 1].state = 'out';
      }
      this.cards[this.currentIndex].state = 'in';
    }, 100);

    const itemIndex = Math.floor((Math.random() * ((this.cards.length * 5) - this.cards.length)) + this.cards.length);
    console.log(itemIndex);
    setTimeout(() => {
      clearInterval(this.intervalInstance);
      const randomCard = this.cards.filter(card => card.state === 'in');
      console.log(randomCard);
    }, itemIndex * 100);
  }
}