1

I'm try to create like this example animated for my Ionic mobile application, I have small issue , actually this animated part not working for me, anyone know know how to put that script part correctly for Ionic, Please help me to fix that issue Thanks

this is my code sample my live code part without animated

this is my code

html

<div class="claps">
  <button id="clap" class="clap">
  <span>
    <!--  SVG Created by Luis Durazo from the Noun Project  -->
    <svg id="clap--icon" xmlns="http://www.w3.org/2000/svg" viewBox="-549 338 100.1 125">
  <path d="M-471.2 366.8c1.2 1.1 1.9 2.6 2.3 4.1.4-.3.8-.5 1.2-.7 1-1.9.7-4.3-1-5.9-2-1.9-5.2-1.9-7.2.1l-.2.2c1.8.1 3.6.9 4.9 2.2zm-28.8 14c.4.9.7 1.9.8 3.1l16.5-16.9c.6-.6 1.4-1.1 2.1-1.5 1-1.9.7-4.4-.9-6-2-1.9-5.2-1.9-7.2.1l-15.5 15.9c2.3 2.2 3.1 3 4.2 5.3zm-38.9 39.7c-.1-8.9 3.2-17.2 9.4-23.6l18.6-19c.7-2 .5-4.1-.1-5.3-.8-1.8-1.3-2.3-3.6-4.5l-20.9 21.4c-10.6 10.8-11.2 27.6-2.3 39.3-.6-2.6-1-5.4-1.1-8.3z"/>
  <path d="M-527.2 399.1l20.9-21.4c2.2 2.2 2.7 2.6 3.5 4.5.8 1.8 1 5.4-1.6 8l-11.8 12.2c-.5.5-.4 1.2 0 1.7.5.5 1.2.5 1.7 0l34-35c1.9-2 5.2-2.1 7.2-.1 2 1.9 2 5.2.1 7.2l-24.7 25.3c-.5.5-.4 1.2 0 1.7.5.5 1.2.5 1.7 0l28.5-29.3c2-2 5.2-2 7.1-.1 2 1.9 2 5.1.1 7.1l-28.5 29.3c-.5.5-.4 1.2 0 1.7.5.5 1.2.4 1.7 0l24.7-25.3c1.9-2 5.1-2.1 7.1-.1 2 1.9 2 5.2.1 7.2l-24.7 25.3c-.5.5-.4 1.2 0 1.7.5.5 1.2.5 1.7 0l14.6-15c2-2 5.2-2 7.2-.1 2 2 2.1 5.2.1 7.2l-27.6 28.4c-11.6 11.9-30.6 12.2-42.5.6-12-11.7-12.2-30.8-.6-42.7m18.1-48.4l-.7 4.9-2.2-4.4m7.6.9l-3.7 3.4 1.2-4.8m5.5 4.7l-4.8 1.6 3.1-3.9"/>
</svg>
  </span>
  <span id="clap--count" class="clap--count"></span>
  <span id="clap--count-total" class="clap--count-total"></span>
</button>
  </div>

css

  $btn-dimension: 80px;
    $primary-color: rgba(189,195,199 ,1);
    $secondary-color: rgba(39,174,96 ,1);
    @mixin debug {
      outline: 1px solid red;
    }



    /*========================
        BASIC styles
      =======================*/
    * {
      box-sizing: border-box
    }
    .claps {margin-top: 10rem;margin-left: 5rem;}
    /*========================
        BUTTON styles
      =======================*/
    .clap {
      position: relative;
      outline: 1px solid transparent;
      border-radius: 50%;
      border: 1px solid $primary-color;
      width: $btn-dimension;
      height: $btn-dimension; 
      background: none;
      &:after {
        content: "";
        position: absolute;
        top: 0;
        left: 0;
        display: block;
        border-radius: 50%;
        //border: 1px solid $primary-color;
        width: $btn-dimension - 1px;
        height: $btn-dimension - 1px;
      }
      &:hover {
       cursor: pointer;
       border: 1px solid $secondary-color;
       transition: border-color 0.3s ease-in;
        &:after {
          animation: shockwave 1s ease-in infinite;
        } 
      }
      svg {
        width: 40px;
        fill: none;
        stroke: $secondary-color;
        stroke-width: 2px;
        &.checked {
          fill: $secondary-color;
          stroke: #fff;
          stroke-width: 1px;
        }
      }
      .clap--count {
        position: absolute;
        top: -$btn-dimension/1.6;
        left: $btn-dimension/4;
        font-size: 0.8rem;
        color: white;
        background: $secondary-color;
        border-radius: 50%;
        height: $btn-dimension/2;
        width: $btn-dimension/2;
        line-height: $btn-dimension/2;
      }
      .clap--count-total {
        position: absolute;
        font-size: 0.8rem;
        width: $btn-dimension;
        text-align: center;
        left: 0;
        top: -$btn-dimension/3.5;
        color: $primary-color; 
      }
    }



    /*====================
            Message
    ======================*/
    #message {
      position: absolute;
      bottom: 20px;
      color: $secondary-color;
      line-height: 1.52rem;
      padding: 1rem;
      font-size: 0.9rem;
      a {
        color: $primary-color
      }
    }
