Can I "freeze" fixed elements to take a screenshot with getDisplayMedia? - javascript

I'm implementing a chrome extension with javascript to take a screenshot of a full page, so far I've managed to take the screenshot and make it into a canvas in a new tab, it shows the content of a tweet perfectly, but as you can see, the sidebars repeat all the time (Ignore the red button, that's part of my extension and I know how to delete it from the screenshots) screenshot of a tweet
This is the code I'm using to take the screenshot:
async function capture(){
navigator.mediaDevices.getDisplayMedia({preferCurrentTab:true}).then(mediaStream=>{
scrollTo(0,0);
var defaultOverflow = document.body.style.overflow;
//document.body.style.overflow = 'hidden';
var totalHeight = document.body.scrollHeight;
var actualHeight = document.documentElement.clientHeight;
var leftHeight = totalHeight-actualHeight;
var scroll = 200;
var blob = new Blob([document.documentElement.innerHTML],{ type: "text/plain;charset=utf-8" });
console.log('total Height:'+totalHeight+'client height:'+actualHeight+'Left Height:'+leftHeight);
var numScreenshots = Math.ceil(leftHeight/scroll);
var arrayImg = new Array();
var i = 0;
function myLoop() {
setTimeout(function() {
var track = mediaStream.getVideoTracks()[0];
let imgCapture = new ImageCapture(track);
imgCapture.grabFrame().then(bitmap=>{
arrayImg[i] = bitmap;
window.scrollBy(0,scroll);
console.log(i);
i++;
});
if (i <= numScreenshots) {
myLoop();
}else{
document.body.style.overflow = defaultOverflow;
saveAs(blob, "static.txt");
printBitMaps(arrayImg, numScreenshots, totalHeight);
}
}, 250)
}
myLoop();
})
}
async function printBitMaps(arrayImg, numScreenshots, totalHeight){
var win = window.open('about:blank', '_blank');
win.document.write('<canvas id="myCanvas" width="'+arrayImg[0].width+'px" height="'+totalHeight+'px" style="border:5px solid #000000;"></canvas>');
var e = numScreenshots+1;
function printToCanvas(){
setTimeout(function(){
var canvas = win.document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.drawImage(arrayImg[e], 0, 200*e);
e--;
if(e>=0){
printToCanvas();
}
},10);
}
printToCanvas();
}
Do you know any way by CSS or javascript that I can use to make the sidebars stay at the top of the page so they don't keep coming down with the scroll?

It's not really a case of "the sidebars ... coming down with the scroll" - the code you're using is taking a first screenshot, scrolling the page, taking another screenshot and then stitching it onto the last one and iterating to the bottom of the page. Thus it's inevitable you're seeing what you see on the screen at the point you take the subsequent screenshots.
To resolve your issue, after your first screenshot you would need to set the div element for the side bar to be display=None by use of CSS. You can find details of the side bar by using the browser Dev Tools, right clicking an using "Inspect" in Chrome.
From what I can see, Twitter seems to use fairly cryptic class names, so it might be easiest and more robust to identify the div for the side bar with some other attribute. It appears they set data-testid="sidebarColumn" on it so give using that a go (but YMMV).

Related

Get offsetHeight after manipulating DOM

I wrote the following code:
var image_pinch_help = document.getElementById("image_pinch_help");
var pinch_img_el = document.getElementById("pinch_img_el");
function openImage(img_url) {
pinch_img_el.src = img_url;
if (pinch_img_el.complete) {
IMGloaded();
}
else {
pinch_img_el.addEventListener('load', IMGloaded())
pinch_img_el.addEventListener('error', function() {
alert('error')
});
}
image_pinch_help.style.display = "block";
document.getElementById("pinch_values").setAttribute("content", "");
}
function IMGloaded() {
var screen_height = window.screen.height;
console.log("HERE IS THE HEIGHT: " + screen_height);
var img_height = pinch_img_el.offsetHeight;
console.log("HERE IS THE IMAGE: " + img_height);
}
The code starts at the function openImage(). This function is called to open an Image at my webpage. So the image gets the full width of the screen. The screen where the image is shown on is image_pinch_help. You see that I make this screen visible. To allow the user to zoom into the image I am manipulation the HTML DOM, that the user can scroll at the whole page. I do this here: document.getElementById("pinch_values").setAttribute("content", "");.
When the Image loaded I need to get the height of the screen, which works perfectly. But I also need to get the height of the loaded image. The problem is that its always returning an empty string. I read a long time ago that this could be caused by HTML DOM Manipulations.
But how to fix this disaster?
~Marcus

Images not preloaded properly

I am having a jQuery script that loads 15 images and their hover versions (the hover versions are used when... hovering, not when the page loads). In both Chrome and Firefox in the Network tab, i see this :
images/img1.png
images_hover/img1.png
This must mean the hover images are preloaded correctly, but... when i actually hover and those images are used, they are being loaded again. Here is the preload code :
var prel = new Image();
prel.src = "http://hdodov.byethost15.com/color-game/graphics/parts_hover/" + id + ".png";
I tried using the whole path - http://hdodov.byethost15.com/color-game/graphics/parts_hover/ and the short path too (where the root and the script is) - graphics/parts_hover/. It made no difference.
Could this be caused because my images have the same name? They are in different directories though.
For the next question, you really should paste more code, which makes it easier to help you. I checked your URL you provided, but for other people that might have the same problem, it will be hard to understand what went wrong...
OK, as I said, you are always requesting he images again on hover state...
This worked for me:
var context = new Array(15),
canvas = new Array(15),
canvasNum = -1,
hElem,
hElemPrev,
mousePos = {x:-1, y:-1},
images = [], //<-- Store preloaded images here
imagesHover = []; //<-- Store preloaded images here
... then save them on building like this:
function drawMenuItems(id, width, height){
var canNumHolder, createCanvas;
$('#canvas_holder').append($('<canvas></canvas>')
.attr({
width:width,
height:height,
id:id
})
);
canvasNum++;
canvas[canvasNum] = document.getElementById(id);
context[canvasNum] = canvas[canvasNum].getContext('2d');
canNumHolder = canvasNum;
images[id].crossOrigin = 'Anonymous';
images[id].onload = function(){
context[canNumHolder].drawImage(images[id],0,0);
};
images[id].src = 'graphics/parts/' + id + '.png';
//Hover states
imagesHover[id] = new Image();
imagesHover[id].src = "graphics/parts_hover/" + id + ".png";
}
... give just the id...
function hoverStateChange() {
//....rest of the code
if(hElem >= 0){
drawImageOnCanvas(
canvas[hElem],
context[hElem],
"hover", //pass somethink to checke what you want to do
$(canvas[hElem]).attr('id')
);
//....rest of the code
//change to normal texture
if(hElemPrev >= 0){
drawImageOnCanvas(
canvas[hElemPrev],
context[hElemPrev],
"", //pass empty for other state
$(canvas[hElemPrev]).attr('id')
);
$(canvas[hElemPrev]).removeClass('active');
$('#text_holder').removeClass('a' + hElemPrev);
}
.. and finally
function drawImageOnCanvas(canv, contxt, state, src){
contxt.clearRect(0,0,400,400);
if(state == "hover"){
contxt.drawImage(imagesHover[src],0,0);
}else {
contxt.drawImage(images[src],0,0);
}
}
Like this, you chache you images and not calling them again and again...
I hope it helps.
Yuo can preload images with css like this:-
#preload-01 { background: url(http://domain.tld/image-01.png) no-repeat -9999px -9999px; }

Capture group of HTML5 video screenshots

I am trying to generate a group of thumbnails in the browser out of a HTML5 video using canvas with this code:
var fps = video_model.getFps(); //frames per second, comes from another script
var start = shot.getStart(); //start time of capture, comes from another script
var end = shot.getEnd(); //end time of capture, comes from another script
for(var i = start; i <= end; i += 50){ //capture every 50 frames
video.get(0).currentTime = i / fps;
var capture = $(document.createElement("canvas"))
.attr({
id: video.get(0).currentTime + "sec",
width: video.get(0).videoWidth,
height: video.get(0).videoHeight
})
var ctx = capture.get(0).getContext("2d");
ctx.drawImage(video.get(0), 0, 0, video.get(0).videoWidth, video.get(0).videoHeight);
$("body").append(capture, " ");
}
The the amount of captures is correct, but the problem is that in Chrome all the canvases appear black and in Firefox they always show the same image.
Maybe the problem is that the loop is too fast to let the canvases be painted, but I read that .drawImage() is asynchronous, therefore, in theory, it should let the canvases be painted before jumping to the next line.
Any ideas on how to solve this issue?
Thanks.
After hours of fighting with this I finally came up with a solution based on the "seeked" event. For this to work, the video must be completely loaded:
The code goes like this:
var fps = video_model.getFps(); //screenshot data, comes from another script
var start = shot.getStart();
var end = shot.getEnd();
video.get(0).currentTime = start/fps; //make the video jump to the start
video.on("seeked", function(){ //when the time is seeked, capture screenshot
setTimeout( //the trick is in giving the canvas a little time to be created and painted, 500ms should be enough
function(){
if( video.get(0).currentTime <= end/fps ){
var capture = $(document.createElement("canvas")) //create canvas element on the fly
.attr({
id: video.get(0).currentTime + "sec",
width: video.get(0).videoWidth,
height: video.get(0).videoHeight
})
.appendTo("body");
var ctx = capture.get(0).getContext("2d"); //paint canvas
ctx.drawImage(video.get(0), 0, 0, video.get(0).videoWidth, video.get(0).videoHeight);
if(video.get(0).currentTime + 50/fps > end/fps){
video.off("seeked"); //if last screenshot was captured, unbind
}else{
video.get(0).currentTime += 50/fps; //capture every 50 frames
}
}
}
, 500); //timeout of 500ms
});
This has worked for me in Chrome and Firefox, I've read that the seeked event can be buggy in some version of particular browsers.
Hope this can be useful to anybody. If anyone comes up with a cleaner, better solution, it would be nice to see it.

jQuery changing the size of Video players

James here. I'm working on a project (actually two projects that both require the same code) that deals with posts, except the content is always 100% of the users screen, and uses jquery to divide the width to make the same amount of columns no matter what screen resolution. I'm having a trouble with video posts however. If anyone could help me write (or write, that'd be way more helpful) a script that forces a default 500px video to the width of the posts? The script that I'm using to divide posts is as follows. Any answers at ALL would be helpful. Oh and I'm bumping this because it's almost a week old, and I have still not received a working script.
var container = function(){
var posts = $(document).width() - 40;
var entry = (posts - 200) / 5;
$("#posts").css("width", posts);
$(".entry").css("width", entry);
$("img.photo").css("width", entry - 22);
}
container();
The site I'm doing this on is http://jamestestblog5.tumblr.com
Thank you to anyone who can help with this, it's REALLY bothering me!
Hiya please see demo here : http://jsfiddle.net/ytcAk/
you can read the logic when you click grow button.
fork from old solution. :) (please let me know if this is what you are looking for if not I will remove this post)
Jquery code
function increaseVideoSize() {
var columnWidth = 450; // width of your content column - any
var defaultVideoWidth = 400; // theme tag width - 400,500
var increaseRatio = columnWidth/defaultVideoWidth;
$(".video-post").each(function() {
var iframe = $("iframe", this);
if(iframe.length>0) {
var currHeight = iframe.height();
var newHeight = currHeight * increaseRatio;
iframe.height(newHeight).width(columnWidth);
} else {
var object = $("object", this);
var embed = $("embed", object);
var currHeight = object.attr('height');
var newHeight = currHeight * increaseRatio;
object.width(columnWidth).attr('height', newHeight);
embed.width(columnWidth).attr('height', newHeight);
};
});
};​

Get image dimensions with Javascript before image has fully loaded

I've read about various kinds of ways getting image dimensions once an image has fully loaded, but would it be possible to get the dimensions of any image once it just started to load?
I haven't found much about this by searching (which makes me believe it's not possible), but the fact that a browser (in my case Firefox) shows the dimensions of any image I open up in a new tab right in the title after it just started loading the image gives me hope that there actually is a way and I just missed the right keywords to find it.
You are right that one can get image dimensions before it's fully loaded.
Here's a solution (demo):
var img = document.createElement('img');
img.src = 'some-image.jpg';
var poll = setInterval(function () {
if (img.naturalWidth) {
clearInterval(poll);
console.log(img.naturalWidth, img.naturalHeight);
}
}, 10);
img.onload = function () { console.log('Fully loaded'); }
The following code returns width/height as soon as it's available. For testing change abc123 in image source to any random string to prevent caching.
There is a JSFiddle Demo as well.
<div id="info"></div>
<img id="image" src="https://upload.wikimedia.org/wikipedia/commons/d/da/Island_Archway,_Great_Ocean_Rd,_Victoria,_Australia_-_Nov_08.jpg?abc123">
<script>
getImageSize($('#image'), function(width, height) {
$('#info').text(width + ',' + height);
});
function getImageSize(img, callback) {
var $img = $(img);
var wait = setInterval(function() {
var w = $img[0].naturalWidth,
h = $img[0].naturalHeight;
if (w && h) {
clearInterval(wait);
callback.apply(this, [w, h]);
}
}, 30);
}
</script>
One way is to use the HEAD request, which asks for HTTP Header of the response only. I know in HEAD responses, the size of the body is included. But I don't know if there anything available for size of images.

Categories