I need to show Sound icon on top of the video, when sound is OFF and hide it when sound is ON. For some reason the code below is not working.
if (video.prop('muted') === true) {
video.mouseenter( function() {sound.show()} ).mouseleave( function() {sound.hide()} );
}
else {
sound.hide();
}
<video id="video" controls muted preload="none" width="446" height="250"></video>
I figured it out. Now it works like that.
video.mouseenter( function() {
if (video.prop('muted') === true) {
sound.show()
}
else {
sound.hide()
}
});
video.mouseleave( function() {
sound.hide();
});
Considering your video element:
<video id="video" controls muted preload="none" width="446" height="250">
</video>
You can determine whether sound is on by testing the volume and muted media properties of the element:
var video = document.getElementById("video");
if (video.muted || video.volume === 0) {
// Sound is off
}
Related
What I have
I have made use of IntersectionObserver on my feed page. It behaves like TikTok. Scroll up or down to see the next video. On enter the video plays. On exit, it stops playing.
The Problem
It works on Android and Windows perfectly. On iOS, not so much. The videos are not autoplaying as expected. A bit strange though, is if I click the video (which calls play() on that video), and then scroll it out of view, the video does stop. When I scroll it back into view, it auto plays again. So I know that IntersectionObserver is being recognized, just not triggered initially.
The Code
==================================
The Feed Page, where the Observer is being instantiated:
data() {
return {
observer: null,
}
};
mounted() {
let config = { threshold: 0.1 };
if (process.client) {
if (/iPhone|iPad/i.test(navigator.userAgent)) {
config.root = null;
}
}
this.observer = new IntersectionObserver(function (entries) {
entries.forEach(({ target, isIntersecting }) => {
if (isIntersecting) {
target.play();
} else {
target.pause();
target.currentTime = 0;
}
});
}, config);
beforeDestroy() {
this.observer.disconnect();
},
HTML
<div
v-for="(post, index) in posts"
:key="index"
>
<MobileFeedItem :post="post" :observer="observer" />
</div>
=================================
The MobileFeedItem component
props: ["post", "observer"],
mounted() {
this.$nextTick(() => {
if (this.observer !== null && this.checkMediaTypeIsVideo(this.post)) {
this.observer.observe(
document.getElementById(
`video_${this.getContentId(this.post)}_mobile`
)
);
}
});
HTML
<video
:id="`video_${getContentId(post)}_mobile`"
:data-poster="getThumbnail(post)"
preload="none"
disablePictureInPicture
crossorigin
loop
playsinline
v-lazy-load
class="w-full h-full my-4"
>
<source :data-src="getMedia(post)" type="video/mp4" />
</video>
My Thoughts...
The observer is being instantiated and recognized, just not triggered. So is there a way that I can force the browser to wake up and become aware of the observer without having to click on each of the video elements first?
I found the answer. It wasn't anything wrong with the IntersectionObserver, but because my video was not muted.
"By default, autoplay executes only if the video doesn’t contain an audio track, or if the video element includes the muted attribute. Video playback pauses if the element gains an audio track, becomes unmuted without user interaction, or if the video is no longer onscreen"
source - https://developer.apple.com/documentation/webkit/delivering_video_content_for_safari
It also appears that we don't need intersection observer at all for Safari, as it does this for us. Huh.
In a Vue app I play background music, using the audio tag :
<audio id="music" autoplay loop>
<source
:src="musicfilename"
type="audio/mp3"
/>
</audio>
This works fine, but when the app is restarted during development, by Webpack 'hot reload', the music that was playing isn't stopped, so it keeps playing multiple music instances at the same time!
When I check with document.getElementsByTagName there is really only 1 instance of AUDIO, so I can't find a way to stop the previous audio instances from playing.
Any idea how to solve this problem?
You should track when your module is being hot replaced, and when it is just stop the audio player.
<audio id="music" ref="player" autoplay loop>
<source
:src="musicfilename"
type="audio/mp3"
/>
</audio>
export default {
...
mounted() {
if (module.hot) {
module.hot.dispose(this.hmrHandler);
}
},
destroyed() {
if (module.hot) {
module.hot.removeDisposeHandler(this.hmrHandler)
}
},
methods: {
hmrHandler() {
const { player } = this.$refs
player.pause()
// Lines below can also help
// player.currentTime = 0
// player.src = ''
},
},
...
}
Update:
Howler.unload() placed in the mounted hook solved the problem.
Webpack HMR API.
Look at dispose and removeDisposeHandler.
I have a scene where I am changing the src for sky using buttons I created "outside the scene". Currently everything works fine but I would like to show a preloader while waiting for the next image to load.
Here you can see my scene: http://scriptstrainer.com/vr_training/
Below I have provided some of my code:
<a-scene>
<a-sky src="images/0-1.jpg" id="img-src">
</a-scene>
<div>
<img src="images/t1.png">
</div>
<div>
<img src="images/t2.png">
</div>
<div>
<img src="images/t3.png">
</div>
<script>
var sky = document.querySelector('#img-src');
var button1 = document.querySelector('#button1');
var button2 = document.querySelector('#button2');
var button3 = document.querySelector('#button3');
button1.addEventListener('click', function() {
sky.setAttribute('src', 'images/0-1.jpg');
});
button2.addEventListener('click', function() {
sky.setAttribute('src', 'images/2.JPG');
});
button3.addEventListener('click', function() {
sky.setAttribute('src', 'images/3.JPG');
});
</script>
Thanks for your assistance...
https://aframe.io/docs/0.4.0/components/material.html#events_materialtextureloaded
There's an event materialtextureloaded you can use to detect when the texture has loaded onto the mesh. In between the time you request to set the texture and the time the texture is set, you can display a loading graphic.
button1.addEventListener('click', function() {
sky.setAttribute('src', 'images/0-1.jpg');
// Display something in the meantime.
sky.addEventListener('materialtextureloaded', function () {
// Small timeout just in case?
setTimeout(function () { // Remove the placeholder. }, 100);
});
});
The loading graphic can be like a spinning object in the scene, a fade-in black mask around the camera (as used in https://github.com/aframevr/360-image-gallery-boilerplate). It depends on what you want it to be.
I had the same scenario where I wanted to add a preloader and only when the image is displayed, to remove the preloader.
I tried using events 'load' and 'loaded' but didn't work as I found out images are not displayed once they finish loading.
Eventually I got help from the AFrame GitHub page and that's how I did it:
<a-assets>
<img id='img-src' src='image.jpg'/>
</a-assets>
<a-sky src='#img-src' id='sky-id'></a-sky>
<script type='text/javascript'>
var skyEl = document.querySelector('#sky-id');
function loaded()
{
var preloader = document.querySelector('#preloader');
preloader.style.display = "none";
}
skyEl.addEventListener('materialtextureloaded', loaded);
</script>
I use mediaelement.js and I need to override the width and height of a video element, but it's not working:
<video id="v1" width="960" height="720">
<source src="video.mp4" type="video/mp4" />
</video>
<script>
player1 = new MediaElementPlayer('#v1',{ features: [], videoWidth: 1323, videoHeight: 995 });
player1.play();
</script>
I have the container div.mejs-mediaelement at 1323x995, but the video is still at 960x720.
If I do:
<video id="v1" width="100%" height="100%">
It works but not on IE9... IE9 understands width="100" and height="100".
Thanks for your help!
I used this solution:
function scaleToFill(videoTag) {
var $video = $(videoTag),
videoRatio = videoTag.videoWidth / videoTag.videoHeight,
tagRatio = $video.width() / $video.height();
if (videoRatio < tagRatio) {
$video.css('-webkit-transform','scaleX(' + tagRatio / videoRatio + ')')
} else if (tagRatio < videoRatio) {
$video.css('-webkit-transform','scaleY(' + videoRatio / tagRatio + ')')
}
}
function calc(){
$("video").each(function(){
scaleToFill($(this)[0]);
});
}
calc();
$(window).on('resize', function(){
calc();
});
This is from the Youtube API docs:
"As mentioned in the Getting started section, instead of writing an empty element on your page, which the player API's JavaScript code will then replace with an element, you could create the tag yourself."
But I cannot get the API to callback to my javascript. Any help would be great.
Here's my HTML code:
<iframe id="player" type="text/html" width="640" height="360"src="http://www.youtube.com/embed/1_QO8LoGNpc?enablejsapi=1" frameborder="0"></iframe>
JAVASCRIPT:
<script>
function onYouTubeIframeAPIReady() {
alert("hey");
player = new YT.Player('player', {
// height: '720',
// width: '1280',
// videoId: '1_QO8LoGNpc',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
</script>
I can get it to work with using javascript creating the iframe but not setting the iframe on the page. HELP!
Function onYouTubeIframeAPIReady() should be defined before loading https://www.youtube.com/iframe_api.
Here is the idea:
<iframe id="player" type="text/html" width="640" height="360"src="http://www.youtube.com/embed/1_QO8LoGNpc?enablejsapi=1" frameborder="0"></iframe>
<script>
function onYouTubeIframeAPIReady() {
alert("hey");
player = new YT.Player('player', {
// height: '720',
// width: '1280',
// videoId: '1_QO8LoGNpc',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
</script>
<script src="https://www.youtube.com/iframe_api"></script>
Why? Because iframe_api will call this function after being loaded. In your case iframe_api was loaded before.
Best regards.
But your code works. See it here:
http://jsfiddle.net/aXLA2/
<iframe id="player" type="text/html" width="640" height="360"src="http://www.youtube.com/embed/1_QO8LoGNpc?enablejsapi=1" frameborder="0"></iframe>
<script src="https://www.youtube.com/iframe_api"></script>