Not being able to Load Image from a function To HTML Canvas - javascript

I am trying to place the screenshot image into html canvas but not being able to. I have taken screenshot of current tab by using following chrome API. Trying to make a chrome extension.
/* background.js */
let id = 100;
//calling chrome api
chrome.browserAction.onClicked.addListener(() => {
chrome.tabs.captureVisibleTab((screenshotUrl) => {
const viewTabUrl = chrome.extension.getURL('screenshot.html?id=' + id++)
let targetId = null;
//checking if the opened tab id is the same as the target id
chrome.tabs.onUpdated.addListener(function listener(tabId, changedProps) {
if (tabId != targetId || changedProps.status != "complete")
return;
chrome.tabs.onUpdated.removeListener(listener);
const views = chrome.extension.getViews();
for (let i = 0; i < views.length; i++) {
let view = views[i];
if (view.location.href == viewTabUrl) {
view.setScreenshotUrl(screenshotUrl);
return;
}
}
});
//chrome tabs create method
//no listener on create event because the tab’s URL may not be set at the time this event is fired
chrome.tabs.create({ url: viewTabUrl }, (tab) => {
targetId = tab.id;
});
});
});
But not able to call the image from the function
function setScreenshotUrl(url) {
document.getElementById('target').src = url;
};
var cnv = document.getElementById("conv");
cnv.onclick = function() {
var x = document.getElementById("target").src;
document.getElementById("demo").innerHTML = x;
};
window.addEventListener('load', function() {
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var img = document.getElementById("target");
ctx.drawImage(img, 10, 10, 300, 175, 0, 0, 100, 175);
});

The problem was image was not ready at windows on load event, verified by checking the innerHTML of the image, while if I try to do the same thing in onClick event, the image is successfully loaded into canvas.

Related

How to use 'timeupdate' event listener to highlight text from audio?

I'm using the 'timeupdate' event listener to sync a subtitle file with audio.
It is working currently, but I'd like to adjust it to where it is just highlighting the corresponding sentence in a large paragraph instead of deleting the entire span and replacing it with just the current sentence. This is the sort of functionality I am trying to replicate: https://j.hn/lab/html5karaoke/dream.html (see how it only highlights the section that it is currently on).
This is made complicated due to timeupdate constantly checking multiple times a second.
Here is the code:
var audioSync = function (options) {
var audioPlayer = document.getElementById(options.audioPlayer);
var subtitles = document.getElementById(options.subtitlesContainer);
var syncData = [];
var init = (function () {
return fetch(new Request(options.subtitlesFile))
.then((response) => response.text())
.then(createSubtitle);
})();
function createSubtitle(text) {
var rawSubTitle = text;
convertVttToJson(text).then((result) => {
var x = 0;
for (var i = 0; i < result.length; i++) {
if (result[i].part && result[i].part.trim() != '') {
syncData[x] = result[i];
x++;
}
}
});
}
audioPlayer.addEventListener('timeupdate', function (e) {
syncData.forEach(function (element, index, array) {
if (
audioPlayer.currentTime * 1000 >= element.start &&
audioPlayer.currentTime * 1000 <= element.end
) {
while (subtitles.hasChildNodes()) {
subtitles.removeChild(subtitles.firstChild);
}
var el = document.createElement('span');
el.setAttribute('id', 'c_' + index);
el.innerText = syncData[index].part + '\n';
el.style.background = 'yellow';
subtitles.appendChild(el);
}
});
});
};
new audioSync({
audioPlayer: 'audiofile', // the id of the audio tag
subtitlesContainer: 'subtitles', // the id where subtitles should show
subtitlesFile: './sample.vtt', // the path to the vtt file
});

Black images from canvas.toDataURL()