@keyframes shockwave {
  0% {
    transform: scale(1);
    box-shadow: 0 0 2px $secondary-color;
    opacity: 1;
  }

  100% {
    transform: scale(1);
    opacity: 0;
    box-shadow: 0 0 50px darken($secondary-color, 20%), inset 0 0 10px $secondary-color;
  }
}

I want to know how to put this part correctly for Ionic

const clap = document.getElementById('clap')
const clapIcon = document.getElementById('clap--icon')
const clapCount = document.getElementById('clap--count')
const clapTotalCount = document.getElementById('clap--count-total')
const initialNumberOfClaps = generateRandomNumber(500, 10000);
const btnDimension = 80
const tlDuration = 300
let numberOfClaps = 0
let clapHold;

const triangleBurst = new mojs.Burst({
  parent: clap,
  radius: {50:95},
  count: 5,
  angle: 30,
  children: {
    shape: 'polygon',
    radius: {6: 0},
    scale: 1,
    stroke: 'rgba(211,84,0 ,0.5)',
    strokeWidth: 2,
    angle: 210,
    delay: 30,
    speed: 0.2,
    easing: mojs.easing.bezier(0.1, 1, 0.3 ,1),
    duration: tlDuration
  } 
})
const circleBurst = new mojs.Burst({
  parent: clap,
  radius: {50:75},
  angle: 25,
  duration: tlDuration,
  children: {
    shape: 'circle',
    fill: 'rgba(149,165,166 ,0.5)',
    delay: 30,
    speed: 0.2,
    radius: {3: 0},
    easing: mojs.easing.bezier(0.1, 1, 0.3, 1),
  }
})
const countAnimation = new mojs.Html({
  el: '#clap--count',
  isShowStart: false,
  isShowEnd: true,
  y: {0: -30},
  opacity: {0:1},
  duration: tlDuration
}).then({
  opacity: {1:0},
  y: -80,
  delay: tlDuration/2
})
const countTotalAnimation = new mojs.Html({
  el: '#clap--count-total',
  isShowStart: false,
  isShowEnd: true,
  opacity: {0:1},
  delay: 3*(tlDuration)/2,
  duration: tlDuration,
  y: {0: -3}
})
const scaleButton = new mojs.Html({
  el: '#clap',
  duration: tlDuration,
  scale: {1.3: 1},
  easing: mojs.easing.out
})
clap.style.transform = "scale(1, 1)" /*Bug1 fix*/

const animationTimeline = new mojs.Timeline()
animationTimeline.add([
  triangleBurst,
  circleBurst,
  countAnimation,
  countTotalAnimation,
  scaleButton
])


clap.addEventListener('click', function() {
   repeatClapping();
})

clap.addEventListener('mousedown', function() {
   clapHold = setInterval(function() {
   repeatClapping();
   }, 400)
})

clap.addEventListener('mouseup', function() {
 clearInterval(clapHold);
})


function repeatClapping() {
 updateNumberOfClaps()
 animationTimeline.replay()
 clapIcon.classList.add('checked')
}

function updateNumberOfClaps() {
  numberOfClaps < 50 ? numberOfClaps++ : null
  clapCount.innerHTML = "+" + numberOfClaps
  clapTotalCount.innerHTML = initialNumberOfClaps + numberOfClaps
}

