How to prevent Canvas to act like an Image on Mobile? - javascript

I have an interactive canvas, where you can click on some buttons. But when I test it on mobile, and try to click on the buttons, it doesnt react immediately. I have to press and wait for the action.
test here: http://choix.me/labor/canvas/car.html
here the javascript code for the upBtn:
this.upBtn.addEventListener("mousedown", upClick.bind(this));
this.upBtn.addEventListener("click", upRelease.bind(this));
function upClick()
{
up = true;
speed = 10;
forward = 1;
}
function upRelease()
{
up = false;
speed = 10;
forward = 0;
}

You are using mousedown and click events, neither of which are events that are naturally supported on a mobile browser. They do work but not the same way they work on the desktop. Tie into the touch events, specifically the touchstart and touchend event
https://developer.mozilla.org/en-US/docs/Web/API/Touch_events
this.upBtn.addEventListener("touchstart", upClick.bind(this));
this.upBtn.addEventListener("touchend", upRelease.bind(this));
function upClick()
{
up = true;
speed = 10;
forward = 1;
}
function upRelease()
{
up = false;
speed = 10;
forward = 0;
}
not tested code, but the gist is there

It had something to do with CreateJS:
This line fixes it:
createjs.Touch.enable(stage);
from:
Touch Events not registering for HTML5 Canvas Authoring using Flash CC

Related

"touchend" event partly not working on iphone mobile browser

On the mobile version of the website I have made, I use a javascript file to handle the tap of a user to open up the side display.
What I wanted to do was if a user presses the side display it should expand, if the touch area is not the side display shrink back to the original state.
It is currently working on samsung browsers, I have also tried on iphones using a chrome and safari browser and it expands when pressed on the side display but only closes when I press the button located on the lower right hand corner. The areas excluding the side navigation and the background do not initiate the div to return to original state.
Here is my javascript code for handling touch events:
document.getElementById('side_box').addEventListener('touchend', function(event){
var touchobj = event.changedTouches[0] // reference first touch point for this event
const vals = document.getElementById('val-display');
const lst_elem = document.getElementsByClassName('list-element');
const arrow = document.getElementById('arrow');
if(screen && screen.width < 480) {
if (event.target === this || event.target === arrow) {
this.style.opacity = "0.7";
this.style.width = "200px";
vals.style.opacity = "0";
arrow.style.display = "none";
for (let i = 0; i < lst_elem.length; i++) {
lst_elem[i].style.visibility = "visible";
}
} else {
this.style.opacity = "0.3";
this.style.width = "50px";
vals.style.opacity = "1";
arrow.style.display = "block";
for (let i = 0; i < lst_elem.length; i++) {
lst_elem[i].style.visibility = "hidden";
}
}
}
event.preventDefault();
}, false);
The if statement is currently working perfect but there is a problem in the else not initiating when pressed anywhere else except the side display.
You can also check the problem by going on andy.serra.us on your mobile.
Thanks for the help in advance.

How do I detect the Keyboard show/hide event occurence on Android browser

