I'm trying to make a website which will keep on adding video players to the page as the page is being scrolled down. Though I have some concerns that large amount of video players on a page can cause lag on the website and cause the website to slow down. I think I have experienced slow down during some tests of my website. So is it possible to detect whether the website is being slowed down due of the amount of elements on the web and so I can start deleting the video elements from the top of the page?
index.html:
window.onbeforeunload = function () {
this.scrollTo(0, 0);
}
var content = document.getElementById("content"),
timeout = undefined;
for (var x=0;x<50;x++) {
var video = document.createElement("video");
video.style.backgroundColor = "orange";
video.poster = "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c5/Big_buck_bunny_poster_big.jpg/220px-Big_buck_bunny_poster_big.jpg";
video.src = "https://dash.akamaized.net/akamai/bbb/bbb_3840x2160_60fps_18000k.mp4";
video.controls = true;
content.appendChild(video);
}
window.addEventListener("scroll", function () {
var $this = this;
window.clearTimeout(timeout);
timeout = setTimeout(function() {
var content_margin_top = $this.innerHeight * 0.11;
var last_player = content.children[content.querySelectorAll("video").length - 1];
if (last_player.offsetTop - content_margin_top <= $this.scrollY) {
for (var x=0;x<10;x++) {
var video = document.createElement("video");
video.style.backgroundColor = "orange";
video.poster = "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c5/Big_buck_bunny_poster_big.jpg/220px-Big_buck_bunny_poster_big.jpg";
video.src = "https://dash.akamaized.net/akamai/bbb/bbb_3840x2160_60fps_18000k.mp4";
video.controls = true;
content.appendChild(video);
}
}
}, 250);
});
body {
margin: 0;
}
#nav {
width: 100%;
height: 10%;
position: absolute;
top: 0;
background-color: rgb(108, 171, 247);
}
#content {
height: 100%;
width: 98%;
position: absolute;
top: 11%;
left: 1%;
}
video {
width: 100%;
height: 75%;
border: 1px solid black;
}
<html>
<head>
<title></title>
</head>
<body>
<div id="nav"></div>
<div id="content"></div>
</body>
</html>
I would think about the issue in a slightly different way: What should I do to make that page work as fast as possible, downloading as little data as possible and render only necessary containers when needed?
My recommendations:
1) Don't append and init video containers on during scroll. Render only thumbnails for future video containers using img tags. Making lazy loading for these images should be considered as well. Add "play" button to the center of preview container. Once user clicks on the button - render video tag with a proper src and play it.
2) Don't use a scroll event listener to detect containers offsets and lazy loading. Use Intersection API instead.
Related
I decided to make a Pac-Man game and after I did it and everything was working somewhat fine on local document I pushed my website on Github pages and decrease in fps was enormous. It turned out page was making recalculation for hundreds elements which caused 20ms+ delay.
Here's a small part of the code that still has performance difference between local and github-pages hosted website.
const gameBoard = document.getElementById("game-board");
const root = document.documentElement.style;
let elements;
let characterNode;
let position = 658;
makeLevel();
function makeLevel() {
for (let i = 0; i < 868; i++) {
const element = document.createElement("DIV");
element.style.backgroundPosition = `0 0`;
let character = document.createElement("DIV");
character.className = "yellow";
element.append(character);
gameBoard.append(element);
}
elements = Array.from(gameBoard.children);
characterNode = elements[658].children[0];
changePosition();
}
function changePosition() {
root.setProperty(`--yellow-sprite-y`, `-32px`);
characterNode.style.transform = `translateX(-20px)`;
setTimeout(() => {
characterNode.style.transform = "";
characterNode.classList.remove(`yellow-visible`);
position = position - 1;
characterNode = elements[position].children[0];
characterNode.classList.add(`yellow-visible`);
changePosition()
}, 200)
}
:root {
--yellow-sprite-y: -32px;
}
#game-board {
width: 560px;
height: 620px;
display: grid;
grid-template-columns: repeat(28, 20px);
background-color: #000000;
}
#game-board > * {
position: relative;
width: 20px;
height: 20px;
}
.yellow {
position: absolute;
top: -4px;
left: -5.5px;
width: 30px;
height: 28px;
z-index: 10;
}
.yellow-visible {
background-image: url("https://i.imgur.com/SphNpH6.png");
background-position: -32px var(--yellow-sprite-y);
transition: transform 200ms linear;
}
<div id="game-board">
</div>
The exact problem in this code is line 29 which on local document performs like this:
while after hosting it on Github performs this way:
Why is it working this way and what can I do to lessen the performance decrease on hosted page?
Amazingly everything works well and bug doesn't exist on CodePen, yet on Github it still persists.
After getting some feedback that my site works well for other users I shared it on CodePen and it also worked fine, day later somebody said there could be an extension that could do something like that and indeed Adblocker Ultimate caused the slow performance.
Is it possible to trigger an action on click if a video is set to background=1 (no controls)?
This is a Vimeo video, plus account (where background=1 is permitted).
Essentially, I have a Vimeo video with no controls set to loop and autoplay with a volume of 0. My implementation has an icon overlayed on top of the video, in the center. When clicked, it is set to full volume and the icon is hidden.
Once the volume is set to 1 and the icon is hidden, the person viewing should have the option of clicking the video so as to mute it (set volume to 0).
The problem is that I am not able to figure out how to target this click. I have tried attaching an .on('click') to the iframe, its parent, and as far up the chain as I can go but beyond that first click of the icon, the click is never registered.
Can anyone please offer any pointers on how to target a click on a Vimeo iframe video (or its parent container, etc)?
Here is my code thus far:
var iframe = document.getElementById('vimeo-video');
var player = new Vimeo.Player(iframe);
player.ready().then(function() {
var volume = 0
player.setVolume(volume);
$('#vimeo-video-play').on('click', function(event) {
if (volume > 0) {
player.setVolume(0);
} else {
player.setVolume(1);
}
$('#vimeo-video-play').hide();
});
});
Vimeo Promises
It appears that if you use Vimeo API, you must return a Promise. If you just want to do a simple task such as controlling the volume, the documentation gives this example:
player.setVolume(0.5).then(function(volume) {
// volume was set
}).catch(function(error) {
switch (error.name) {
case 'RangeError':
// the volume was less than 0 or greater than 1
break;
default:
// some other error occurred
break;
}
});
Not simple, and it's overkill. It's not apparent by looking at it but if you see then(), await async, new Promise you can say with 99.9% certainty that a Promise will be returned. I haven't looked deep enough into player.js but I think each method is wrapped in a Promise so as far as I could tell, we can just return the method without using all of that extra crap. So compare the above code to the following:
var sVol = player.setVolume(1); return sVol;
So I believe when invoking a Vimeo API method, we can return the function as a value. There's no work involving what exactly that value is because it's going to be either resolved or rejected. A Promise is also immutable so returning the function itself should be a guaranteed resolve (concerning Vimeo methods, not Promises in general).
Overlay Layout
Instead of clicking an iframe which is filled with a video player that will do 100 other tasks except your custom callback, you need to click an element outside of the iframe. As a background video without controls, you are very limited. I suggest an element that covers the iframe player edge to edge so that the user clicks it and nothing else. The following are the steps to setup an overlay:
Wrap the iframe player (#vFrame0 in the demo) in a relpos🞳 parent container (a.k.a. .box)
Place an older sibling🗡 abspos🞳 element (a.k.a. .overlay) inside the parent with the iframe player.
Set the older sibling "above" the player by setting its z-index at l more than the iframe player and the necessary CSS properties (see demo CSS for .overlay) to ensure that the older sibling covers the iframe player edge to edge completely.
Register the click event to the overlay element so when the player ignores the click event, the event will bubble back to the older sibling overlay element. The overlay element is now a proxy of sorts and will run the callback:
var sVol = player.setVolume(1); return sVol
Demo
This Demo does not function because there are conflicts between Vimeo's connections and SO's security measures. For a functioning Demo review this Plunker or this Plunker.
<!DOCTYPE html>
<html>
<head>
<base href="https://player.vimeo.com/api/demo">
<meta charset='utf-8'>
<style>
.box {
display: table;
border: 3px dashed red;
position: relative;
}
.overlay {
cursor: pointer;
display: table-cell;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
min-height: 100%;
z-index: 1;
}
.overlay::before {
position: absolute;
cursor: pointer;
display: block;
content: '🕪';
font-size: 2em;
width: 2em;
height: 2em;
color: cyan;
opacity: 0;
transition: opacity .5s ease 3s;
}
.overlay:hover::before {
opacity: 1;
transition: .5s ease;
}
.mute.overlay::before {
content: '🕩';
}
</style>
</head>
<body>
<figure class='box'>
<figcaption class='overlay mute'></figcaption>
<div id='vFrame0' data-vimeo-id="76979871" data-vimeo-autoplay data-vimeo-background></div>
</figure>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src='https://player.vimeo.com/api/player.js'></script>
<script>
var player = new Vimeo.Player('vFrame0', options);
var options = {
mute: true
};
$('.overlay').on('click', function(e) {
var state = $(this).hasClass('mute') ? player.setVolume(1) : player.setVolume(0);
$(this).toggleClass('mute');
return state;
});
</script>
</body>
</html>
🞳relpos: position: relative | abspos position: absolute
🗡older sibling an element that is positioned before the element being referred to.
$('.button-trigger').click(function (e) {
let element = $('iframe').attr('src');
element = element.replace("autoplay=0", "autoplay=1");
$('iframe').attr('src', element);
});
I'm testing out a script found here
Show images one after one after some interval of time
I don't get any errors, and it rotates the images but the images simply appear as little broken squares, which I'm assuming is the broken image icon.
I'm using google chrome, and on MAC, running mavericks. Also, all the files are on a user.local(server/host) page I created, so if I go into the directory structure in my browser, and I click on the images the images show just fine. It is only when I try to load the .html file that they appear broken. I've also tried loading images with PHP and I get the same broken image icon.
This is what I mean when I say all the files are my local computer as a server/host:
Anyone know why this is happening and how to fix it?
This is the script I'm using:
var current = 0;
var rotator_obj = null;
var images_array = new Array();
images_array[0] = "rotator_1";
images_array[1] = "rotator_2";
images_array[2] = "rotator_3";
var rotate_them = setInterval(function(){rotating()},4000);
function rotating(){
rotator_obj = document.getElementById(images_array[current]);
if(current != 0) {
var rotator_obj_pass = document.getElementById(images_array[current-1]);
rotator_obj_pass.style.left = "-320px";
}
else {
rotator_obj.style.left = "-320px";
}
var slideit = setInterval(function(){change_position(rotator_obj)},30);
current++;
if (current == images_array.length+1) {
var rotator_obj_passed = document.getElementById(images_array[current-2]);
rotator_obj_passed.style.left = "-320px";
current = 0;
rotating();
}
}
function change_position(rotator_obj, type) {
var intleft = parseInt(rotator_obj.style.left);
if (intleft != 0) {
rotator_obj.style.left = intleft + 32 + "px";
}
else if (intleft == 0) {
clearInterval(slideit);
}
}
</script>
<style>
#rotate_outer {
position: absolute;
top: 50%;
left: 50%;
width: 320px;
height: 240px;
margin-top: -120px;
margin-left: -160px;
overflow: hidden;
}
#rotate_outer img {
position: absolute;
top: 0px;
left: 0px;
}
</style>
<html>
<head>
</head>
<body onload="rotating();">
<div id="rotate_outer">
<img src="images/owl.png" id="rotator_1" style="left: -320px;" />
<img src="images/bee.png" id="rotator_2" style="left: -320px;" />
<img src="images/owl.png" id="rotator_3" style="left: -320px;" />
</div>
</body>
</html>
This sounds like a bad url in the src attribute of the image. right click the broken image icon and inspect it (most, if not all browsers support this) and see where the url leads.
Fixing url, try one of the following:
Try adding a / in front of your image links
Try including the full path on the image including the http, also you can try excluding the http and writing //yourwebsite.com/images/foo.jpg instead and see if that fixes things.
If you work on some framework there is bound to be a base url variable which you can use instead of manually writing the url
I have a kiosk application running on Ubuntu server 14.04.3 and chrome. Currently I have some code which hides the mouse if there was no movement for 2 seconds and once the user attempts to move the mouse again it shows up again. The trick is by using a cursor:none and adding an overlay:
js:
var body = $('body');
function hideMouse() {
body.addClass("hideMouse");
body.on('mousemove', function(){
if(window.hiding) return true;
window.hiding = true;
body.removeClass("hideMouse");
$('div.mouseHider').remove();
clearTimeout(window.hideMouse);
window.hideMouse = setTimeout(function(){
body.addClass("hideMouse");
$('<div class="mouseHider"></div>').css({
position: 'fixed',
top: 0,
left: 0,
height: '100%',
width: '100%',
zIndex: 99999
}).appendTo(body);
redraw(document.body);
setTimeout(function(){
window.hiding = false;
}, 100);
}, 4000);
});
}
function redraw(e) {
e.style.display = 'none';
e.offsetHeight;
e.style.display = 'block';
}
css:
body.hideMouse *, body.hideMouse{
cursor: none;
}
body.hideMouse *{
pointer-events: none !important;
}
This code works perfectly fine but there is only 1 caveat. When the page first loading it attempts to hide the mouse with the same trick but the mouse is still sticking there since it just didn't repainted the layer I guess. If I want it to work, I have to move the mouse a little bit and from then on it will work as expected and hide the mouse. The thing is that the kiosk application is restarting every day which means I boot the X display again and the mouse is being reset to the middle of the screen and it just sticks there until I move it a little bit. I hope you understand what I mean.
Do you guys have any idea how I can fix this?
You don't need all that code to do what you want. You could do:
Create a setTimeout to hide the cursor after 2s as soon as the page is loaded
When someone moves the mouse, you:
2.1. Show the cursor again
2.2. Clear the current setTimeout
2.3. And create the setTimeout to hide the cursor after 2s again.
The code below should work for you:
document.addEventListener('DOMContentLoaded', function() {
var cursorNone = document.getElementById('cursor-none');
var t = setTimeout(hideMouse, 2000);
document.addEventListener('mousemove', function(e) {
showMouse();
clearTimeout(t);
t = setTimeout(hideMouse, 2000);
});
function hideMouse() {
cursorNone.classList.remove('hidden');
}
function showMouse() {
cursorNone.classList.add('hidden');
}
});
#cursor-none {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 9999;
cursor: none;
background-color: transparent;
}
.hidden {
display: none;
}
<body>
<div id="cursor-none" class="hidden"></div>
</body>
Demonstration: http://jsfiddle.net/calvintennant/NrJ8T/show/
When I force a window resize by doing: $(window).resize() my listener is called, and everything is fine. However if I actually resize the window, I'm getting multiple resize events called within the same frame.
Timeline during forced resize:
Timeline during natural resize:
Is this a bug in Chrome, or am I misunderstanding something?
As pointed out by #avram-lavinsky, resize events can be called multiple time per frame.
Updated example using Request Animation Frame (seen first here https://developer.mozilla.org/en-US/docs/Web/Reference/Events/resize):
http://jsfiddle.net/calvintennant/v69WW/
# HTML
<div class="box-1"></div>
<div class="box-2"></div>
<div class="box-3"></div>
# CSS
html, body {
margin: 0;
}
.box-1 {
background: #00F;
bottom: 0;
position: absolute;
width: 100%;
}
.box-2 {
background: #0F0;
height: 30px;
position: relative;
}
.box-3 {
background: #F0F;
height: 66px;
position: relative;
}
# JS
var box1 = $('.box-1');
var box2 = $('.box-2');
var box3 = $('.box-3');
var drawing = false;
var resizeFired = false;
var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame;
$(window).resize(function() {
// set resizedFired to true and execute drawResize if it's not already running
if (drawing === false) {
resizeFired = true;
drawResize();
}
});
function drawResize() {
var height;
// render friendly resize loop
if (resizeFired === true) {
resizeFired = false;
drawing = true;
height = $(window).height();
height -= $(box2).height();
height -= $(box3).height();
$(box1).height(height);
requestAnimationFrame(drawResize);
} else {
drawing = false;
}
}
$(window).resize();
Window resize as a user action is real-time event. It fires many times as the user drags.