I have created a web application where users can upload images from their filesystem but in few cases I only get black image data on the server side. I have never got this issue in my development environment on lan even with the original images causing the error. This failure occurs overall only sporadic but especially one user is affected. All users should work with the latest firefox. I have read most of the question about this topic and I dont think this is related to a security issue, the jpg/png setting or preserveDrawingBuffer. Because I have never got this problem on lan with a fast PC and the fact that often the last selected images are affected it seems to be a synchronization problem.
To avoid this I work with $.Deferred and Callbacks but may be I missed something. First I load and convert the selected images directly into a list of canvas. The upload is disabled until this is finished. During the upload I just loop through the canvas list and read the data (canvas.toDataURL()) with a $.Deferred and transfer it to the server via ajax.
Get the images to the page:
var file = document.getElementById('fileSelect');
if (file != undefined) {
file.onchange = function (e) {
btnUpload.SetEnabled(false);
loadImgs(e, function () {
btnUpload.SetEnabled(true);
});
};
}
function loadImgs(e, callback) {
for (var t = 0; t < e.target.files.length; t++) {
if ($('.CanvasClass').length <= 20) {
var myTmpImg = new Image();
var myName = e.target.files[t].name;
myTmpImg.src = URL.createObjectURL(e.target.files[t]);
myTmpImg.onload = function () {
var GrenzeX = 1024;
var GrenzeY = 768;
var myFactor = 1;
if (myTmpImg.naturalWidth > GrenzeX || myTmpImg.naturalHeight > GrenzeY) {
if ((GrenzeX / myTmpImg.naturalWidth) < (GrenzeY / myTmpImg.naturalHeight)) {
myFactor = GrenzeX / myTmpImg.naturalWidth;
} else {
myFactor = GrenzeY / myTmpImg.naturalHeight;
}
}
var myCanvas = document.createElement('canvas');
myCanvas.className = "CanvasClass";
myCanvas.height = this.naturalHeight * myFactor;
myCanvas.width = this.naturalWidth * myFactor;
var ctx = myCanvas.getContext("2d");
ctx.drawImage(this, 0, 0, myCanvas.width, myCanvas.height);
$("#myImages").append(myCanvas);
myMetaData.push(new newImgData('', '', myName));
}
}
}
if (callback && typeof callback == "function") {
callback();
}
}
Read and transfer the Data:
$('.CanvasClass').each(function () {
var fd = new FormData();
var base64Data = getBase64Image(this, false);
base64Data.then(function (result) {
//Create Ajax Request
});
});
function getBase64Image(myCanvas, modus) {
var deferred = $.Deferred();
var dataURL = myCanvas.toDataURL("image/jpeg", 0.9);
if (modus === true) {
deferred.resolve(dataURL);
} else {
deferred.resolve(dataURL.replace(/^data:image\/(png|jpeg);base64,/, ""));
}
return deferred.promise();
}
I can also exclude issues during the data transfer because I logged the size of the images on clientside before uploading and compared this to the data at serverside and it was the same.

javascript audio gets lauder each click

I was implementing some audio files today in my Javascript and it works fine. But
I noticed that with each subsequent click the sound gets louder. I alreadio used the "Audio.volume" method but no luck.
My Code:
var AppController = (function () {
var correctItem, secondItem, thirdItem, selectedItems, selectedItemsShuffled;
var rotateImage = function() {
var i = 0;
var randomNumbers = createThreeRandomNumbers();
var interval = window.setInterval(function () {
i++;
document.getElementById("imageToRotate").src= "img/" + items.images[i].imageLink;
if (i === items.images.length -1) {
i = -1;
}
}, 125);
var clearData = function () {
correctItem = "";
secondItem = "";
thirdItem = "";
selectedItems = "";
selectedItemsShuffled = "";
removeNodeChildren("button-1");
removeNodeChildren("button-2");
removeNodeChildren("button-3");
};
var removeNodeChildren = function (obj) {
var myNode = document.getElementById(obj);
while (myNode.firstChild) {
myNode.removeChild(myNode.firstChild);
}
};
var resetGame = function () {
clearData();
document.getElementById("buttonWrapper").classList.remove("show");
rotateImage();
}
document.getElementById("imageToRotate").addEventListener("click", function (e) {
// select 3 items
correctItem = getSelectedItemDetails(e);
secondItem = getRandomItem(correctItem);
thirdItem = getSecondRandomItem(correctItem, secondItem);
selectedItems = [correctItem, secondItem, thirdItem];
selectedItemsShuffled = shuffleArray(selectedItems);
// create the numbers 1 to 3 randomly
var order = createThreeRandomNumbers();
// remove the rotating effect
clearInterval(interval);
// set the images to the buttons in a random order.
setItemsToButtons(order, selectedItemsShuffled);
//show the buttons
showButtons();
// Create an event which triggers when a button is clicked.
document.getElementById("buttonWrapper").addEventListener("click", function(e) {
var valueOfButtonPressed = e.srcElement.innerText.toLowerCase();
var clickedButton = e.srcElement.id;
if (valueOfButtonPressed === correctItem) {
document.getElementById(clickedButton).innerHTML = e.srcElement.innerText.toLowerCase() + '<span class="icon"><i class="fa fa-check green" aria-hidden="true"></i></span>';
if (!audio) {
var audio = new Audio("audio/correct.mp3");
}
audio.play();
audio.volume = 0.5;
setTimeout(resetGame, 5000);
} else {
document.getElementById(clickedButton).innerHTML = e.srcElement.innerText.toLowerCase() + '<span class="icon"><i class="fa fa-times red" aria-hidden="true"></i></span>';
if (!audio) {
var audio = new Audio("audio/wrong.mp3");
}
audio.play();
audio.volume = 0.5;
setTimeout(resetGame, 5000);
}
});
});
};
// 1: hide the buttons
hidebuttons();
// 2: Replace the title
replaceTitle("Animals");
// 3: set up image rotation until it's clicked.
rotateImage();
})();
Any help will be much appreciated. Cheers!
I'm fairly certain the reason is that you're making a new Audio object with every single click.
Instead of doing this with every click:
var audio = new Audio("audio/correct.mp3");
do a check to see if audio already exists. if it does, then simply do audio.play(). If it does not, THEN you make a new audio object.