I am in a fix. I am not able to identify a way to capture the keyboard show/hide status on a mobile device browser.
Problem :
I have a popup on a form in which a Text Field is present. When the user taps on the text field the keyboard shows up pushing the popup on the form and eventually making the text field invisible.
Is there a way to identify the key board show/hide status???
No, there is no way to reliably know when a keyboard is showing. The one level of control you do have is you can set your app to pan or resize when the keyboard shows up. If you set it to resize, it will recalculate your layout and shrink things so if fits the remaining screen. If you choose pan, it will keep the same size and just slide up the entire app.
you can find out keyboard show/hide inside your application,Try following code inside oncreate method,and pass your parent layout to view.
final View activityRootView = rellayLoginParent;
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener()
{
#Override
public void onGlobalLayout()
{
Rect r = new Rect();
// r will be populated with the coordinates of your view that area still visible.
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
//MyLog.w("height difference is", "" + heightDiff);
if (heightDiff > 100)
{ // if more than 100 pixels, its probably a keyboard...
if(lytAppHeader.getVisibility() == View.VISIBLE)
{
lytAppHeader.setVisibility(View.GONE);
}
}
else
{
if(lytAppHeader.getVisibility() == View.GONE)
{
lytAppHeader.setVisibility(View.VISIBLE);
}
}
}
});
It seems there is no reliable way to do this in the browser. The closest I have come is to listen for focus events and then temporarily listen for resize events. If a resize occurs in the next < 1 second, it's very likely that the keyboard is up.
Apologies for the jQuery...
onDocumentReady = function() {
var $document = $(document);
var $window = $(window);
var initialHeight = window.outerHeight;
var currentHeight = initialHeight;
// Listen to all future text inputs
// If it's a focus, listen for a resize.
$document.on("focus.keyboard", "input[type='text'],textarea", function(event) {
// If there is a resize immediately after, we assume the keyboard is in.
$window.on("resize.keyboard", function() {
$window.off("resize.keyboard");
currentHeight = window.outerHeight;
if (currentHeight < initialHeight) {
window.isKeyboardIn = true;
}
});
// Only listen for half a second.
setTimeout($window.off.bind($window, "resize.keyboard"), 500);
});
// On blur, check whether the screen has returned to normal
$document.on("blur.keyboard", "input[type="text"],textarea", function() {
if (window.isKeyboardIn) {
setTimeout(function() {
currentHeight = window.outerHeight;
if (currentHeight === initialHeight) {
window.isKeyboardIn = false;
}, 500);
}
});
};

How to detect a double-click-drag in Javascript/jQuery

I'd like to detect in a web page when the user selects some text by dragging. However, there's one scenario in Windows which I'm calling a "double-click-drag" (sorry if there's already a better name I don't know) and I can't figure out how to detect it. It goes like this:
press mouse button
quickly release mouse button
quickly press mouse button again
drag with the button held down
This causes the dragging to select whole Words. It's quite a useful technique from the user perspective.
What I'm trying to do is tell the difference between a double-click-drag and a click followed by a separate drag. So when I get to step 2 I will get a click event but I don't want to treat it as a click yet; I want to see if they're about to immediately do step 3.
Presumably Windows detects this on the basis of the timing and how much the mouse has moved between step 2 and 3, but I don't know the parameters it uses so I can't replicate the windows logic. note that even if the mouse doesn't move at all between step 2 and 3, I still get a mousemove event.
I realise that I should be designing interfaces that are touch-friendly and device-neutral, and I have every intention of supporting other devices, but this is an enterprise application aimed at users on windows PCs so I want to optimize this case if I can.
We've done something similar. Our final solution was to create a click handler that suppressed the default response, and then set a global variable to the current date/time. We then set another function to fire in some 200ms or so that would handle the "click" event. That was our base function.
We then modified it to look at the global variable to determine when the last click occured. If it's been less than 200ms (modify based on your needs) we set a flag that would cause the click handler to fizzle and called a double click handler.
You could extend that approach by having your click and double click handlers manually fire the drag functionality.
I don't have access to the aforementioned code right now, but here is an example of that framework being used to track keyboard clicks to determine if a scanner or user has finished typing in a field:
var lastKeyPress = loadTime.getTime();
// This function fires on each keypress while the cursor is in the field. It checks the field value for preceding and trailing asterisks, which
// denote use of a scanner. If these are found it cleans the input and clicks the add button. This function also watches for rapid entry of keyup events, which
// also would denote a scanner, possibly one that does not use asterisks as control characters.
function checkForScanKeypress() {
var iVal = document.getElementById('field_id').value;
var currentTime = new Date()
var temp = currentTime.getTime();
if (temp - lastKeyPress < 80) {
scanCountCheck = scanCountCheck + 1;
} else {
scanCountCheck = 0;
}
lastKeyPress = currentTime.getTime();
}
// The script above tracks how many successive times two keyup events have occurred within 80 milliseconds of one another. The count is reset
// if any keypress occurs more than 80 milliseconds after the last (preventing false positives from manual entry). The script below runs
// every 200 milliseconds and looks to see if more than 3 keystrokes have occurred in such rapid succession. If so, it is assumed that a scanner
// was used for this entry. It then waits until at least 200 milliseconds after the last event and then triggers the next function.
// The 200ms buffer after the last keyup event insures the function is not called before the scanner completes part number entry.
function checkForScan() {
var currentTime = new Date();
var temp = currentTime.getTime();
if (temp - lastKeyPress > 200 && scanCountCheck > 3) {
FiredWhenUserStopsTyping();
scanCountCheck = 0;
}
setTimeout(checkForScan, 200);
}
Here is some code that I just wrote up based upon the above ideas. It's not tested and doesn't contain the actual drag events, but should give you a good starting point:
var lastClick = loadTime.getTime();
function fireOnClickEvent(event) {
event.preventDefault;
var currentTime = new Date()
var temp = currentTime.getTime();
if (temp - lastClick < 80) {
clearTimeout(tf);
doubleClickHandler();
} else {
tf = setTimeout(singleClickHandler, 100);
}
lastClick = currentTime.getTime();
}
function singleClickHandler() {
// Begin normal drag function
}
function doubleClickHandler() {
// Begin alternate drag function
}
A single double-click-drag action involves the following events in sequence:
mousedown -> mouseup -> click -> mousedown -> mousemove
With that in mind, I came up with this simple solution:
let maybeDoubleClickDragging = false;
let maybeDoubleClickDraggingTimeout;
const element = document.querySelector('#container');
element.addEventListener("click", function (e) {
maybeDoubleClickDragging = true;
element.removeEventListener("mousemove", handleMousemove);
});
element.addEventListener("mousedown", (e) => {
element.addEventListener("mousemove", handleMousemove);
if (maybeDoubleClickDragging) {
clearTimeout(maybeDoubleClickDraggingTimeout);
return;
}
});
element.addEventListener("mouseup", (event) => {
maybeDoubleClickDraggingTimeout = setTimeout(() => {
maybeDoubleClickDragging = false;
}, 200);
});
function handleMousemove(e) {
if(maybeDoubleClickDragging) {
element.textContent = 'you are double-click-dragging'
}
}
#container {
width: 300px;
height: 300px;
background: yellow;
}
<div id="container"></div>

