Problem w animacji

0

Cześć!
Stworzyłem animację która działa tylko w określionych warunkach.

Metoda draw() jest regularnie wywoływana.
Domyślinie obrazek ma się poruszać w jednej osi do punktu limitX a potem wrócić na poprzednie miejsce i się zatrzymać.
Niestety animacja się nie zatrzymuje.
Próbowałem określić** velocityX** jako jakiś ułamek całej drogi ale wtedy problem również się pojawiał.
Nie wiem jak to naprawić:

export class Sweet {
  constructor(name, x, y, width, row, column) {
    this.name = name;
    this.image = document.getElementById(name);
    this.x = x;
    this.y = y;
    this.velocityX = 0;
    this.velocityY = 0;
    this.width = width;
    this.limitY = null;
    this.limitX = null;
    this.startX = null;
    this.startY = null;
    this.animation = false;
  }
  draw() {
    if (this.animation == "spinX") {
      this.calculateSpinX();
    }
    c.drawImage(
      this.image,
      (this.x += this.velocityX),
      (this.y += this.velocityY),
      this.width,
      this.width
    );
  }
  calculateSpinX() {
    if (this.x == this.limitX) {
      this.velocityX = -this.velocityX;
    }
    if (this.velocityX == 0) {
      this.startX = this.x;
      const direction = this.limitX - this.x > 0 ? 1 : -1;
      this.velocityX = 10 * direction;
    } else if (this.x == this.startX) {
      this.velocityX = 0;
      this.animation = false;
    }
  }
}

Prametry animation i limitX przekazuje funkcja zewnętrzna.
Parametry x i y nie zawsze są liczbami całkowitymi.

Czy wiecie jak to naprawić ?

PS: Wiem że od takich rzeczy są biblioteki. W następnym projekcie na pewno jakiejś użyję :D.

0

Skoro to liczby zmiennoprzecinkowe to zamiast:

    if (this.x == this.limitX) {
      this.velocityX = -this.velocityX;
    }

zrób

    if (this.x >= this.limitX) {
      this.velocityX = -this.velocityX;
    }
0

Tylko że limitX nie zawsze jest większy. To rusza się w prawo i w lewo.

I co z tego?
Po pierwsze musisz analogiczny warunek zastosować przy ruchu w drugą stronę, np.: if ( Math.abs(this.x) >= this.limitX)
Po drugie jeśli nie chcesz by coś przekraczało granicę to także to musisz obłożyć jakimś warunkiem np.:

if (this.x > this.limitX) {
      this.velocityX = this.limitX;
    }

A najlepiej nie eksperymentuj w ciemno tylko rozrysuj to sobie na kartce. Jedno jest pewne, jeśli używasz liczb zmiennoprzecinkowych to porównanie == Ci nie zadziała.

0

Mam coś takiego:

calculateSpinX() {
    const rate = 10;
    if (
      (this.velocityX > 0 && this.x > this.limitX) ||
      (this.velocityX < 0 && this.x < this.limitX)
    ) {
      this.velocityX = -this.velocityX;
    }
    if (this.velocityX == 0) {
      this.startX = this.x;
      this.velocityX = (this.limitX - this.x) / rate;
    } else if (
      (this.velocityX > 0 && this.x > this.startX) ||
      (this.velocityX < 0 && this.x < this.startX)
    ) {
      this.velocityX = 0;
      this.animation = false;
    }
  }

Niestety instrukcja po else if bardzo szybko się wykonuje.
Czy muszę umieścić w klasie kolejną zmienną, która będzie przechowywała "fazę" ruchu, czy da się to pominąć ?
(w wypadku ruchu w jedną i z powrotem)

0

Tak jak napisałem. Kombinujesz jak koń pod górę zamiast pomyśleć co ma się dziać i w jakiej kolejności.

https://jsfiddle.net/arLsb4xg/

