Display currentTime of a song in vue - javascript

I'd like to know how can I make time constantly update itself. So when I press the play button the seconds start to update automatically from 0:00 to the end , because now it just updates onclick. I am trying to use HTML5 audio and I have successfully managed to get the time updating as a label from this line of code:
sound.ontimeupdate = function () { document.getElementById('Time').innerHTML = sound.currentTime.toFixed() }
But thats not what I need, I would like to get the time attribute in data() to get updated and displayed in the label as shown in my HTML code.
I tried adding an event listener but it did not work... It gets called and every call was logged with console.log but time attribute was not updated
let sound = null
export default {
data () {
return {
isPlaying: false,
time: 0,
duration: 0
}
},
methods: {
playMusic () {
if (!sound) {
sound = new Audio(require('assets/YES.mp3'))
}
this.isPlaying = true
sound.play()
// sound.addEventListener('timeupdate', function () { this.time = sound.currentTime.toFixed() }) -- did not work
this.time = sound.currentTime.toFixed()
}
Html:
<label id="Time" #timeupdate>
{ { time } }:{ { duration } }
</label>

Inside your addEventListener you get a different this than you might expect.
Either use fat arrow
sound.addEventListener('timeupdate', () => this.time = sound.currentTime.toFixed() )
or, the old way, save this
let that = this
sound.addEventListener('timeupdate', function () { that.time = sound.currentTime.toFixed() })

you could just add a generic timer dynamically. You can use a watch to add/remove it like so:
(untested code)
export default {
data() {
return {
isPlaying: false,
time: 0,
duration: 0,
intervalId: null,
sound: null
};
},
watch: {
isPlaying(isPlaying) {
if (this.intervalId !== null) {
clearInterval(this.intervalId);
}
if (isPlaying) {
this.sound.play();
this.intervalId = setInterval(() => {
this.time = this.sound.currentTime.toFixed();
}, 500);
} else {
this.sound.stop();
}
}
},
methods: {
playMusic() {
if (!this.sound) {
this.sound = new Audio(require("assets/YES.mp3"));
}
this.isPlaying = true;
}
}
};

Related

How to call a method when the props are passed (for the first time) to a component in Vue.js

<script>
import _ from "lodash";
export default {
name: "QuestionBottom",
props: {
currentQuestion: Object,
nextQuestion: Function,
increment: Function,
},
data() {
return {
selectedIndex: null,
correctIndex: null,
shuffleArray: [],
ansArray: [],
};
},
// watch listens for changes to the props
watch: {
currentQuestion() {
this.selectedIndex = null;
},
// I want this method to run when props are passed to the component even at the first time
allOptions() {
console.log("I am second");
console.log("what's in this.allOptions", this.allOptions);
this.correctIndex = this.allOptions.indexOf(
this.currentQuestion.correct_answer
);
console.log("Correct index isss", this.correctIndex);
},
},
computed: {
allOptions() {
let allOptions = [
...this.currentQuestion.incorrect_answers,
this.currentQuestion.correct_answer,
];
// console.log("array that is not shuffled is ", allOptions);
allOptions = _.shuffle(allOptions);
console.log("shuffled array is", allOptions);
// console.log("Corect ans is ", this.currentQuestion.correct_answer);
console.log("I am first");
return allOptions;
},
},
methods: {
selectedAns(index) {
this.selectedIndex = index;
console.log("Selected answer index", this.selectedIndex);
},
submitAnswer() {
let isCorrect = false;
if (this.selectedIndex === this.correctIndex) {
isCorrect = true;
}
this.increment(isCorrect);
},
},
};
</script>
I want my watch region's allOptions() to be run when the props are passed for the first time. Now, I know that watch runs when a method or prop changes but can I run the method from the first time props are passed. Is there any way that I can make this method to run from the point props are passed to the component.
Use the immediate flag of the watch option:
export default {
watch: {
allOptions: {
handler() {
this.correctIndex = this.allOptions.indexOf(this.currentQuestion.correct_answer);
},
immediate: true,
}
}
}
demo

Vuex: Dispatch a function every 2 seconds only when a store property exists?

I am trying to run a function that makes a GET request to update the quantity of a product but only when the currentProduct store property exists. If there's no selected product, then I don't want to have the interval be firing a function for no reason.
In pseudo code, I basically to say:
if $this.store.getters['myStore/getCurrentProduct']
then setInterval(this.$store.dispatch('updateQuantity'), 2000);
My only thought was to make a computed property for currentProduct:
computed: {
currentProd() {
return $this.store.getters['myStore/getCurrentProduct'];
}
}
and then watch it :
watch: {
currentProd(newVal,oldVal) {
if (newVal != null) {
let foo = setInterval(this.$store.dispatch('updateQuantity'), 2000);
}
}
}
I'm just not sure how to keep this from overlapping and having a ton of intervals firing off
You should store the interval object in one place so you can reset it easily, instead of creating a new local interval in the function every time the watcher runs:
data () {
return {
productCheckInterval: null
}
},
watch: {
currentProd (newVal, oldVal) {
clearInterval(this.productCheckInterval)
if (newVal !== null) {
this.productCheckInterval = setInterval(() => {
this.$store.dispatch('updateQuantity')
}, 2000)
}
}
}

How to play a sound only at the first time false value after true

I have person data that contain dynamic boolean value. The value is generated automatically and can be true or false every time.
Webpage get the data every 5 seconds and render it. If the value on each person is false then the sound is played.
This is the code :
import React, { Component } from 'react';
import { render } from 'react-dom';
import Sound from './Mp3';
const data = [
{
id: '1',
name: 'Peter',
value: true
},
{
id: '2',
name: 'John',
value: false
}
];
class App extends Component {
state = {
results: [],
}
componentDidMount() {
this.getData()
// get data every 5 sec
setInterval(this.getData, 5000);
}
getData = () => {
// generate random value
data[0].value = Math.random() >= 0.5;
data[1].value = Math.random() >= 0.5;
// set results state to data
this.setState({ results: data });
// condition if John or Peter value is false
if (data.some(d => d.value === false)) {
var audio = new Audio(Sound);
// play a sound
audio.play();
}
}
render() {
const { results } = this.state;
return (
<div>
{results.map(item => {
return (
<div key={item.id}>
<div>
Name: {item.name}
</div>
<div>
Value: {item.value.toString()}
</div>
<br />
</div>
)
})}
</div>
);
}
}
render(<App />, document.getElementById('root'));
This is a demo
With the code above, the sound is played every time if the value on each person is false.
How to play a sound only at the first time false value after true?
I mean, if the first rendered John value is false then play a sound and if 5 seconds later John value still false then don't play a sound after the value back to true and change to false again.
Results I expect :
// first rendered
Name: Peter
Value: true
Name: John
Value: false // play a sound
// second (5 seconds later)
Name: Peter
Value: true
Name: John
Value: false // don't play a sound
// third (10 seconds later)
Name: Peter
Value: true
Name: John
Value: true // don't play a sound
// fourth (15 seconds later)
Name: Peter
Value: true
Name: John
Value: false // play a sound
...
You'll need to keep track of each users's previous value outside of the getData function inside of another object, then compare the previous value with the new value inside of the some.
state = {
results: []
}
const previousResults = {};
componentDidMount() {
this.getData()
// get data every 5 sec
setInterval(this.getData, 5000);
}
getData = () => {
// generate random value
data[0].value = Math.random() >= 0.5;
data[1].value = Math.random() >= 0.5;
// set results state to data
this.setState({ results: data });
// condition if user value is false & previous result for user was not false
if (data.some(d => (d.value === false) && (this.previousResults[d.id] !== false))) {
var audio = new Audio(Sound);
// play a sound
audio.play();
}
data.forEach((item) => {
this.previousResults[item.id] = item.value;
});
}
You could use a sort of "Circuit Breaker" which keeps track of its state, and whether it is enabled. Having tripped, it waits an amount of time before re-enabling itself.
The code below demonstrates the following
switching from true to false executes the action callback
continuing to pulse false does NOT repeat this action
switching to true and then waiting a greater amount of time than the timeout before pulsing a new false again executes the action callback.
function TFCircuitBreaker(timeout, action){
this.state = null;
this.enabled = true;
this.valueChange = function(newValue){
if(this.enabled){
if(this.state && !newValue){
action();
this.enabled = false;
setTimeout( () => this.enabled = true,timeout);
}
}
this.state = newValue;
}
}
var cb = new TFCircuitBreaker(5000, () => console.log("Play sound"));
cb.valueChange(true);
cb.valueChange(false);
cb.valueChange(true);
cb.valueChange(false);
cb.valueChange(true);
setTimeout( () => cb.valueChange(false), 6000);
Ive also updated your demo with similar code which I think does what you want: https://stackblitz.com/edit/react-kmfiij?embed=1&file=index.js

Using a variable as a function name that could be multiple levels deep

I have had a look around the site and seem to have come across some solutions, but none seem to work. So here's the problem.
I have the following Javascript:
THEOBJECT = {
init: function() {
this.tools.init();
}
};
THEOBJECT.tools = {
active: false,
init: function() {
// active variable updated elsewhere/loading
if (!this.active) {
THEOBJECT.utils.timeout('tools.init', 100);
}
// is active so continue
}
};
THEOBJECT.utils = {
timeout: function(functionName, time) {
setTimeout(function() {
THEOBJECT[functionName]();
}, time);
}
};
THEOBJECT.init();
I am getting an error when running the THEOBJECT.utils.timeout saying:
THEOBJECT[functionName] is not a function
I'm trying to run THEOBJECT.tools.init()
I'm assuming this is because it's not a direct function of the THEOBJECT object is there a way around this or is a split() the best way to go?
There is other option to do it using lodash.js. Please see the updated snippet.
Or go with conventional approch mentioned below
You are trying to access a property in wrong way
you are doing THEOBJECT[tools.init] which incorrect. It should be like THEOBEJECT[tools][init]
THEOBJECT = {
init: function() {
this.tools.init();
}
};
THEOBJECT.tools = {
active: false,
init: function() {
// active variable updated elsewhere/loading
if (!this.active) {
THEOBJECT.utils.timeout('tools.init', 100);
}
console.log("active")
// is active so continue
}
};
THEOBJECT.utils = {
timeout: function(functionName, time) {
setTimeout(function() {
_.get(THEOBJECT,functionName);
}, time);
}
};
THEOBJECT.init();
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
you can also go with a simple reduce and make it a one-liner:
functionName.split('.').reduce((acc,curr) => return acc[curr], THEOBJECT)();
within your code:
THEOBJECT = {
init: function() {
this.tools.init();
}
};
THEOBJECT.tools = {
active: false,
init: function() {
// active variable updated elsewhere/loading
if (!this.active) {
THEOBJECT.utils.timeout('tools.init', 100);
}
// is active so continue
}
};
THEOBJECT.utils = {
timeout: function(functionName, time) {
setTimeout(function() {
functionName.split('.').reduce((acc,curr) => acc[curr],THEOBJECT)();
}, time);
}
};
THEOBJECT.init();

Vue scope: how to delay handling of #mouseover

So I want to have an action only if the user has the mouse on the div for at least 1 second. Inside template:
<div #mouseover="trigger"></div>
Inside script:
data() {
return {
hovered: false
}
}
methods: {
trigger() {
setTimeout(function(){ this.hovered = true }, 1000)
}
}
My problem is that I don't understand the scope of Vue. Because this.hovered is inside another function, it does not find the actual hovered data variable. What's the solution to this?
Have you tried using an arrow function in your setTimeout? It will maintain this.
data() {
return {
hovered: false
}
}
methods: {
trigger() {
setTimeout(() => { this.hovered = true }, 1000)
}
}
<div #mouseover="activarOver" #mouseleave="resetOver "> eventos </div>
data: () => {
return {
countMouseOver: 0
}
},
methods: {
activarOver () {
this.countMouseOver++
if (this.countMouseOver < 2) {
this.setMostrarPopup()
}
console.log(this.countMouseOver)
},
resetOver () {
this.countMouseOver = 0
console.log('reset')
},
}
Implementation to show when hovered over for 1 second, then disappear when no longer hovered.
<span #mouseover="hover" #mouseleave="unhover">Hover over me!</span>
<div v-if="show">...</div>
data() {
return {
show: false;
hovering: false,
};
},
methods: {
hover() {
this.hovering = true;
setTimeout(() => this.show = this.hovering, 1000);
},
unhover() {
this.hovering = false;
this.show = false;
},
}
I have been solving this problem for selecting items in a list only if the user hovers for some time (to prevent menu flickering)
Template (pug):
.div(
#mouseover="select(identifier)"
#mouseout="clear()"
) {{content}}
Data:
hovered: false
selectedId: ""
and the methods
select(identifier) {
this.selectedId = identifier
setTimeout(() => {
if(this.selectedId === identifier )
this.hovered = true
},
1000
)
},
clear() {
this.selectedId = ''
}
the approach is to check if whatever user is hovering on is the same as they were hovering on a second ago.
If you want to use old syntax, bind 'this' to the setTimeout function
data() {
return {
hovered: false
}
}
methods: {
trigger() {
setTimeout(function(){ this.hovered = true }.bind(this), 1000)
}
}

Categories

Resources