Detect whether the intent of a user is to tap or to scroll up/down the page on tactile device

If a user taps (touchstart) outside a popup-div, I want to hide the popup.
But if the user's intent is to scroll/swipe (touchmove), I don't want to hide the popup.
How could the code look like to detect and respond to those two actions (with or without jQuery)?
Here is a basic example of how you could do this:
http://jsfiddle.net/4CrES/2/
The logic behind it involves detecting the initial touch time and saving it to a var
touchTime = new Date();
In the touchend handler subtract this time from the current time to get the difference:
var diff = new Date() - touchTime;
Use an if statement to decide whether the touch duration was short enough to consider it a tap, or long enough to consider it a drag.
if (diff < 100){
//It's a tap
}
else {
//Not a quick tap
}
You could write a more robust implementation by doing a similar difference of the initial touch y position to the final touch y position in the handlers. Another option is to compare the scrollTop of the scrolling area to see if it has been scrolled.
Since click events do not bubble up the DOM on mobile Safari while touch events and custom events do, I recently wrote some code to detect a quick-tap.
It's a quick-tap when
The touch event starts and ends without any movement along the screen
No scrolling occurrs
It all happens in less than 200ms.
If the touch is determined to be a 'quickTap', the TouchManager causes the touched element in the DOM to emit a custom "quickTap" event which then bubbles up the DOM to any other elements that happen to be listening for it. This code defines and creates the touch manager and it will be ready to go immediately
Drawbacks:
Uses jQuery
Only designed with one finger in mind.
also borrowed some code from modernizr. (You can omit that bit if you already include Modernizr.)
Maybe this is overkill, but it's part of a larger codebase I'm working on.
/**
* Click events do not bubble up the DOM on mobile Safari unless the click happens on a link or form input, but other events do bubble up.
* The quick-tap detects the touch-screen equivalent of a click and triggers a custom event on the target of the tap which will bubble up the DOM.
* A touch is considered a click if there is a touch and release without any movement along the screen or any scrolling.
*/
var qt = (function ($) {
/**
* Modernizr 3.0.0pre (Custom Build) | MIT
* Modernizr's touchevent test
*/
var touchSupport = (function() {
var bool,
prefixes = ' -webkit- -moz- -o- -ms- '.split(' ')
if(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
bool = true;
} else {
var query = ['#media (',prefixes.join('touch-enabled),('),'heartz',')','{#modernizr{top:9px;position:absolute}}'].join('');
testStyles(query, function( node ) {
bool = node.offsetTop === 9;
});
}
return bool;
}()),
MobileTapEvent = 'tapEvent';
if(touchSupport) {
/* Create a new qt (constructor)*/
var startTime = null,
startTouch = null,
isActive = false,
scrolled = false;
/* Constructor */
function qt() {
var _qt = this,
context = $(document);
context.on("touchstart", function (evt) {
startTime = evt.timeStamp;
startTouch = evt.originalEvent.touches.item(0);
isActive = true;
scrolled = false;
})
context.on("touchend", function (evt) {
window.ct = evt.originalEvent['changedTouches'];
// Get the distance between the initial touch and the point where the touch stopped.
var duration = evt.timeStamp - startTime,
movement = _qt.getMovement(startTouch, evt.originalEvent['changedTouches'].item(0)),
isTap = !scrolled && movement < 5 && duration < 200;
if (isTap) {
$(evt.target).trigger('quickTap', evt);
}
})
context.on('scroll mousemove touchmove', function (evt) {
if ((evt.type === "scroll" || evt.type === 'mousemove' || evt.type === 'touchmove') && isActive && !scrolled) {
scrolled = true;
}
});
}
/* Calculate the movement during the touch event(s)*/
qt.prototype.getMovement = function (s, e) {
if(!s || !e) return 0;
var dx = e.screenX - s.screenX,
dy = e.screenY - s.screenY;
return Math.sqrt((dx * dx) + (dy * dy));
};
return new qt();
}
}(jQuery));
To use the code you would add it to your page then just listen for the quickTap event.
<script type="text/javascript" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<script type="text/javascript" src="quick-tap.js"></script>
<script type="text/javascript">
$(document).on('quickTap', function(evt, originalEvent) {
console.log('tap event detected on: ', evt.target.nodeName, 'tag');
});
</script>
evt is the quickTap event.
evt.target is the tapped DOM element (not the jQuery object).
originalEvent is the touchend event where qt determines whether it was a tap or not.
You can hide the popup-div on touchend event.
In touchstart event you remember window.scrollY.
In touchend event, if scrollY positions differ the user has scrolled.

