在Ionic 4上添加刷卡火种

我想问一下具有离子4的火种可刷卡。我找不到下面的链接:https://github.com/ionic-team/ionic-ion-swipe-cards

'

huyang1644 回答:在Ionic 4上添加刷卡火种

我想我实现了它,希望您可以利用此代码并提供反馈。

组件模板包括3个部分:

    用户选择的
  • 指示器,无论何时它都会获得可见性(不透明度) 用户将他们的选择拖到是/否方向上

  • 纸牌实际叠

  • 用户可以用来选择的
  • 按钮代替
    拖动

模板代码+ scss代码如下:

// HTML:

<div class="tinder" [hidden]="!cards.length">

  <div class="tinder--status">

      <div [style.opacity]="crossVisible? '1':'0'">
          <svg width="200px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
              <svg:path
                  d="M405 136.798L375.202 107 256 226.202 136.798 107 107 136.798 226.202 256 107 375.202 136.798 405 256 285.798 375.202 405 405 375.202 285.798 256z"
                  fill="#CDD6DD" />
          </svg>
      </div>

      <div [style.opacity]="heartVisible? '1':'0'">
          <svg width="200px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
              <svg:path
                  d="M349.6 64c-36.4 0-70.7 16.7-93.6 43.9C233.1 80.7 198.8 64 162.4 64 97.9 64 48 114.2 48 179.1c0 79.5 70.7 143.3 177.8 241.7L256 448l30.2-27.2C393.3 322.4 464 258.6 464 179.1 464 114.2 414.1 64 349.6 64zm-80.8 329.3l-4.2 3.9-8.6 7.8-8.6-7.8-4.2-3.9c-50.4-46.3-94-86.3-122.7-122-28-34.7-40.4-63.1-40.4-92.2 0-22.9 8.4-43.9 23.7-59.3 15.2-15.4 36-23.8 58.6-23.8 26.1 0 52 12.2 69.1 32.5l24.5 29.1 24.5-29.1c17.1-20.4 43-32.5 69.1-32.5 22.6 0 43.4 8.4 58.7 23.8 15.3 15.4 23.7 36.5 23.7 59.3 0 29-12.5 57.5-40.4 92.2-28.8 35.7-72.3 75.7-122.8 122z"
                  fill="#FFACE4" />
          </svg>
      </div>

  </div>

  <div class="tinder--cards" (pan)="handlePan($event)" (panend)="handlePanEnd($event)">

      <div #tinderCard class="tinder--card" (transitionend)="handleShift()" *ngFor="let card of cards; let i = index"
          [ngStyle]="{ zIndex: cards.length - i,transform: 'scale(' + (20 - i) / 20 + ') translateY(-' + 20 * i + 'px)' }">

          <img #tinderCardImage [src]="card.img" (load)="tinderCardImage.style.opacity = 1">
          <h3>{{ card.title }}</h3>
          <p>{{ card.description }}</p>

      </div>

  </div>

  <div class="tinder--buttons">

    <button (click)="userClickedButton($event,false)">
        <svg width="30px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
            <svg:path
                d="M405 136.798L375.202 107 256 226.202 136.798 107 107 136.798 226.202 256 107 375.202 136.798 405 256 285.798 375.202 405 405 375.202 285.798 256z"
                fill="#CDD6DD" />
        </svg>
    </button>

    <button (click)="userClickedButton($event,true)">
        <svg width="30px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
            <svg:path
                d="M349.6 64c-36.4 0-70.7 16.7-93.6 43.9C233.1 80.7 198.8 64 162.4 64 97.9 64 48 114.2 48 179.1c0 79.5 70.7 143.3 177.8 241.7L256 448l30.2-27.2C393.3 322.4 464 258.6 464 179.1 464 114.2 414.1 64 349.6 64zm-80.8 329.3l-4.2 3.9-8.6 7.8-8.6-7.8-4.2-3.9c-50.4-46.3-94-86.3-122.7-122-28-34.7-40.4-63.1-40.4-92.2 0-22.9 8.4-43.9 23.7-59.3 15.2-15.4 36-23.8 58.6-23.8 26.1 0 52 12.2 69.1 32.5l24.5 29.1 24.5-29.1c17.1-20.4 43-32.5 69.1-32.5 22.6 0 43.4 8.4 58.7 23.8 15.3 15.4 23.7 36.5 23.7 59.3 0 29-12.5 57.5-40.4 92.2-28.8 35.7-72.3 75.7-122.8 122z"
                fill="#FFACE4" />
        </svg>
    </button>

  </div>