Is it possible to cache canvas pages?

I am using the code in http://jsfiddle.net/epistemex/LUNaJ/
PDFJS.disableWorker = true; // due to CORS
var canvas = document.createElement('canvas'), // single off-screen canvas
ctx = canvas.getContext('2d'), // to render to
pages = [],
currentPage = 1,
url = 'http://www.corsproxy.com/www.ohio.edu/technology/training/upload/Java-Script-Reference-Guide.pdf';
PDFJS.getDocument(url).then(function (pdf) {
PROGRESS.max = pdf.numPages; // just for demo
PROGRESS.value = 1; // just for demo
// init parsing of first page
if (currentPage <= pdf.numPages) getPage();
// main entry point/function for loop
function getPage() {
// when promise is returned do as usual
pdf.getPage(currentPage).then(function(page) {
var scale = 1.5;
var viewport = page.getViewport(scale);
canvas.height = viewport.height;
canvas.width = viewport.width;
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
// now, tap into the returned promise from render:
page.render(renderContext).then(function() {
// store compressed image data in array
pages.push(canvas.toDataURL());
if (currentPage < pdf.numPages) {
currentPage++;
PROGRESS.value = currentPage; // just for demo
getPage(); // get next page
}
else {
done(); // call done() when all pages are parsed
}
});
});
}
});
function done() {
// NOTE: Just for demo - correct order is not guaranteed here
// as the drawPage is async. use same method as above to make
// sure the order is correct (not for-loop, but use the callback
// to get next page). To present a single page it won't be
// a problem though... (just use drawPage() directly)
for(var i = 0; i < pages.length; i++) {
drawPage(i, addPage);
}
}
function addPage(img) {
img.style.width = '100px';
img.style.height = '120px';
document.body.appendChild(img);
}
function drawPage(index, callback) {
var img = new Image;
img.onload = function() {
ctx.drawImage(this, 0, 0, ctx.canvas.width, ctx.canvas.height);
callback(this); // invoke callback when we're done
}
img.src = pages[index]; // start loading the data-uri as source
}
to render pdf pages to canvas. The problem with this is that it takes along time if the pdf file has large number of files. Is it possible to cache/save these generated files in the users computer/bowser so that if he runs the code a secondary time, he doesn't have to generate them again and instead they can be displayed immediately.
No, dataURI are not "cached" by browser since there is no download involved.
What you can do however, is to store all your pages into a globally accessible array, and check if you already have it before you call PDFJS.getDocument(url) :
PDFJS.disableWorker = true; // due to CORS
var canvas = document.createElement('canvas'), // single off-screen canvas
ctx = canvas.getContext('2d'), // to render to
docs = {}, // an object that will store our pdf documents
urls = ["https://www.ohio.edu/technology/training/upload/html-tag-reference-guide.pdf", "https://www.ohio.edu/technology/training/upload/Java-Script-Reference-Guide.pdf"];
btn0.onclick = getDoc;
btn1.onclick = getDoc;
function getDoc() {
// get the doc's url
var url = urls[+this.id.split('btn')[1]];
// clear the result div
result.innerHTML = '';
// we already have it
if (docs[url]) {
// simply call the callback
done(docs[url]);
}
else {
// create our array for this document
docs[url] = [];
// download and parse the doc
PDFJS.getDocument(url).then(function(pdf) {
PROGRESS.max = pdf.numPages; // just for demo
PROGRESS.value = 1; // just for demo
var currentPage = 1;
// init parsing of first page
if (currentPage <= pdf.numPages) getPage();
// main entry point/function for loop
function getPage() {
// when promise is returned do as usual
pdf.getPage(currentPage).then(function(page) {
var scale = 1.5;
var viewport = page.getViewport(scale);
canvas.height = viewport.height;
canvas.width = viewport.width;
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
// now, tap into the returned promise from render:
page.render(renderContext).then(function() {
// store compressed image data in array
docs[url].push(canvas.toDataURL());
if (currentPage < pdf.numPages) {
currentPage++;
PROGRESS.value = currentPage; // just for demo
getPage(); // get next page
} else {
done(docs[url]); // call done() when all pages are parsed
}
});
});
}
});
}
}
function done(pages) {
for (var i = 0; i < pages.length; i++) {
drawPage(pages[i], addPage);
}
}
function addPage(img) {
img.style.width = '100px';
img.style.height = '120px';
result.appendChild(img);
}
function drawPage(dataURI, callback) {
var img = new Image;
img.onload = function() {
ctx.drawImage(this, 0, 0, ctx.canvas.width, ctx.canvas.height);
callback(this); // invoke callback when we're done
}
img.src = dataURI; // start loading the data-uri as source
}
#PROGRESS {
width: 100%
}
<script src="https://rawgit.com/mozilla/pdf.js/gh-pages/build/pdf.js"></script>
<button id="btn0">1st Doc</button>
<button id="btn1">2nd Doc</button>
<progress id="PROGRESS" value=0></progress>
<div id="result"></div>

