I'm trying to use Video.js Thumbnails to display seek-preview thumbnails when hovering over my Video.js player track bar. I have extracted thumbnails from my videos to sprite sheets using ffmpeg. Each sprite sheet holds a set number of thumbnails, and when one gets filled up during thumbnail extraction, a new sprite file is created.
On my video player page, I am using javascript to create an object to load into the thumbnails() function. If I wanted thumbnails for every five seconds, my object may look something like:
th_object = {
0: {
src: 'source001.jpg',
style: {
left: '-40px',
width: '4800px',
height: '45px',
clip: 'rect(0, 80px, 45px, 0)'
}
},
5: {
style: {
left: '-120px',
clip: 'rect(0, 160px, 45px, 80px)'
}
},
...
}
When it is time to change sprite sources (say around 60 seconds of video), I am using the same object:
...
60: {
src: 'source002.jpg',
style: {
left: '-40px',
width: '4800px',
height: '45px',
clip: 'rect(0, 80px, 45px, 0)'
}
},
...
Then I call the thumbnails() function for my video player (called "video" here):
video.thumbnails(th_object);
So, what this is doing is creating a placeholder in my page for the thumbnails and loading the source image and offsetting it, cropping the parts of the sprite that don't need to be shown. But there is only one placeholder being created in the page. For example, an excerpt from my HTML might look like:
<div class="vjs-thumbnail-holder" style="left: 157px;">
<img src="/storage/source001.jpg" class="vjs-thumbnail" style="left: -680px; width: 4800px; height: 45px; clip: rect(0px 720px 45px 640px); visibility: visible;">
</div>
As I move the mouse across the progress bar, the values get updated and my source will eventually change. I am not entirely sure that both images are being loaded on the page, as when I inspect the page elements, there is only one visible at any given time.
When I hover the mouse to a point when a non-first sprite sheet should be loaded as the thumbnail source, the source seems to get stuck. So if I moved the mouse back to a point where the first sprite sheet should be loaded, it won't, and the wrong sprite is displayed for that point in the video.
My first thought is that I need different HTML elements for my sprite sheet sources, but I am new to web development, so modifying the Video.js Thumbnail code this way may not be viable for me. Any help concerning this would be greatly appreciated. If you have any suggestions about other players that incorporate a seek-preview thumbnail display or a better way to accomplish this, those would also be greatly appreciated.
After working with the Thumbnails code for a bit, I discovered a way to get the results I need. The portion of the code that updates the img src for the thumbnails looks like this:
// apply updated styles to the thumbnail if necessary
mouseTime = Math.floor(event.offsetX / progressControl.width() * duration);
for (time in settings) {
if (mouseTime > time) {
active = Math.max(active, time);
}
}
setting = settings[active];
if (setting.src && img.src != setting.src) {
img.src = setting.src;
}
if (setting.style && img.style != setting.style) {
extend(img.style, setting.style);
}
Essentially, I needed to check the current img src against the source source required by the current mouseTime. So I added an extra check that looks like this:
var x = Math.floor(mouseTime / 300);
x = x * 300;
var sourceNeeded = settings[x];
if (setting.src && (img.src != setting.src)) {
img.src = setting.src;
}
if (sourceNeeded.src && (img.src != sourceNeeded.src)) {
img.src = sourceNeeded.src;
}
if (setting.style && img.style != setting.style) {
extend(img.style, setting.style);
}
The hard-coded 300 from above comes from the fact that my sprite sheets contained 300 seconds worth of thumbnails. (Note that in the example in my original post I used 60 instead.) The x variable determines the index in th_object for the proper source location, and if that doesn't match the current img src, I update it. This will allow proper transition from one img src to another as needed.
Hopefully this will help someone in a similar situation in the future.
Related
I'm building a photo application using electron that loads user photos from the file system. These photos can easily be 7MB or more in size. The application allows the user to switch between photo's using the keyboard arrows, at which point I want the new photo to display extremely fast.
For a 7MB image, just changing the src of an existing image tag in the DOM can take ~200-300ms, webkit must load the file, decode the file, and render the file on the page. The loading and decoding take 100-150ms each. (actually the profiler just says 2 x decoding, but the next step removes one of those decodes, so I presume it's related to the file read).
Preloading an img tag...
var img = new Image();
img.src = "/path/to/photo.jpg"
...means that webkit preloads the file, and this strips the file load time, but there is still a 100-150ms delay in appending like this...
domElement.appendChild(img);
...because the read data must still be decoded for the item to be appended to the DOM.
Is there a way to pre-decode the image, so that appending to the DOM does not have a 100-150ms delay, and only the fast rendering is required?
No you cannot "pre-decode". However, you can pre-append the img in an effectively invisible way by applying the style width: 1px; height: 1px; opacity: 0.01, and webkit won't redo the work if you append again.
You can even remove the image in the mean time, provided it has had time to fully decode, and webkit will hold on to the decoded data (although I'm not sure for how long).
If you want to be absolutely certain it will load fast, you could do one of two things. Reveal the img tag by removing the styles above, or by loading the same img tag in a different part of the DOM, while leaving the 'pre-appended' one in place. This will take between 3ms and 20ms in my experience.
BE CAREFUL regarding cleanup if you are using a lot of user defined photo contents.
In my experience, simply removing numerous img elements from the DOM will cause memory leaks (and electron windows to crash). I would advise that you either set the img.src to null after removing the image from the DOM, or set the entire img to null if you no longer need the Image instance.
You could play with the following code (use images of your own) using the chrome devtools timeline to measure the render speeds of photos in different scenarios.
<style>
/*#preload img {
width: 1px;
height: 1px;
opacity: 0.01;
}*/
</style>
<button>Toggle Image</button>
<div id="container"></div>
<div id="preload"></div>
<script>
"use strict"
const button = document.getElementsByTagName('button')[0]
, container = document.getElementById('container')
, preload = document.getElementById('preload')
, img1 = new Image()
, img2 = new Image()
var current = img2
img1.src = './img1.JPG'
img2.src = './img2.JPG'
preload.appendChild(img2)
setTimeout(function(){
preload.removeChild(preload.childNodes[0])
}, 200)
button.addEventListener('click', function(e){
toggleImg()
})
function toggleImg(){
if (current === img1) {
setImg(img2);
} else {
setImg(img1)
}
}
function setImg(img){
if (container.hasChildNodes()) container.removeChild(container.childNodes[0])
container.appendChild(img)
current = img
}
</script>
I'd suggest experimenting with using the HTML5 Canvas to render your images, that way you can load the next image ahead of time and have more direct control over the caching strategy.
Background
I've created a slideshow application with asp.net (C#) and html5/css3/javascript (w/ a bit of jQuery). I'm trying to display this on a Raspberry Pi 2 device running Windows 10 (IoT version) inside the Windows Universal WebView component.
I'm having issues with slides lagging. My slideshow is based off of one div with 3 background images:
The top image is the slide image. Displayed with background-size: contain.
The middle image is a transparent gradient meant to lay over the bottom image for affect.
The bottom image is server generated on image upload and is a zoomed and blurred image meant to give a kind of gradient splash mapping of color hot zones. Any image artifacts left from this process are smoothed by image #2.
Problem
I switch the background image with one line of code:
slide.style.backgroundImage = "url(" + slides[slideIndex % slides.length] + "), " +
"url(../images/egg-shell.png), " +
"url(" + blurred[slideIndex % blurred.length] + ")";
Works great in my browser, however, on the Raspberry Pi the bottom image with middle image overlayed displayed way before the top image.
Abnormalities
I have had to do a couple things to make this work with the Raspberry Pi and Webview component, I only list these because they might be causing my problem:
After changing the background image, I have to set the slide element's display property to none, then back to block to redraw the background image else it won't change.
I'm preloading images by loading a bunch of JS Image objects with paths specified by webservice, then waiting for each image to finish reporting .onload to start the slideshow.
The whole application is pretty lightweight. If needed I can provide it but there must be something simple I'm missing. I don't know the efficiency if I were to load each image in a separate <img> element and then set z-indexs. Nor do I know if the efficiency would increase by letting the images load in separate slides behind the current one. This is why the question is asking about DOM manipulation and Javascript. At any rate, thanks for reading this long explanation and hopefully you can help!
Like this: http://jsfiddle.net/utwqsb45/ or if you prefer
$(function($){
var $slides = $('.slide');
function transition(){
var $current = $('.slide.showing'),
$next = $('.slide[data-number="' + parseInt($current.data('number') + 1) + '"]');
if (!$next.length) {
$next = $('.slide[data-number="1"]');
}
requestAnimationFrame(function(){
$current.removeClass('showing');
$next.addClass('showing');
});
}
setInterval(transition, 1000);
})($);
.slide {
width: 99%;
height: 99%;
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
display: none;
margin: 0 auto;
}
.slide.showing {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="slide showing" data-number="1">one</div>
<div class="slide" data-number="2">two</div>
<div class="slide" data-number="3">three</div>
How does Google Doodle work?
When i search for it, i found following
Animated Gif
Animated Jpeg Frame. Sprite image will have all frames and this frame is animated using javascript.
Canvas
Which one is correct?
First they enclose the <img> tag JPEG with all the animation frames inside a <div> tag that has a fixed height of 182 pixels and which hides overflow. This creates a fixed window so to speak, which masks all but current animation frame. The image is animated using JavaScript, which changes the top property for the absolutely positioned image to slide it up a fixed interval with the setTimeout() function.
Here is some code of example by Google from one of reference:
<div style="height:182px;position:relative;width:468px;overflow:hidden">
<img border="0" src="source.jpg" id="filmstrip" style="position: absolute; height: 2912px; top: -0px; display: block; ">
</div>
Jquery:
<script>
function naiveAnimation(id) {
var img = document.getElementById(id);
var offset = 0;
var animate = function() {
//slide the image correct frame of animation given by offset
img.style.top = -offset + "px";
//calculate offset to next frame
offset = Math.floor(offset + 182);
//if we are not yet on the last frame...
if(offset < 2912) {
//call me again in half a second
window.setTimeout(animate, 500);
} else {
//at last frame, so all done!
}
};
//start the animation
animate();
}
naiveAnimation('filmstrip');
</script>
I would go for the Animated JPEG and Canvas, although APNG may work too. I haven't seen a 256-bit color image on a doodle. Maybe even a webm. Some doodles have sound and some are interactive, so I think they use whatever they see suitable for their purposes.
I'm applying a repeated background image from a canvas to a div via javascript like this:
var img_canvas = document.createElement('canvas');
img_canvas.width = 16;
img_canvas.height = 16;
img_canvas.getContext('2d').drawImage(canvas, 0, 0, 16, 16);
var img = img_canvas.toDataURL("image/png");
document.querySelector('#div').style.backgroundImage = 'url(' + img + ')';
I have to update it quite frequently. The problem is it flickers upon change, it doesn't appear to happen in Chrome but it's really bad in Firefox and Safari. Is it possible to stop this? I didn't think it would happen since it's a dataurl and therefore doesn't need to be downloaded.
Solution:
// create a new Image object
var img_tag = new Image();
// when preload is complete, apply the image to the div
img_tag.onload = function() {
document.querySelector('#div').style.backgroundImage = 'url(' + img + ')';
}
// setting 'src' actually starts the preload
img_tag.src = img;
Try to preload the image resource to the device storage by including the image in DOM like in the following HTML-Code. Maybe the error comes up because the image resource need to be loaded which takes some time (flickering).
<img src="imageToPreload.png" style="display:none;" alt="" />
You may prefer to use sprites-images. By using sprites your application will need less HTTP-Requests to load all ressources into your page. Also add the following CSS styles if you are using css animations. It will prevent background flickering on mobile devices:
-webkit-backface-visibility: hidden;
-moz-backface-visibility: hidden;
-ms-backface-visibility: hidden;
Preload your image like this, no need to include a <img> with display: none
<link rel="preload" href="/images/bg-min.png" as="image">
Try adding this css to your background element:
-webkit-backface-visibility: hidden;
-moz-backface-visibility: hidden;
-ms-backface-visibility: hidden;
It should help with flickering..
You can also "force" hardware acceleration by adding this to your background element:
-webkit-transform: translate3d(0, 0, 0);
Another option is to use image instead of DIV and change only the image url.
I struggled with this for a bit, tried preloading, appending the image to the document, etc.
In the end, I resaved the JPEG without the "Progressive" option.
That fixed the rolling flicker when the img src was swapped.
In my case changing height: 1080px; (background height) to height: fit-content;
I think that preloading all the images is essential in any case. What I found is that the way the browsers behave while changing the background image dynamically is different from one another. In Firefox for example it flickers when the change is frequent however in Chrome and Safari it doesn't.
The best solution I came up with so far is drawing the image inside a child canvas that fills the space of the whole parent div.
In all cases, the images you are using must be optimized as it affects the rendering performance.
My javascript code that works now, looks like this
const pic = new Image();
const pic2 = new Image();
pic.src="../images/settings_referrals_anim.gif";
pic2.src="../images/settings_referrals_still.png";
I don't actually reference that code in the query, for example, i use
document.querySelector(".button_Settings_referrals").addEventListener("mouseover", function() {
myDiv.style.backgroundImage = "url('../images/settings_referrals_anim.gif')";
But it seems to work. If I replace the long URL with const pic for example it doesn't work, and if I include the image object declaration and location at first time in the assignment, then the flickering stops.
This does not address all of the specifics noted by the OP, but might be useful for others. Tested in Chrome 97, Firefox 96, Android 11, iOS 15.
I have a div that includes these CSS parameters...
#div_image {
background-image: url( [Path to low-res image] );
background-size: cover;
}
I have a corresponding class that looks like this...
.div_image_highres {
background-image: none !important;
}
The corresponding class has a pseudo-element defined as follows:
.div_image_highres::before {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
content: " ";
background-image: url( [Path to highres image] );
background-repeat: no-repeat;
background-position: 50% 0;
background-size: cover;
opacity: 1;
display: block;
}
I have an img element that also points to the high-res image...
<img id="img_highres_preload" src=" [Path to high-res image ] ">
The img element has a corresponding style which allows the image to be displayed (ensuring that image file loads) but not seen...
#img_highres_preload {
width: 1px;
height: 1px;
}
Two notes: (1) I realize a lot of people use other methods of pre-loading (e.g., programmatically), but I have a personal preference for this method. (2) See the addendum about the reliability of the load event.
Last but not least, I have some Javascript (jQuery) that adds the "high-res" class to "div_image" once the high-res file is loaded...
$(document).ready(function() {
$("#img_highres_preload").off().on("load", function() {
$("#div_image").addClass("div_image_highres");
});
});
This could easily be vanilla JS, but since I use jQuery throughout my code, I like having a consistency.
Here's a summary of what's happening...
Presumably, the low-res image is loaded first and becomes the background image for the div. Even if that does not occur, everything will work as intended (i.e., the high-res image will be displayed).
When the high-res image loads into the img element (i.e., Javascript confirms that the high-res file is loaded), the "div_image_highres" class is applied to "div_image".
As result, the div switches to the high-res image without flashing. In my experience, if anything, it shifts a little to the left; but that often doesn't occur and, if it does, it's not inelegant.
And here's the primary reason I use this approach when required: In my application, there are multiple panels the user can navigate, which results in one panel sliding out of view and the new one into view. If I don't use a pseudo-element (as described above) for displaying a high-res image, the image flickers when its div is hidden and re-displayed. With the above-described technique, I can slide the div in and out of view without any flickering.
Regarding the Load Event
You can't depend on the load event firing. For instance, it typically does not fire when the browser has cached an image. So to make a long post even longer, here's the enhancement I have in my code to accommodate that reality...
I modify the document.ready event (shown above) to look like this:
$(document).ready(function() {
positionOnPage(true);
$("#img_highres_preload").off().on("load", function() {
checkImage();
});
});
checkImage = function() {
var image = $("#img_highres_preload")[0];
if (!image.complete || (typeof image.naturalWidth != "undefined" && image.naturalWidth == 0)) {
console.log("Waiting for high-res image.");
}
else if (!$("#div_home").hasClass("div_home_highres")) {
$("#div_home").addClass("div_home_highres");
$("#img_highres_preload").remove();
}
}
The checkImage function examines the image element to see whether an image has in fact been loaded. In this code example, it is a little redundant — that is, the img element has confirmed the load, so there's usually no need to check it (unless there is some reason to believe the file is being misloaded).
I might do it as shown because I also call checkImage from other places in my code, so if I have more of a programmatic response (unlike the simple version shown), I want all of that code in the same place and written just once. The checkImage function might be called when triggered by a timer or when the section displaying the intended image is about to be displayed. Perhaps something like this...
if (sectionName == "[whatever]" && $("#img_highres_preload").length === 1) {
checkImage();
}
In this example, I look for the presence of the preload img element because I know that my previous function removes the element after it has fulfilled its purpose.
This post has a stripped-down version to illustrate the concept. As written above, it only accommodates a single known img element, so the code could be extended to call checkImage with some parameters (e.g., the name of an image or the element itself) and checkImage could look for the existence of the preload element, so that check occurs in one place. It can be fairly fancy, so I went with the simplest example for this post.
In many cases, this stripped-down version is all I need because typically I only use a high-res photo for a window background image. I either start with the display of a low-res image and switch it out as soon as the high-res file is loaded, or I have some animation that gets triggered after I confirm the presence of the high-res image.
A good case for a more generalized version is when I need a series of images loaded at the outset and don't want to start until all of them are ready. In those cases, the web page might begin with some welcome text that stays displayed until all images have been confirmed.
Hey Guys I know this has been an older question but if you are still flickering after all this you can simply put the final version behind you background div. That flicker is seeing behind the image you currently have so if its the final image it will be smooth.
I need to add a Transparent image on top of all images on a page. The goal is if a user were to do a simple right click and save of an image, they would save the transparent image.
I do realize this is not a guaranteed method and that none exist to prevent image theft but simply a measure that a client wants added to prevent your average non tech person from saving images.
Using JavaScript I would like to find all images or all images within a certain Div.
Apply a new image overlay on top of these images that will have the same width and height of the image they are covering
I am not sure how to do this with JavaScript and was hoping someone would have a quick fix or example. I was unable to find anything so far on Google or SO. Appreciate any help
I have this JS which gets all images on a page so far...
// Get all images on a Page
function checkimages() {
var images = document.images;
for (var i=0; i<images.length; i++){
var img =images[i].src;
// Add new transparent image on top of this image
alert(img);
}
}
I would advise you to work with jQuery (or similar library) to keep things easier. I would even write a small jquery extension to make it easy to recycle the code, and apply it on any div (or other wrapper), wich child images you want to be overlayed.
My code would look something like this:
// jquery plugin to create overlays
// src is the url of the overlay image
// apply to any container that contains images to be overlayed
$.fn.overlayImages = function(src) {
// loop trough the images
$(this).find('img').each(function() {
// cache some variables
var $img = $(this);
var $parent = $img.parent();
// make the parent relative, if not yet absolute or fixed, for easy positioning
if ($parent.css('position') !== 'fixed' && $parent.css('position') !== 'absolute') {
$parent.css('position', 'relative');
}
// get the position of the image
var position = $img.position();
// clone the image
var $overlay = $img.clone();
// set the styling, based on the img, for exact positioning
$overlay.css({
top: position.top,
left: position.left,
position: 'absolute',
width: $img.width(),
height: $img.height()
});
// change the src attribute for the overlay
$overlay.attr('src', src);
// insert the overlay to the DOM
$overlay.insertAfter($img);
});
}
// when the DOM is loaded (not just ready, the images need to be there to copy their position and size)
$(window).load(function() {
// apply the overlay plugin to the wrapper of the images
$('#replace-images').overlayImages("http://www.riptideinnovations.com/images/watermark.png");
});
I added the step by step explanation inside the code as comments, but do feel free to ask if you want any further explanation.
I set up a small fiddle to demonstrate: http://jsfiddle.net/pP96f/6/
I don't know if this would help, but you could make your images all div's with backgrounds like this:
<div style="background-image: url('<your_image>');"></div>