Добавь тинер карты смахивания на ioni c 4 - PullRequest
4 голосов
/ 08 января 2020

Я хотел бы спросить о карточке tinder swipeable с ioni c 4 . Я не нахожу ссылку, см. Ниже: https://github.com/ionic-team/ionic-ion-swipe-cards

'

1 Ответ

2 голосов
/ 12 января 2020

Я думаю, что реализовал его, и я надеюсь, что вы сможете использовать этот код и предоставить обратную связь.

Шаблон компонента состоит из 3 частей:

  • индикатор для выбора пользователя , он получает видимость (непрозрачность) всякий раз, когда пользователь перетаскивает свой выбор в направлении да / нет

  • фактическая стопка карт

  • кнопки, которые пользователь может используйте для выбора в качестве альтернативы
    перетаскивание

Код шаблона + s css код выглядит следующим образом:

// 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>

// S CSS:

.tinder {
  width: 100%;
  height: 100%;
  overflow: hidden;
  background-color: rgba(0,0,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, которая копирует каждую карту и помещает ее в стек
  • мы используем молоток. 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, 'transform', '');
      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, 'transform', '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();
    })
  };

}

Примечание: часть:

  • @ Ввод используется для получения л список карт
  • мы используем @Output для выдачи пользователем выбора
  • мы используем @ViewChildren для отслеживания стека карт
  • карта только фактически удалена (массив .shift ()) когда переход завершен (событие окончания перехода)
  • сам компонент скрыт, если в стеке нет карточек, идея состоит в том, что мы показываем этот компонент из компонента страницы, например, ссылка на массив, который может содержать карточки

Надеюсь, это хороший пример того, как такой компонент может быть реализован

Редактор URL: https://stackblitz.com/edit/ionic-4-template-bks4dd

Демо: https://ionic-4-template-bks4dd.stackblitz.io

Артикул: Средний пост

...