Changes to Javascript created element doesn't reflect to DOM

I have a class, that is supposed to display grey overlay over page and display text and loading gif. Code looks like this:
function LoadingIcon(imgSrc) {
var elem_loader = document.createElement('div'),
elem_messageSpan = document.createElement('span'),
loaderVisible = false;
elem_loader.style.position = 'absolute';
elem_loader.style.left = '0';
elem_loader.style.top = '0';
elem_loader.style.width = '100%';
elem_loader.style.height = '100%';
elem_loader.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
elem_loader.style.textAlign = 'center';
elem_loader.appendChild(elem_messageSpan);
elem_loader.innerHTML += '<br><img src="' + imgSrc + '">';
elem_messageSpan.style.backgroundColor = '#f00';
this.start = function () {
document.body.appendChild(elem_loader);
loaderVisible = true;
};
this.stop = function() {
if (loaderVisible) {
document.body.removeChild(elem_loader);
loaderVisible = false;
}
};
this.setText = function(text) {
elem_messageSpan.innerHTML = text;
};
this.getElems = function() {
return [elem_loader, elem_messageSpan];
};
}
Problem is, when I use setText method, it sets innerHTML of elem_messageSpan, but it doesn't reflect to the span, that was appended to elem_loader. You can use getElems method to find out what both elements contains.
Where is the problem? Because I don't see single reason why this shouldn't work.
EDIT:
I call it like this:
var xml = new CwgParser('cwg.geog.cz/cwg.xml'),
loader = new LoadingIcon('./ajax-loader.gif');
xml.ondataparse = function() {
loader.stop();
document.getElementById('cover').style.display = 'block';
};
loader.setText('Loading CWG list...');
loader.start();
xml.loadXML();
xml.loadXML() is function that usually takes 3 - 8 seconds to execute (based on download speed of client), thats why I'm displaying loading icon.

Categories

Resources