How to improve image cross-fade performance?

I want to be able to do a cross fade transition on large images whose width is set to 100% of the screen. I have a working example of what I want to accomplish. However, when I test it out on various browsers and various computers I don't get a buttery-smooth transition everywhere.
See demo on jsFiddle: http://jsfiddle.net/vrD2C/
See on Amazon S3: http://imagefader.s3.amazonaws.com/index.htm
I want to know how to improve the performance. Here's the function that actually does the image swap:
function swapImage(oldImg, newImg) {
newImg.css({
"display": "block",
"z-index": 2,
"opacity": 0
})
.removeClass("shadow")
.animate({ "opacity": 1 }, 500, function () {
if (oldImg) {
oldImg.hide();
}
newImg.addClass("shadow").css("z-index", 1);
});
}
Is using jQuery animate() to change the opacity a bad way to go?
You might want to look into CSS3 Transitions, as the browser might be able to optimize that better than Javascript directly setting the attributes in a loop. This seems to be a pretty good start for it:
http://robertnyman.com/2010/04/27/using-css3-transitions-to-create-rich-effects/
I'm not sure if this will help optimize your performance as I am currently using IE9 on an amped up machine and even if I put the browser into IE7 or 8 document mode, the JavaScript doesn't falter with your current code. However, you might consider making the following optimizations to the code.
Unclutter the contents of the main photo stage by placing all your photos in a hidden container you could give an id of "queue" or something similar, making the DOM do the work of storing and ordering the images you are not currently displaying for you. This will also leave the browser only working with two visible images at any given time, giving it less to consider as far as stacking context, positioning, and so on.
Rewrite the code to use an event trigger and bind the fade-in handling to the event, calling the first image in the queue's event once the current transition is complete. I find this method is more well-behaved for cycling animation than some timeout-managed scripts. An example of how to do this follows:
// Bind a custom event to each image called "transition"
$("#queue img").bind("transition", function() {
$(this)
// Hide the image
.hide()
// Move it to the visible stage
.appendTo("#photos")
// Delay the upcoming animation by the desired value
.delay(2500)
// Slowly fade the image in
.fadeIn("slow", function() {
// Animation callback
$(this)
// Add a shadow class to this image
.addClass("shadow")
// Select the replaced image
.siblings("img")
// Remove its shadow class
.removeClass("shadow")
// Move it to the back of the image queue container
.appendTo("#queue");
// Trigger the transition event on the next image in the queue
$("#queue img:first").trigger("transition");
});
}).first().addClass("shadow").trigger("transition"); // Fire the initial event
Try this working demo in your problem browsers and let me know if the performance is still poor.
I had the same problem too. I just preloaded my images and the transitions became smooth again.
The point is that IE is not W3C compliant, but +1 with ctcherry as using css is the most efficient way for smooth transitions.
Then there are the javascript coded solutions, either using js straight (but need some efforts are needed to comply with W3C Vs browsers), or using libs like JQuery or Mootools.
Here is a good javascript coded example (See demo online) compliant to your needs :
var Fondu = function(classe_img){
this.classe_img = classe_img;
this.courant = 0;
this.coeff = 100;
this.collection = this.getImages();
this.collection[0].style.zIndex = 100;
this.total = this.collection.length - 1;
this.encours = false;
}
Fondu.prototype.getImages = function(){
var tmp = [];
if(document.getElementsByClassName){
tmp = document.getElementsByClassName(this.classe_img);
}
else{
var i=0;
while(document.getElementsByTagName('*')[i]){
if(document.getElementsByTagName('*')[i].className.indexOf(this.classe_img) > -1){
tmp.push(document.getElementsByTagName('*')[i]);
}
i++;
}
}
var j=tmp.length;
while(j--){
if(tmp[j].filters){
tmp[j].style.width = tmp[j].style.width || tmp[j].offsetWidth+'px';
tmp[j].style.filter = 'alpha(opacity=100)';
tmp[j].opaque = tmp[j].filters[0];
this.coeff = 1;
}
else{
tmp[j].opaque = tmp[j].style;
}
}
return tmp;
}
Fondu.prototype.change = function(sens){
if(this.encours){
return false;
}
var prevObj = this.collection[this.courant];
this.encours = true;
if(sens){
this.courant++;
if(this.courant>this.total){
this.courant = 0;
}
}
else{
this.courant--;
if(this.courant<0){
this.courant = this.total;
}
}
var nextObj = this.collection[this.courant];
nextObj.style.zIndex = 50;
var tmpOp = 100;
var that = this;
var timer = setInterval(function(){
if(tmpOp<0){
clearInterval(timer);
timer = null;
prevObj.opaque.opacity = 0;
nextObj.style.zIndex = 100;
prevObj.style.zIndex = 0;
prevObj.opaque.opacity = 100 / that.coeff;
that.encours = false;
}
else{
prevObj.opaque.opacity = tmpOp / that.coeff;
tmpOp -= 5;
}
}, 25);
}

Categories