<html> 
<body>

  <div id='screenArea'></div> 
  
  <style>
    #screenArea{
      width:640px;
      height:400px;
      background:#333;
      position:relative;
    }
  </style>
  
  <script>

    class ball{

      constructor(){
        this.x = 0 ;
        this.y = 0 ;
        this.radius = 0 ;
        this.speedX = 0 ;
        this.speedY = 0 ;
        this.color = '' ;
        this.animation = null ;
        this.myElement = null ;
      }

      move() {
        this.x = this.x + this.speedX ;
        this.y = this.y + this.speedY ;
        
        // odbicie od lewej krawędzi
        if ( this.x - this.radius <= 0 ){
          this.speedX = -this.speedX ;
          this.x = 0 + this.radius ;
        }        
        // odbicie od prawej krawędzi
        if ( this.x + this.radius >= this.animation.maxX ){
          this.speedX = -this.speedX ;
          this.x = this.animation.maxX - this.radius ;
        }
        // odbicie od górnej krawędzi
        if ( this.y - this.radius <= 0 ){
          this.speedY = -this.speedY ;
          this.Y = 0 + this.radius ;
        }
        // odbicie od prawej krawędzi
        if ( this.y + this.radius >= this.animation.maxY ){
          this.speedY = -this.speedY ;
          this.y = this.animation.maxY - this.radius ;
        }
        
        this.myElement.style.left = Math.round(this.x-this.radius).toString()+'px' ; // zaokrąglamy tylko do wyświetlania
        this.myElement.style.top = Math.round(this.y-this.radius).toString()+'px' ;
      }
      
      init() {
        var iDiv = document.createElement('div');
        iDiv.style.left = Math.round(this.x).toString()+'px' ; // zaokrąglamy tylko do wyświetlania
        iDiv.style.top = Math.round(this.y).toString()+'px' ;
        iDiv.style.width = (this.radius*2).toString()+'px' ;
        iDiv.style.height = (this.radius*2).toString()+'px' ;
        iDiv.style.backgroundColor = this.color ;
        iDiv.style.position = 'absolute' ;
        iDiv.style.display = 'block' ;
        iDiv.style.borderRadius = '50%';
        this.animation.screen.appendChild(iDiv) ;        
        this.myElement = iDiv ;
      }
    }    

    class animation{
    
      constructor ( areaElementName ){      
        this.screen = document.getElementById( areaElementName );
        this.maxX = this.screen.clientWidth ;
        this.maxY = this.screen.clientHeight ;  
        this.balls = new Array();
        this.ballsCount = 0 ;
      }
      
      addBall(){
        var newBall = new ball();
        newBall.speedX = Math.random()*10 - 5 ;
        newBall.speedY = Math.random()*10 - 5 ;
        newBall.radius = Math.round ( Math.random()*10 ) ;
        newBall.x = Math.random()*(this.maxX-2*newBall.radius)+newBall.radius ;
        newBall.y = Math.random()*(this.maxY-2*newBall.radius)+newBall.radius ;
        newBall.color = '#'+(0x1000000+(Math.random())*0xffffff).toString(16).substr(1,6) ;
        newBall.animation = this ;
        newBall.init();                
        this.balls [ this.ballsCount ] = newBall ;
        this.ballsCount++;        
      }      
            
      calcNextFrame() {
        var i;
        for ( i = 0 ; i < this.ballsCount ; i++ ) {
          this.balls[ i ].move();
        }
      }
      
      drawFrame() {
        this.calcNextFrame();
        var i;
        for ( i = 0 ; i < this.ballsCount ; i++ ) {
          this.balls[ i ].move();
        }
        var self = this;
        setTimeout ( function(){self.drawFrame();}, 50 );
      }
      
      start() {
        var i = 0 ;
        for ( i = 0; i<50; i++ ) {
          this.addBall();
        }
        this.drawFrame();
      }
    }

    var anim = new animation( 'screenArea' );
    anim.start();

  </script>
</body>
</html>
0

Poddałeś się czy walczysz z tematem?

0

Walczę ! :D

Twoje kulki wyglądają naprawdę świetnie i podziwiam estetykę kodu, tylko że zupełnie nie o to mi chodziło.

Nie chcę czegoś co się będzie odbijało od krawędzi canvas w nieskończoność, to już umiem zrobić.
tylko coś co z początkowego punktu dotrze do zadanego punktu, potem wróci na miejsce, zatrzyma się i wywoła jakąś funkcję kiedy to zrobi.
To wcale nie musi być przy krawędziach. Ten ruch może się odbywać równie dobrze gdzieś w środku.
Problem w mojej animacji właśnie polegał na tym że obiekty nie mogły się zatrzymać.

0

A co za różnica czy przy krawędziach czy w punkcie?
Zasada będzie dokładnie taka sama. Kwestia ustalenia granic ruchu dla tego obiektu.

0

Mam takie działające rozwiązanie:

 checkIfComingBack() {
    if (
      (this.velocityX > 0 && this.limitX - this.startX > 0) ||
      (this.velocityX < 0 && this.limitX - this.startX < 0)
    ) {
      return false;
    }
    return true;
  }
  checkIfCrossStart() {
    if (
      (this.velocityX < 0 && this.x < this.startX) ||
      (this.velocityX > 0 && this.x > this.startX)
    ) {
      return true;
    }
    return false;
  }

  checkIfCrossLimit() {
    if (
      (this.velocityX < 0 && this.x < this.limitX) ||
      (this.velocityX > 0 && this.x > this.limitX)
    ) {
      return true;
    }
    return false;
  }
  calculateSpinX() {
    const rate = 10;
    if (this.velocityX == 0) {
      this.startX = this.x;
      this.velocityX = (this.limitX - this.x) / rate;
    }
    if (this.checkIfComingBack() && this.checkIfCrossStart()) {
      this.x = this.startX;
      this.startX = this.limitX = 0;
      this.velocityX = 0;
      this.animation = false;
      parameters.globalAction = false;
    }
    if (!this.checkIfComingBack() && this.checkIfCrossLimit()) {
      this.velocityX = -this.velocityX;
    }
  }
Przy czym wymaga ono funkcji pomocniczych i jest tylko dla ruchu w osi X. 
Czy można to uprościć ?
0

Spróbuj to wrzucić w takiej formie jak ja podałem swoje. Bo bez możliwości testowania jest o wiele trudniej analizować czyjś kod.

0

Mam to wszystko podzielone na moduły. Jeszcze nie pushowałem na githuba.

1 użytkowników online, w tym zalogowanych: 0, gości: 1