function generateRandomNumber(min, max) {
  return Math.floor(Math.random()*(max-min+1)+min);
}
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
core114
  • 5,155
  • 16
  • 92
  • 189
  • 1
    Initially I would start with implementing functionality, like clapping count increases when user clicks. simple [here](https://stackblitz.com/edit/ionic-pmzppf?file=pages%2Fhome%2Fhome.html) – omurbek Mar 02 '18 at 04:50
  • @OmurbekKadyrbekov sir, your solution is really appreciate, but whers is the put animated part like this https://codepen.io/anon/pen/QQoqjg – core114 Mar 02 '18 at 04:53
  • It uses mojs library, you will need to import it, see [here how to import js file](https://stackoverflow.com/questions/43833895/how-to-include-js-file-in-ionic-3). And then you will rewrite js file in `angular` style – omurbek Mar 02 '18 at 04:54

1 Answers1

6

I would create dedicated component like clap

But first you need to add mojs to your page

<script src="https://cdnjs.cloudflare.com/ajax/libs/mo-js/0.288.1/mo.min.js"></scrip>

Since we don't have typings for mojs you can just define it as any:

declare let mojs: any;

(Note: you can create typings something like How to extend js class from outside library in TypeScript / Using mojs in TypeScript if you want)

Now it's time to create our custom component.

We're going to use ViewChild instead of document.getElementById to hold of reference to DOM element. For that we need to put template references on elements (<button #clap). We will use that references instead of selectors like el: '#clap--count-total', so our component will be scope safed

Also we won't listen to dom events through .addEventListener but rather via element output events like (click)

So here's the code

clap.component.html

<button #clap class="clap"
     (click)="repeatClapping()" 
     (mousedown)="onClapMouseDown()" 
     (mouseup)="onClapMouseUp()">
  <span>
    <svg [class.checked]="clapIconChecked" ...>
      ...
    </svg>
  </span>
  <span #clapCount class="clap--count">+{{currentClaps}}</span>
  <span #clapCountTotal class="clap--count-total">{{claps}}</span>
</button>

clap.component.ts

import { Component, ElementRef, Input, OnInit, ViewChild, EventEmitter, Output } from '@angular/core';

declare let mojs: any;

@Component({
  selector: 'clap',
  templateUrl: './clap.component.html',
  styleUrls: ['./clap.component.css']
})
export class ClapComponent implements OnInit {

  @Input() claps = 0;

  @Input() tlDuration = 300;

  @Output() clapsChange = new EventEmitter();

  @ViewChild('clap') clapEl: ElementRef;

  @ViewChild('clapCount') clapCountEl: ElementRef;

  @ViewChild('clapCountTotal') clapCountTotalEl: ElementRef;

  currentClaps: number = 0;

  clapIconChecked = false;

  private animationTimeline: any;

  private clapHold: any;

  ngOnInit() {
    const triangleBurst = new mojs.Burst({
      parent: this.clapEl.nativeElement,
      radius: {50: 95},
      count: 5,
      angle: 30,
      children: {
        shape: 'polygon',
        radius: {6: 0},
        scale: 1,
        stroke: 'rgba(211,84,0 ,0.5)',
        strokeWidth: 2,
        angle: 210,
        delay: 30,
        speed: 0.2,
        easing: mojs.easing.bezier(0.1, 1, 0.3, 1),
        duration: this.tlDuration
      }
    });

    const circleBurst = new mojs.Burst({
      parent: this.clapEl.nativeElement,
      radius: {50: 75},
      angle: 25,
      duration: this.tlDuration,
      children: {
        shape: 'circle',
        fill: 'rgba(149,165,166 ,0.5)',
        delay: 30,
        speed: 0.2,
        radius: {3: 0},
        easing: mojs.easing.bezier(0.1, 1, 0.3, 1),
      }
    });

    const countAnimation = new mojs.Html({
      el: this.clapCountEl.nativeElement,
      isShowStart: false,
      isShowEnd: true,
      y: {0: -30},
      opacity: {0: 1},
      duration: this.tlDuration
    }).then({
      opacity: {1: 0},
      y: -80,
      delay: this.tlDuration / 2
    });

    const countTotalAnimation = new mojs.Html({
      el: this.clapCountTotalEl.nativeElement,
      isShowStart: false,
      isShowEnd: true,
      opacity: {0: 1},
      delay: 3 * (this.tlDuration) / 2,
      duration: this.tlDuration,
      y: {0: -3}
    });

    const scaleButton = new mojs.Html({
      el: this.clapEl.nativeElement,
      duration: this.tlDuration,
      scale: {1.3: 1},
      easing: mojs.easing.out
    });

    this.clapEl.nativeElement.style.transform = 'scale(1, 1)';

    this.animationTimeline = new mojs.Timeline();
    this.animationTimeline.add([
      triangleBurst,
      circleBurst,
      countAnimation,
      countTotalAnimation,
      scaleButton
    ]);
  } 

  repeatClapping() {
    if (this.currentClaps > 49) {
      alert('Limit 50 claps');
      return;
    }

    this.currentClaps++;
    this.claps++;
    this.clapsChange.emit(this.claps);

    this.animationTimeline.replay();
    this.clapIconChecked = true;
  }

  onClapMouseDown() {
    this.clapHold = setInterval(() => {
      this.repeatClapping();
    }, 400);
  }

  onClapMouseUp() {
    clearInterval(this.clapHold);
  }
}

As you can see there is no magic here. It's simple component with two-way binding that we can use anywhere as follows:

home.html

<clap [(claps)]="claps"></clap>

home.ts

export class HomePage {
  claps = generateRandomNumber(500, 10000);
}

function generateRandomNumber(min, max) {
  return Math.floor(Math.random() * (max - min + 1) + min);
}

enter image description here

Try it out Example

yurzui
  • 205,937
  • 32
  • 433
  • 399
  • Sir ,I face the some error , `Typescript Error Cannot find name 'generateRandomNumber'. claps = generateRandomNumber(500, 10000);` – core114 Mar 05 '18 at 04:08
  • I moved `generateRandomNumber` to `home.ts` https://ng-run.com/edit/zid3Do5mGNPUwTEE48fu?open=app%2Fpages%2Fhome%2Fhome.ts&layout=1 – yurzui Mar 05 '18 at 04:09
  • i added that part, but display error `Typescript Error Cannot find name 'generateRandomNumber'. claps = generateRandomNumber(500, 10000); ` – core114 Mar 05 '18 at 04:11
  • this is my added code part `export class SupportPage { claps = generateRandomNumber(500, 10000); constructor(public navCtrl: NavController, public navParams: NavParams,public viewCtrl: ViewController) { function generateRandomNumber(min, max) { return Math.floor(Math.random() * (max - min + 1) + min); } }` – core114 Mar 05 '18 at 04:12
  • Function should be outside of class. Or you can use method like https://ng-run.com/edit/I0WASt0PdRT4pxrji4cb?open=app%2Fpages%2Fhome%2Fhome.ts&layout=1 – yurzui Mar 05 '18 at 04:13
  • Sir ,can you please check it, its not work for me https://stackblitz.com/edit/ionic-tkgtjz – core114 Mar 05 '18 at 04:49
  • It won't work in stackblitz. mojs creates `div` inside body to calculate some parameters but stackblitz removes this `div` And you made mistake when changing index.html – yurzui Mar 05 '18 at 04:52
  • Here's plunker version https://plnkr.co/edit/r7LJNtRhfIlF9BEUI6Mo?p=preview Notice that I added script inside body because mojs tries insert div inside body and if we add mojs in head there is no body tag yet – yurzui Mar 05 '18 at 04:57
  • sir now my error is `Error: Uncaught (in promise): ReferenceError: mojs is not defined ReferenceError: mojs is not defined`, I added `mojs` for index .html – core114 Mar 05 '18 at 05:11
  • Do you get this error In stackblitz? I said it won't work there. See my two demos – yurzui Mar 05 '18 at 05:12
  • no sir , I get that error for my an Ionic project > home page – core114 Mar 05 '18 at 05:13
  • Did you add it inside body? – yurzui Mar 05 '18 at 05:14
  • yeah I added index.html to – core114 Mar 05 '18 at 05:14
  • Can you share your repository? – yurzui Mar 05 '18 at 05:16
  • sir now its working, my mistake im add ` mojs` to header tag, now i added inside the body its working fine – core114 Mar 05 '18 at 05:17
  • Sir thanks for your valuable time and again thanks for the guide me – core114 Mar 05 '18 at 05:17
  • You can take the bounty for after 8hours – core114 Mar 05 '18 at 05:18