What could be causing the failure of the update for this computed property in my Vue 3 application?

Currently, I am in the process of creating an audio player using Vue 3 and the Napster API.

About the Project

I have managed to make the vinyl rotate by utilizing a CSS keyframes-based animation paired with the computed property isSpinning.

I intend for the vinyl to stop spinning once it reaches the end of the current track. This is achieved through the following "formula" within the isSpinning function:

isSpinning() {
  return this.isPlaying && !this.player.ended;
}
...

Encountered Issue

However, I found that the application does not recognize when the value of this.player.ended changes as expected.

What Could be the Error in my Approach?

Answer №2

When using an Audio object in Vue, it will not function like typical JavaScript objects due to how Vue abstracts state change observation. The internal mechanisms used by Vue to detect changes in the Audio object's state may not be maintained, making it difficult for Vue to recognize when the object transitions from ended === false to ended === true.

To monitor changes in the ended state effectively, consider adding a custom event listener within the created hook to toggle the spinning state without relying on the ended check:

const musicApp = {
  data() {
    return {
      player: new Audio(),
      trackCount: 0,
      tracks: [],
      muted: false,
      isPlaying: false
    };
  },
  methods: {
    async getTracks() {
      try {
        const response = await axios
          .get(
            "https://api.napster.com/v2.1/tracks/top?apikey=ZTk2YjY4MjMtMDAzY00MTg4LWE2MjYtZDIzNjJmMmM0YTdm"
          )
          .catch((error) => {
            console.log(error);
          });
        this.tracks = response;
        this.tracks = response.data.tracks;
      } catch (error) {
        console.log(error);
      }
    },
    nextTrack() {
      if (this.trackCount < this.tracks.length - 1) {
        this.trackCount++;
        this.setPlayerSource();
        this.playPause();
      }
    },
    prevTrack() {
      if (this.trackCount >= 1) {
        this.trackCount--;
        this.setPlayerSource();
        this.playPause();
      }
    },
    setPlayerSource() {
      this.player.src = this.tracks[this.trackCount].previewURL;
    },
    playPause() {
      if (this.player.paused) {
        this.isPlaying = true;
        this.player.play();
      } else {
        this.isPlaying = false;
        this.player.pause();
      }
    },
    toggleMute() {
      this.player.muted = !this.player.muted;
      this.muted = this.player.muted;
    }
  },
  async created() {
    await this.getTracks();
    this.setPlayerSource();
    this.player.addEventListener('ended', () => {
      this.isPlaying = false;
    });
  },
  computed: {
    isSpinning() {
      return this.isPlaying;
    }
  }
};

Vue.createApp(musicApp).mount("#audioPlayer");
html,
body {
  margin: 0;
  padding: 0;
  font-size: 16px;
}

body * {
  box-sizing: border-box;
  font-family: "Montserrat", sans-serif;
}

@-webkit-keyframes spin {
  0% {
    -webkit-transform: rotate(0deg);
  }
  100% {
    -webkit-transform: rotate(360deg);
  }
}

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