</div>

// SCSS:

.tinder {
  width: 100%;
  height: 100%;
  overflow: hidden;
  background-color: rgba(0,0.4);
  position: absolute;
  left: 0;
  top: 0;
}

.tinder--status {
  position: absolute;
  top: 50%;
  margin-top: -30px;
  z-index: 2;
  width: 100%;
  text-align: center;
  pointer-events: none;
}

.tinder--status > div {
  transition: all 0.3s ease-in-out;
}

.tinder--status svg {
  transition: all 0.3s ease-in-out;
  position: absolute;
  width: 100px;
  margin-left: -50px;
}

.tinder--cards {
  text-align: center;
  display: flex;
  flex-direction: column;
  position: fixed;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

.tinder--card {
  display: inline-block;
  width: 260px;
  height: 70%;
  background: #FFFFFF;
  padding-bottom: 40px;
  border-radius: 8px;
  overflow: hidden;
  position: absolute;
  will-change: transform;
  transition: all 0.3s ease-in-out;
  cursor: -webkit-grab;
  cursor: -moz-grab;
  cursor: grab;
}

.moving.tinder--card {
  transition: none;
  cursor: -webkit-grabbing;
  cursor: -moz-grabbing;
  cursor: grabbing;
}

.tinder--card img {
  max-width: 100%;
  max-height: 75%;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.3s ease-in-out;
}

.tinder--card h3 {
  margin-top: 16px;
  font-size: 24px;
  padding: 0 16px;
  pointer-events: none;
}

.tinder--card p {
  margin-top: 24px;
  font-size: 16px;
  padding: 0 16px;
  pointer-events: none;
}

.tinder--buttons {
  position: absolute;
  flex: 0 0 100px;
  text-align: center;
  bottom: 20px;
  left: 0;
  right: 0;
}

.tinder--buttons button {
  border-radius: 50%;
  line-height: 50px;
  width: 50px;
  height: 50px;
  border: 0;
  background: #FFFFFF;
  display: inline-block;
  padding-top: 10px;
  margin: 0 12px;
}

.tinder--buttons button:focus {
  outline: 0;
}

一些注意事项:

  • 模板功能* ngFor,用于复制每个卡和位置 它放在堆栈中
  • 我们利用Hammer.js的平移和平移手势事件来处理拖动
  • 我们正在监听过渡结束事件,以实际从堆栈中移除卡

// TS:

import { Component,Input,ViewChildren,QueryList,ElementRef,EventEmitter,Output,Renderer2 } from '@angular/core';

@Component({
  selector: 'tinder-ui',templateUrl: 'tinder-ui.component.html',styleUrls: ['tinder-ui.component.scss'],})
export class TinderUIComponent {

  @Input('cards') cards: Array<{
    img: string,title: string,description: string
  }>;

  @ViewChildren('tinderCard') tinderCards: QueryList<ElementRef>;
  tinderCardsArray: Array<ElementRef>;

  @Output() choiceMade = new EventEmitter();

  moveOutWidth: number;
  shiftRequired: boolean;
  transitionInProgress: boolean;
  heartVisible: boolean;
  crossVisible: boolean;

  constructor(private renderer: Renderer2) { 
  }

  userClickedButton(event,heart) {
    event.preventDefault();
    if (!this.cards.length) return false;
    if (heart) {
      this.tinderCardsArray[0].nativeElement.style.transform = 'translate(' + this.moveOutWidth + 'px,-100px) rotate(-30deg)';
      this.toggleChoiceIndicator(false,true);
      this.emitChoice(heart,this.cards[0]);
    } else {
      this.tinderCardsArray[0].nativeElement.style.transform = 'translate(-' + this.moveOutWidth + 'px,-100px) rotate(30deg)';
      this.toggleChoiceIndicator(true,false);
      this.emitChoice(heart,this.cards[0]);
    };
    this.shiftRequired = true;
    this.transitionInProgress = true;
  };

  handlePan(event) {

    if (event.deltaX === 0 || (event.center.x === 0 && event.center.y === 0) || !this.cards.length) return;

    if (this.transitionInProgress) {
      this.handleShift();
    }

    this.renderer.addClass(this.tinderCardsArray[0].nativeElement,'moving');

    if (event.deltaX > 0) { this.toggleChoiceIndicator(false,true) }
    if (event.deltaX < 0) { this.toggleChoiceIndicator(true,false) }

    let xMulti = event.deltaX * 0.03;
    let yMulti = event.deltaY / 80;
    let rotate = xMulti * yMulti;

    this.renderer.setStyle(this.tinderCardsArray[0].nativeElement,'transform','translate(' + event.deltaX + 'px,' + event.deltaY + 'px) rotate(' + rotate + 'deg)');

    this.shiftRequired = true;

  };

  handlePanEnd(event) {

    this.toggleChoiceIndicator(false,false);

    if (!this.cards.length) return;

    this.renderer.removeClass(this.tinderCardsArray[0].nativeElement,'moving');

    let keep = Math.abs(event.deltaX) < 80 || Math.abs(event.velocityX) < 0.5;
    if (keep) {

      this.renderer.setStyle(this.tinderCardsArray[0].nativeElement,'');
      this.shiftRequired = false;

    } else {

      let endX = Math.max(Math.abs(event.velocityX) * this.moveOutWidth,this.moveOutWidth);
      let toX = event.deltaX > 0 ? endX : -endX;
      let endY = Math.abs(event.velocityY) * this.moveOutWidth;
      let toY = event.deltaY > 0 ? endY : -endY;
      let xMulti = event.deltaX * 0.03;
      let yMulti = event.deltaY / 80;
      let rotate = xMulti * yMulti;

      this.renderer.setStyle(this.tinderCardsArray[0].nativeElement,'translate(' + toX + 'px,' + (toY + event.deltaY) + 'px) rotate(' + rotate + 'deg)');

      this.shiftRequired = true;

      this.emitChoice(!!(event.deltaX > 0),this.cards[0]);
    }
    this.transitionInProgress = true;
  };

  toggleChoiceIndicator(cross,heart) {
    this.crossVisible = cross;
    this.heartVisible = heart;
  };

  handleShift() {
    this.transitionInProgress = false;
    this.toggleChoiceIndicator(false,false)
    if (this.shiftRequired) {
      this.shiftRequired = false;
      this.cards.shift();
    };
  };

  emitChoice(heart,card) {
    this.choiceMade.emit({
      choice: heart,payload: card
    })
  };

  ngAfterViewInit() {
    this.moveOutWidth = document.documentElement.clientWidth * 1.5;
    this.tinderCardsArray = this.tinderCards.toArray();
    this.tinderCards.changes.subscribe(()=>{
      this.tinderCardsArray = this.tinderCards.toArray();
    })
  };

}

注意部分:

  • @Input用于获取卡片列表
  • 我们利用@Output发出用户的选择
  • 我们使用@ViewChildren跟踪纸牌堆栈
  • 仅在转换完成(转换结束事件)时才实际移除卡(array.shift())
  • 如果堆栈中没有卡片,则组件本身是隐藏的,例如,我们通过引用可以包含卡片的数组来显示页面组件中的该组件

希望这是如何实现此组件的一个很好的例子

编辑者网址:https://stackblitz.com/edit/ionic-4-template-bks4dd

演示:https://ionic-4-template-bks4dd.stackblitz.io

文章:medium post

本文链接:https://www.f2er.com/2802144.html

大家都在问