Invisible/hidden canvas does not show up in Chrome - javascript

A canvas styled with visibility: hidden; is supposed to appear window-sized after one second with this code, right?
var canvas = document.getElementById("myCanvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// draw on canvas.getContext("2d") ...
window.setTimeout(function() {
canvas.style.visibility = "visible";
}, 1000);
It works with IE11, but not with Chrome32 (running on Windows7).
(Phew, I never though I would find something that works with IE but not with Chrome!)
I tried using display: none; and canvas.style.display= "block"; to hide and show the canvas and got the same behavior.
Here is a test: http://jsfiddle.net/CX49R/
Oddly, in jsfiddle with Chrome, the canvas (and its drawn content) appears after you move the mouse over the document area (after the function triggered by setTimeout is called, of course). But it never appears if the same HTML+CSS+JS code runs in a single Chrome tab/window (unless you open "Development tools" through "Inspect element", or click on the document area).
What am I missing here? I tried to reduce the code to the minimum of my original scenario.

It seems like it's somehow related to the canvas element. Creating a hidden parent div and showing that instead of the canvas element seems to solve the issue:
http://jsfiddle.net/CX49R/2/
<div id="wrapper">
<canvas id="myCanvas">Your browser does not support canvas.</canvas>
</div>

I don't have enough reputation to comment, so I'll give you an answer instead. The issue is not the setTimeout, the issue is with canvas.style.visibility = "visible";. I set up an alert in your timeout and it fired after a second, no problem. My only guess is that canvas.style.visibility is waiting on a mouse event to trigger itself.

Related

Hiding an embedded aframe scene

I want to programmatically hide and show my embedded aframe scene. The scene is hidden when the website is loaded, however I only get it to work with a delay like this:
window.onload = function () {
setTimeout(function() { document.getElementById("scene-page").setAttribute("hidden", ""); }, 500);
}
When I don't add this delay, the scene remains hidden, even after setting it to 'visible' again. Specifically, the size of the canvas seems to be resized to 0px:
<canvas class="a-canvas a-grab-cursor" data-aframe-canvas="true" width="0" height="0" style="width: 0px; height: 0px;"></canvas>
The only way to make it visible then is to resize the browser window, which then seems to resize the canvas.
Is there a better way to hide the scene without the delay?
From what i see (posts like here, or here), if you set display: none before the entire canvas loads, the canvas / renderer / camera properties won't set up properly.
I tried waiting for the a-scene's loaded/renderstart event, window.onload, and the internal entities, still it seems to mess up the canvas settings.
The reason why it's working when you resize the window is because the scene has a listener for resize which updates all of these accordingly with the canvas width / height. I'm not sure if its possible to call the resize manually.
Check it out here -> just search for this.resize, there are 3 hits, one in the a-scene.
It seems easier and safer to place a blank <div> if front of the a-frame canvas with a fixed position, which will be switched from display: none to block. live fiddle here !
Once the scene is up and running, You can toggle its display all you want though! live fiddle here !

MouseUp and MouseDown Events in Google Chrome

I have a simple html5/canvas and js-setup:
HTML
<canvas id="canvas" width="500" height="500" ></canvas>
JS
var canvas = $("#canvas"),
c = canvas[0].getContext("2d");
c.fillStyle = "gray";
c.fillRect(0, 0, 500, 500);
$(document).mousedown(function() {
console.log('down');
});
$(document).mouseup(function() {
console.log('up');
});
What buffles me is that I am able to log events with google chrome like so
down
up
down
up
down
down
down
down
up
down
down
down
meaning, I get consecutive down events. I think it is caused by the canvas element, because I am not able to reproduce this behavior without it.
Here is a fiddle: http://jsfiddle.net/SunnyRed/zcmaT/
Update
I think it only happens, when the user clicks and drags, which might be the case in my scenario. Here is a simple clicking demo. I can't reproduce it in Firefox and Opera.
The only way that I could replicate this is in your demo was that I either:
started a click outside the page (in another iframe from JS fiddle) and then did the mouse up over the canvas. Or
start the click on the canvas and then release it outside the bounds of the page (such as over the page padding).
I believe this is the correct behaviour.

context drawImage / canvas toDataURL lagging significantly on opera mobile

I have a bit of Javascript that runs on an Android tablet, running Opera Mobile 12. The tablet is attached to the wall in an office, so it's used by everyone who works in this office, as a timekeeping system (ie. they use it to clock in/clock out of work). This javascript takes a photo of the user when a certain event is raised, then converts this photo to a data URL so it can be sent to a server. This all runs in the background, though - the video and canvas elements are both set to display:none;.
Here's the code that handles the webcam interaction:
var Webcam = function() {
var video = document.getElementById('video');
var stream_webcam = function(stream) {
video.addEventListener('loadedmetadata', function(){
video.play();
}, false);
video.src = window.URL.createObjectURL(stream);
}
// start recording
if (navigator.getUserMedia)
navigator.getUserMedia({video: true}, stream_webcam);
else
_error("No navigator.getUserMedia support detected!");
var w = {};
w.snap = function(callback) {
var canvas = document.getElementById('canvas');
canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
// delay is incurred in writing to canvas on opera mobile
setTimeout(function() {
callback(canvas.toDataURL());
}, 1);
};
return w;
}
w.snap(callback) gets called when the user presses a certain button. The problem is that there is a significant lag between the call to drawImage, and an image actually being drawn to the canvas. Here's the problem that occurs:
User 1 users the tablet, presses the button, has their photo "snapped" and sent to the server. The server receives a blank, transparent, image. (It's not the same issue as html5 canvas toDataURL returns blank image - the data being sent to the server is substantially less for the first image, because it's blank)
A few minutes later, user 2 uses the tablet and presses the button, and has their photo "snapped". The server receives the photo of user 1.
Later, user 3 does the same thing, and the server gets a photo of user 2.
So I'm guessing the call to toDataURL() is returning old data because drawImage() doesn't . (I read somewhere that it's async, but I can't confirm and can't find any way of attaching an event to when it finishes drawing.)
I've tried:
changing the timeout in snap() to a variety of values. The highest I tried was 2000, but the same issue occurred. (If anyone suggests higher numbers, I'm happy to try them, but I don't want to go much higher because if I go too high, there's potential for user 3 to have their photo taken before I've even processed user 2's photo, which means I might lose it entirely!)
having the canvas draw something when the page first loads, but that didn't fix it - when user 1 was photographed, the server received whatever photo was taken when the page loaded, instead of the photo of user 1.
"getting" the canvas element again, using getElementById, before calling toDataURL.
A few more notes:
This works fine on my computer (macbook air) in Chrome and Opera.
I can't reliably replicate it using a debugger (linking to the tablet using Opera Dragonfly).
AFAIK Opera Mobile is the only Android browser that supports getUserMedia and can thus take photos.
If I remove the display:none; from the video and canvas, it works correctly (on the tablet). The issue only occurs when the video and canvas have display:none; set.
Because the page is a single page app with no scrolling or zooming required, a possible workaround is to move the video and canvas below the page fold, then disable scrolling with javascript (ie. scroll to the top whenever the user scrolls).
But I would rather a proper fix than a workaround, if possible!
I don't have a very good solution but I would suggest not handling an invisible canvas through the dom by setting it invisible.
Instead of
var canvas = document.getElementById('canvas');
Try
var canvas = document.createElement('canvas');
canvas.width = video.width;
canvas.height = video.height;
canvas.getContext('2d').drawImage(video, 0, 0)
This way it's separate from the DOM.
Also Why not use
canvas.toDataURL("image/jpg")?
EDIT: Also, if you're designing it, have you tried the other browsers out there? Whats restricting you to Opera over the other browsers available or using phonegap?
EDIT2: Thinking about it, Canvas also has two other options for getting that photo into place that you would want to look in two. Those two being:
var imgData = canvas.getContext('2d').getImageData(0,0,canvas.width,canvas.height);
-or-
canvas.getContext('2d').putImageData(imgData,0,0);
These two ignore any scaling you've done to the context but I've found that they are much more direct and often faster. They are a solid alternative to toDataURL and drawImage but the image data you put and get from these are encoded as an array in the form:
[r1, b1, g1, a1, r2, b2, g2, a2....]
you can find documentation for them here:
http://www.w3schools.com/tags/canvas_putimagedata.asp
http://www.w3schools.com/tags/canvas_getimagedata.asp
Some version of Android have problems with toDataUrl(); Maybe this is the solution?
http://code.google.com/p/todataurl-png-js/wiki/FirstSteps
The script: http://todataurl-png-js.googlecode.com/svn/trunk/todataurl.js
Paste this in your head:
<script src="todataurl.js"></script>

JS Drag and Drop to target without knowing its position

I am creating a simple app which allows for the dragging of multiple img's to a target div on a page. When the img is dragged and release on the target div the img's properties are noted.
Unfortunately my web page is built on percentages and I am wondering if there is anyway way this can be done without knowing the top, left etc positions of the target area. In other words, without working with absolutes.
I've gotten the dragging part working fine it's just simulating the detection which I am finding tricky.
I think I've gotten pretty close using the mouseover event on the target div but of course the dragging img blocks the event firing. When I release my image it resets to its normal position and gives a brief time for the mouseover to seem suitable.
However in tests it would seem that this time gap doesn't give the mouseover enough time to fire (if that is even possible). I believe this is the case because I put an alert right after the dragged image stops blocking the target div and the img's properties are succesfully printed to my console log.
img.style.position = "relative";
img.style.left = "0px";
img.style.top = "0px";
var startDrag = function(e){
initialMouseX = e.clientX;
initialMouseY = e.clientY;
initialElementX = initialElementY = "0px";
img.style.zIndex = 1000;
document.addEventListener("mouseup", removeDrag, true);
document.addEventListener("mousemove",movement,true);
document.getElementById("drag-area").addEventListener("mouseover",
readPeriod,true); //mouseover event starts when the user presses the img
};
var removeDrag = function(e){
document.removeEventListener("mousemove",movement,true);
img.style.left="0px";
img.style.top="0px";
img.style.zIndex = 0;
//an alert at this point gives the mouseup time to fire
img.removeEventListener("mouseup", removeDrag, true);
//mouseover ends after the img has stopped blocking the mouse
};
var movement = function(e){
/*Code which moves the dragging img by style.left/top*/
};
var readPeriod = function(e){
console.log(img.name); //Point of simulated on target div
};
img.addEventListener("mousedown",startDrag,true);
So in summary I am trying to simulate the image dropping onto the target since its impossible to deduce the target div by its location on screen.
(Unfortunately I am unable to use a js library to aid me in this situation.)
You should probably use jQuery UI's Droppable. Things would be much simpler and you won't need to solved problems like this.
People invested many hours in making this work correctly. You shouldn't waste your time again and solve all these bugs again.
On top of that, you would be able to mark the drop target by ID and won't need to deal with coordinates

IE 8 & 9 not resizing container when width set in image.load

To see the issue go to this page and click the next text. The small, medium fullsize text will be off to the left (in IE8 or IE9, works fine in Chrome, FireFox) and will not be aligned with the left edge of the image.
Code (http://moments.qa.chucksoft.com/Scripts/mvc/views/show/Showview.js) Line 154:
ShowView.prototype.setImageSrc = function (imagesrc) {
var image = $('img#image');
image.attr('src', imagesrc);
image.load(function () {
var container = $('div#imagecontainer');
container.fadeIn(200);
var resizer = new Resizer();
resizer.resize(this.width, this.height, maxWidth, maxHeight);
var image = $(this);
var imageWidth = (resizer.width + 23);
container.width(imageWidth);
});
image.attr('src', imagesrc);
};
From what I can tell the container.width(imageWidth); is not rendered in IE8 or IE9. If the page is refreshed then it works. Using the built in developer tool shows the correct value in the html but if you use selector tool in the developer tool outline does not reflect the changed width value.
I've tried using vanilla javascript. That did not work. I've tried setting the image source twice. That did not work either.
I'm at a loss on how to fix this issue. Any insights would be much appreciated.
Update
To clarify, the issue arises when you transition from a landscape image to a portrait image, otherwise everything works as expected.
... ?
I have yet to figure this issue out. I have taken a different direction.

Categories