.player-container {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  background-color: #2998ff;
  background-image: linear-gradient(62deg, #2998ff 0%, #5966eb 100%);
}

#audioPlayer {
  width: 300px;
  height: 300px;
  border-radius: 8px;
  position: relative;
  overflow: hidden;
  background-color: #00ca81;
  background-image: linear-gradient(160deg, #00ca81 0%, #ffffff 100%);
  box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
  display: flex;
  flex-direction: column;
  align-items: center;
}

.volume {
  color: #ff0057;
  opacity: 0.9;
  display: inline-block;
  width: 20px;
  font-size: 20px;
  position: absolute;
  top: 5px;
  right: 6px;
  cursor: pointer;
}

.album {
  width: 100%;
  flex: 1;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
}

.album-items {
  padding: 0 10px;
  text-align: center;
}

.cover {
  width: 150px;
  height: 150px;
  margin: auto;
  box-shadow: 0px 5px 12px 0px rgba(0, 0, 0, 0.17);
  border-radius: 50%;
  background: url("https://w7.pngwing.com/pngs/710/955/png-transparent-vinyl-record-artwork-phonograph-record-compact-disc-lp-record-disc-jockey-symbol-miscellaneous-classical-music-sound.png") center top transparent;
  background-size: cover;
}

.cover.spinning {
  webkit-animation: spin 6s linear infinite;
  /* Safari */
  animation: spin 6s linear infinite;
}

.info {
  width: 100%;
  padding-top: 5px;
  color: #000;
  opacity: 0.85;
}

.info h1 {
  font-size: 11px;
  margin: 5px 0 0 0;
}

.info h2 {
  font-size: 10px;
  margin: 3px 0 0 0;
}

.to-bottom {
  width: 100%;
  margin-top: auto;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
}

.track {
  background-color: #ff0057;
  opacity: 0.9;
  height: 3px;
  width: 100%;
}

.controls {
  width: 150px;
  display: flex;
  height: 60px;
  justify-content: space-between;
  align-items: center;
}

.controls .navigate {
  display: flex;
  box-shadow: 1px 2px 7px 2px rgba(0, 0, 0, 0.09);
  width: 33px;
  height: 33px;
  line-height: 1;
  color: #ff0057;
  cursor: pointer;
  background: #fff;
  opacity: 0.9;
  border-radius: 50%;
  text-align: center;
  justify-content: center;
  align-items: center;
}

.controls .navigate.disabled {
  pointer-events: none;
  color: #606060;
  background-color: #f7f7f7;
}

.controls .navigate.navigate-play {
  width: 38px;
  height: 38px;
}

.navigate-play .fa-play {
  margin-left: 3px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="b5d4cddcdac6f5859b87879b85">[email protected]</a>/dist/axios.min.js"></script>
<script src="https://unpkg.com/vue@next"></script>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@300;500&display=swap" rel="stylesheet">

<div class="player-container">
  <div id="audioPlayer">
    <span class="volume" @click="toggleMute">
      <i v-show="!muted" class="fa fa-volume-up"></i>
      <i v-show="muted" class="fa fa-volume-off"></i>
    </span>
    <div class="album">
      <div class="album-items">
        <div class="cover" :class="{'spinning' : isSpinning}"></div>
        <div class="info">
          <h1>{{tracks[trackCount].name}}</h1>
          <h2>{{tracks[trackCount].artistName}}</h2>
        </div>
      </div>
    </div>

    <div class="to-bottom">
      <div class="track"></div>
      <div class="controls">
        <div class="navigate navigate-prev" :class="{'disabled' : trackCount == 0}" @click="prevTrack">
          <i class="fa fa-step-backward"></i>
        </div>
        <div class="navigate navigate-play" @click="playPause">
          <i v-show="!isPlaying" class="fa fa-play"></i>
          <i v-show="isPlaying" class="fa fa-pause"></i>
        </div>
        <div class="navigate navigate-next" :class="{'disabled' : trackCount == tracks.length - 1}" @click="nextTrack">
          <i class="fa fa-step-forward"></i>
        </div>
      </div>
    </div>
  </div>
</div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

There is a slight gap between the svg element and the path

Here is the size of my path tag (g tag is same size) https://i.sstatic.net/Huk8j.png This is the size of my SVG tag https://i.sstatic.net/VBXQi.png There is a gap between them. These are the inline attributes... <svg preserveAspectRatio="none" cla ...

Node.js encountered an error: Address already in use. The specified port 8080 is currently being utilized by another application

My application was not functioning properly, and upon running the command "npm start", an error was thrown: Error: listen EADDRINUSE: address already in use :8080 Even after restarting my EC2 instance and attempting the command again, I encount ...

Employ a class function within router.get

I'm a beginner with node.js and I'm running into an issue when trying to use a class method in the router.get callback function. Can someone assist me with this problem? Route.get() is expecting a callback function but received a [object object] ...

Implementing jQuery functionality on elements that are generated dynamically

I'm facing a challenge when working with dynamic elements in jQuery and I really could use some help. Let me provide an example from my current project: main.js $(function () { renderPlaceList(places); setupVoting(); } The two functions are ...

Issue with visibility of pagination in AngularJS ui

I am facing an issue with pagination in my AngularJS UI application. I have a dataset that requires server-driven pagination due to its size. The problem I'm encountering is that the pagination element is not displaying on the page, even though I hav ...

The number field is persisting with its content even after submitting, while the other fields successfully clear up

I am facing an issue where all fields clear up after submission except for the number field. I would appreciate any help on why this is happening and how to fix it. Thank you. Textfield number: const [number, setNumber] = useState(""); const han ...

Having trouble transferring information from a form to a page because of the error message " is not defined"?

I'm facing an issue while building a node app. I have set up my routes correctly and installed all the necessary dependencies. However, when I try to load the ("/) "homepage," I receive an error message stating that "newPost" is not defined. Surprisin ...

Having trouble with my findIndex function in Node.js while working with a mongo DB database

I am having difficulty finding the index of a specific product in a MongoDB database. const cart = await this.model.findOne({ user: { $eq: user } }); if (cart) { const itemFound = cart.products.findIndex( (item) => item._id === ...

Convert several XML nodes to display in an HTML format

I am currently facing an issue where I am trying to display all the nodes of a specific tag in XML. Although it does filter the data correctly, only one node is showing up. Is there anyone who can assist me with this problem? Below is the XML content: < ...

The keyDown function in P5.play.js does not seem to be working properly

Can someone please help me figure out why this code isn't functioning properly? I am utilizing p5 libraries such as p5.play, p5.js, p5.sound, and p5dom. Here's the snippet: class Player{ constructor(){ this.x, this.y, this.width, ...

The class name is not defined for a certain child element in the icon creation function

Currently, I am developing a Vue2 web application using Leaflet and marker-cluster. I am encountering an issue with the iconCreateFunction option in my template: <v-marker-cluster :options="{ iconCreateFunction: iconCreateClsPrg}"> ...

continuously repeating with each press of the enter key

Whenever I focus on the input field and press enter, everything works fine. However, if I do it again, the action is repeated multiple times depending on how many times I focus. $(document).delegate(".changeValue","focus one",function(){ $(this ...

Issue with e2e.js file format in Cypress Support

I am trying to save Cypress screenshots into a report using a support file as recommended in the documentation. However, I keep encountering an error: Your supportFile is missing or invalid: support/e2e.js The supportFile must be a .js, .ts, .coffee file ...

Display icon within ng-bind-html when the value is not true

Is there a way to integrate a fontawesome icon into ng-bind-html on ui-select based on a boolean value? <span ng-bind-html="project.IsActive == false && <i class="fa fa-ban" aria-hidden="true"></i> | highlight: $select.search">& ...

Webpack fails to resolve paths provided by a JavaScript variable

I am currently developing an application using vue and ionic, but I have encountered an issue with loading assets as the paths are not resolving correctly. <template> <div id="index"> <img :src="image.src" v-for="image in image ...

Ways to perform a CSS redirection

How can you create a webpage where only the CSS can be altered to show a completely different page to the user? ...

Border at the Top and Text Alignment Problem

I am working on a basic navigation bar that includes a hover effect. .navip { float: left; text-decoration: none; font-weight: lighter; } .navip > a { display: block; color: black; text-decoration: none; font-size: 20px; ...

The line segment refuses to remain hidden beneath the h2 tag

Just getting started with HTML/CSS here! I'm trying to figure out how to keep a line directly under an h2 tag. In my code, there's an h2 tag labeled 'Instructions' followed by a div containing 3 other divs making up a line segment. By d ...

What is the best method for adding anchors or appending items in aspx pages?

I have created code for an image gallery on my webpage, "gallery.aspx". The situation is that I have an external aspx page with various links. When a user clicks on one of these links, I want them to be redirected to the image gallery but to a specific ima ...

Receiving a JSON object in response after sending data to a server using JavaScript

I am facing an issue with passing an email and password on login click to a database using PHP. After testing the email and password combination, PHP sends back a JSON object. While I have verified that the PHP code is functioning correctly and returns a J ...