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

Difficulty understanding JavaScript sum calculations

I am currently working on a website project. Seeking assistance to enable an increment of one when clicked. Also need guidance on calculating the total price of items collected in a designated section under "number of items selected". Goal is to display ...

Learn the process of integrating content into a tinymce editor using vue js

Is there a way I can use a button in the vue js template to insert content like <span class="some-class">text</span> inside tinymce editor with the help of the tinymce-vue wrapper? Here is the code snippet: <template> <tiny ...

What is the method for altering the color of specific columns?

I am currently testing out my Amchart with this example. Check out the working demo on code pen My goal is to modify the color of the first four columns. While I am aware that colors can be changed by specifying them in the JSON file as a property calle ...

Back up and populate your Node.js data

Below is the Course Schema I am working with: const studentSchema = new mongoose.Schema({ name: { type: String, required: true }, current_education: { type: String, required: true }, course_name: { ...

Javascript encountering compatibility issues with certain versions of Internet Explorer; employing browser detection workaround

Unfortunately, Internet Explorer is causing issues when trying to execute this script (specifically the 'else' part). Despite numerous attempts, I have been unable to resolve it. $(document).ready(function(){ if (navigator.appName != "Micros ...

Acquiring administrative authorization upon user login with PHP

I have developed a registration form and a login form using PHP. However, whenever I run this code, it displays the message "invalid username" when the username is invalid, or it says "your account isn't approved by admin yet." Can anyone please guide ...

Using different CSS classes interchangeably in AngularJS

Imagine you have a list with an unknown number of items, ranging from one to potentially dozens. You want to display five CSS classes in a regular alternating pattern. What would be the most effective approach to achieve this? Here is some sample HTML cod ...

What are the steps to successfully integrate Vuetify 2.3 or any other packages into a Vue 3 Project?

How can I properly register Vuetify in my main.js file without using Vue alias? After importing Vuetify, all of my components are hidden. Dependencies: "vue": "^3.0.0-rc.7", "vue-router": "^4.0.0-0", &quo ...

Having trouble getting webpack and babel to start using npm

Greetings, wonderful people of the internet! I am a newcomer to the enchanting world of programming and I am facing a perplexing issue. Although Webpack is trying to guide me towards the solution, I seem to be struggling with fixing it on my own. const pa ...

"Attempting to access a variable defined in one pipe results in it being undefined in a

Currently, I am utilizing gulp to process pages for a website. Some of these pages are PHP files, however, after going through the template engine, they all end up with a .html file extension. My intention is to add a property to each file indicating if it ...

Methods for displaying data on the client side using express, MongoDB, and jade

I'm currently working on displaying data retrieved from my database using node.js, Express, and MongoDB. I successfully see the data in the console, but now I need to output it on the front-end using Jade. Here is the data I have retrieved: { date: ...

Changing environment variables for jasmine tests

My Angular service code snippet includes importing the environment like this: import {environment} from '../environment' .... public something() { if(environment.production) { // do stuf } else { // do something else } } I am now l ...

Ensuring the value of a v-text-field in Vuetify using Cypress

I am currently developing an end-to-end test suite using Cypress for my Vue and Vuetify frontend framework. My goal is to evaluate the value of a read-only v-text-field, which displays a computed property based on user input. The implementation of my v-tex ...

Puppeteer: Easier method for managing new pages opened by clicking a[target="_blank"]; pause for loading and incorporate timeout controls

Overview I'm seeking a more streamlined approach to managing link clicks that open new pages (such as target="_blank" anchor tags). By "handle," I mean: fetch the new page object wait for the new tab to load (with specified timeout) Steps to r ...

Troubleshooting a problem with data retrieval in Hygraph and Next JS

I'm currently facing an issue while attempting to fetch data from Hygraph in a fresh Next JS application. The error message I'm encountering is: Error: Cannot read properties of undefined (reading 'map'). As a beginner in both technolo ...

Unable to find the import "@mui/x-charts/PieChart" in the file "src/components/Pie/Pie.jsx". Could the file be missing or spelled incorrectly?

I utilized the PieChart component in my pie.jsx file pie.jsx import { PieChart } from '@mui/x-charts/PieChart'; const Pie = () => { return ( <div> <PieChart series={[ { data: [ ...

Angular 4 encounters a hiccup when a mistake in the XHR request brings a halt to a

In my Angular 4 application, I have implemented an observable that monitors an input field. When it detects a URL being entered, it triggers a service to make an XHR request. Observable.fromEvent(this._elementRef.nativeElement, 'input') .debou ...

Displaying or concealing fields through Javascript depending on the category type of the results item

I am facing an issue with displaying certain fields in each individual property search result based on the property type. For example, if a property is classified as Land, I do not want the bedrooms and bathrooms fields to be visible, but if it is a Villa, ...

Understanding the fundamentals of positioning elements in CSS as they float on a webpage

Understanding the behavior of float in CSS can be confusing. When floating an element to the left or to the right, it affects how other elements wrap around it. But why do some elements wrap while others don't? For example, when you have a floated im ...

Creating a dynamic tbody element on button click with the help of javascript or jquery

I am working with the following code: $(document).ready(function() { //Optimizing by selecting tbody first using jquery children var tbody = $('#myTable').children('tbody'); //If no tbody found, select the table itself ...