I'm working on something which is more or less a drawing app. I need to support Microsoft Edge and there's the problem. As soon as you want to catch the touchmove or pointermove event, Edge tries to scroll (even if it's not possible). Here's an example. Just try to draw something there (touch/pen), mouse works perfectly even in Edge. In every other browser (except maybe IE) it works very well.
I have no idea why IE is ignoring the preventDefault() function. Has someone an idea?
Sample: https://jsfiddle.net/ryqagkeb/
Actually doesn't work with my Surface Pro & Surface Pen on Chrome and Edge.
canvas = document.getElementsByTagName('canvas')[0];
ctx = canvas.getContext('2d');
start = 0;
canvas.onpointerdown = function(evt) {
start = 1;
}
canvas.onpointermove = function(evt) {
if(start) {
ctx.fillRect(evt.clientX, evt.clientY, 5,5);
}
}
canvas.onpointerup = function(evt) {
start = 0;
}
So I think I've found the solution myself and posting it now because I think it's pretty helpful. It seems the CSS property "touch-action" can solve the problem.
Setting:
canvas {
touch-action: none;
}
It seems like it fixes the problem.
Related
I have a website which should show an image when the browser is in portrait mode, and hide it again when in landscape. I have tested this feature with chrome responsive mode and there it acts fine, but when trying to run the site on iphone or ipad the feature acts up. Code looks like this
let mql = window.matchMedia("(orientation: portrait)")
window.onorientationchange = rotate;
function rotate() {
if(mql.matches) {
document.querySelector("#rotation").classList.remove("hide");
document.querySelector("#game").classList.add("hide");
} else {
document.querySelector("#rotation").classList.add("hide");
document.querySelector("#game").classList.remove("hide");
}
}
The way it acts on iphone and ipad is that #rotation shows when in landscape and hides when in portrait, which is basically the total opposite of the code, as I read it.
Is anyone familiar with this issue, and anyone know of a fix for it?
thanks in advance
I think instead calling that function window.onorientationchange = rotate;
you should do following
let mql = window.matchMedia("(orientation: portrait)")
function rotate(event) {
if(event.matches) {
document.querySelector("#rotation").classList.remove("hide");
document.querySelector("#game").classList.add("hide");
} else {
document.querySelector("#rotation").classList.add("hide");
document.querySelector("#game").classList.remove("hide");
}
}
// this addListner is going to deprecate soon
mql.addListner(rotate);
// you can use below if you don't want to support <= iOS 13 browsers
// Ref: https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList
mql.addEventListner("change", rotate);
Tested in Safari and Chrome - the same result, so I think it's iOS issue.
This happens only if there's an input inside the modal and I tap that input. In the same moment, that input gets focus and native iOS keyboard become visible.
The page below modal in the same moment is automatically scrolled to 50% of its height. This behaviour is totally unwanted and I have no clue how to prevent this default iOS "feature".
Demo:
UPDATE: the fix commit: limonte/sweetalert2/commit/4a2d36b
We are facing a similar issue at work, and I stumbled upon this question with your (excellent) demo page.
As you mentioned, the offset is always ~50% of the height of the page, which happens regardless of where your initial offset is.
In the past, when I observed a similar "jumping" with earlier iOS versions (albeit, much less dramatic jumping), I have usually worked around this by applying position: fixed (or relative) to the body (this allows overflow: hidden to properly work).
However, this has the unattended consequence of jumping the user back to the top of the page, if they've scrolled down.
So, if you're open to addressing this issue with some JavaScript, here's a fix/hack I've thrown together:
// Tapping into swal events
onOpen: function () {
var offset = document.body.scrollTop;
document.body.style.top = (offset * -1) + 'px';
document.body.classList.add('modal--opened');
},
onClose: function () {
var offset = parseInt(document.body.style.top, 10);
document.body.classList.remove('modal--opened');
document.body.scrollTop = (offset * -1);
}
And what the CSS looks like:
.modal--opened {
position: fixed;
left: 0;
right: 0;
}
Here's a fork of your demo page, to illustrate: https://jpattishall.github.io/sweetalert2/ios-bug.html
And for those who are looking for a more general fix, you could do something like the following when opening/closing a modal:
function toggleModal() {
var offset;
if (document.body.classList.contains('modal--opened')) {
offset = parseInt(document.body.style.top, 10);
document.body.classList.remove('modal--opened');
document.body.scrollTop = (offset * -1);
} else {
offset = document.body.scrollTop;
document.body.style.top = (offset * -1) + 'px';
document.body.classList.add('modal--opened');
}
}
Edit: One thing to note is that we didn't apply the fix to all devices/platforms blindly, just iOS Safari. I noticed in your other question that you weren't a fan of overflow: hidden due to the shifting of the page when the scrollbar appears/disappears (which I totally agree with). I would suggest just applying the JS to iOS devices only.
On iOS I had trouble with scroll events caused by setTimeout and setInterval (position the modal causes scrolling?). I found a solution somewhere with the following code.
Function.prototype.bind = function(parent) {
var f = this;
var args = [];
for (var a = 1; a < arguments.length; a++) {
args[args.length] = arguments[a];
}
var temp = function() {
return f.apply(parent, args);
}
return(temp);
}
setTimeout(function() {
// your animation stuff here
}.bind(this), 100);
One thing I can think of here is to probably add the Fast Click library to your code. Some iOS and Android timeout issues like the 300ms delay are handled by Fast Click. It's worth a shot
Something else to check for is that the font size of the input field is above the minimum that will trigger a zoom on the input. I'm working from memory here, but I had a similar problem. Setting the input font size to 16px (again from memory) or more prevented iOS Safari from trying to zoom the input field and thus messing with the page scroll.
Resolved!!!!! Just please add those codes to your script
https://gist.github.com/kiding/72721a0553fa93198ae2bb6eefaa3299
//reach out to that input field (When ever u r gonna click tofocus)
let inputField = document.getElementById("input_focused")
/*
* Method 1: Briefly change the opacity.
* Element might "blink" on focus in some scenarios.
*/
inputField.addEventListener("focus", () => {
methodOne.style.opacity = 0;
setTimeout(() => methodOne.style.opacity = 1);
});
<section id="modal">
<input id="input_focused">
</section>
I am using the sketch.js plugin for drawing on HTML5 canvas. While it works fine on desktop computers it seems to have some issues on mobile browsers.
The problem is that if I draw 2 different shapes, the canvas will reset to blank as soon as I touch it.
Just to be completly clear I will make and example: drawing the number '12' will first draw '1' and then when I start drawing '2' the canvas will clear and only keep the number '2'...
<!-- CANVAS -->
<canvas id="canvas1" style="width:100%; background:white; height:150px;"></canvas>
<script type="text/javascript">
$(function () {
$('#canvas1').sketch();
});
</script>
This is it. I am wondering if there is some workaround to keep the history of the various drawings. I am open to any suggestion or to know if you have found a similar problem.
To actually fix this bug, you need to change a few different lines of code. The plugin is checking during each event if the user is using a desktop (mousedown, mousemove, mouseleave, mouseup) or a mobile (touchstart, touchend, touchcancel). To determine the mouse/finger position to draw the next point, jQuery's e.pageX is being called. This is directly defined in mobile, so the plugin is checking for mobile and if mobile is detected, saving the user's finger position defined by e.originalEvent.targetTouches[0].pageX. The problem comes from touchend or touchcancel because e.originalEvent.targetTouches[0] is not defined for these two events, so e.originalEvent.targetTouches[0].pageX returns an error and the entire canvas is redrawn. To fix this, you need to edit 2 lines of code around line 100/101.
Replace
e.pageX = e.originalEvent.targetTouches[0].pageX;
e.pageY = e.originalEvent.targetTouches[0].pageY;
With
if (e.originalEvent.targetTouches[0] !== undefined && e.originalEvent.targetTouches[0].pageX!==undefined){
e.pageX = e.originalEvent.targetTouches[0].pageX;
}
if (e.originalEvent.targetTouches[0] !== undefined &&e.originalEvent.targetTouches[0].pageY){
e.pageY = e.originalEvent.targetTouches[0].pageY;
}
See the answer below for more details.
Sketch.js pageX undefined error
See the workaround posted by leonth here: https://github.com/intridea/sketch.js/issues/1
I made a jsFiddle with the workaround and it now works for me on mobile devices:http://jsfiddle.net/ezanker/4hfkb/5/
switch (e.type) {
case 'mousedown':
case 'touchstart':
if (this.painting) { //add
this.stopPainting(); //add
} //add
this.startPainting();
break;
...
I'm working on a small jQuery plugin that mimics the jQuery UI draggable/droppable behavior with native HTML5 drag and drop events.
A feature I'd want to add is the ability to specify the node which will serve as the drag proxy.
I did a bit of research, and according to MDN, to do this requires the use of setDragImage(), passing an image or an element.
What is the support for setDragImage in different browsers?
I've noticed there's a plugin named jquery.event.drag which takes a different than I expected to this problem.
Would this feature require me to make some kind of workaround like the above plugin, or should this be possible out-of-the-box in most or all browsers using setDragImage?
EDIT
After playing around a bit with this functionality, it would seem that this function is quite limited.
Besides having no support in quite a few browsers, using an arbitrary DOM element as the helper requires it to be in the DOM tree and visible, and so you have the element itself on the body, and a copy of it as the handler. This is mostly unwanted for this sort of plugin.
Further more, rendering is also problematic even when the right terms are met. When trying to create a helper from <span>TEST</span>, the helper itself only showed a white rectangle with the dimensions of the span.
Are these issues that were to be expected according to the specs? Could they be fixed in code or would they require a workaround?
setDragImage is IMO a vital feature for any non trivial drag and drop use case. e.g consider a multi select list where a drag needs to include all the selected items and not just the row that the drag gesture was made on. it's odd that the thing you want to set needs to be visible in the DOM but even worse is that this method is not implemented at all in IE as of version 11.
However, with a bit of effort I was able to get it working reasonably satisfactorily. The custom drag image node can be removed from the DOM in a timeout 0 function. so add it to the DOM in dragstart then use it in set drag image and then remove it. This works perfectly in FF but in chrome the drag image node will flicker before the timeout fires. One way to prevent this is to position it such that the actual browser generated drag image will appear in exactly the same place, this is not as bad as it sounds since you can control the position of the custom drag image relative to the cursor.
I was playing with this recently and was able to get it working on IE as well. the trick there is to get IE to drag the custom drag image node and not the node that dragstart fired on. you can do this with the IE specific dragDrop() method.
The final thing to be aware of is that on windows there is a 300px limit on the width of the custom drag image node this applies to all draggables not just the custom node actually. so the browser applies a heavy radial gradient if the drag image is too big.
http://jsfiddle.net/stevendwood/akScu/21/
$(function() {
(function($) {
var isIE = (typeof document.createElement("span").dragDrop === "function");
$.fn.customDragImage = function(options) {
var offsetX = options.offsetX || 0,
offsetY = options.offsetY || 0;
var createDragImage = function($node, x, y) {
var $img = $(options.createDragImage($node));
$img.css({
"top": Math.max(0, y-offsetY)+"px",
"left": Math.max(0, x-offsetX)+"px",
"position": "absolute",
"pointerEvents": "none"
}).appendTo(document.body);
setTimeout(function() {
$img.remove();
});
return $img[0];
};
if (isIE) {
$(this).on("mousedown", function(e) {
var originalEvent = e.originalEvent,
node = createDragImage($(this), originalEvent.pageX, originalEvent.pageY);
node.dragDrop();
});
}
$(this).on("dragstart", function(e) {
var originalEvent = e.originalEvent,
dt = originalEvent.dataTransfer;
if (typeof dt.setDragImage === "function") {
node = createDragImage($(this), originalEvent.pageX, originalEvent.pageY);
dt.setDragImage(node, offsetX, offsetY);
}
});
return this;
};
}) (jQuery);
$("[draggable='true']").customDragImage({
offsetX: 50,
offsetY: 50,
createDragImage: function($node) {
return $node.clone().html("I'm a custom DOM node/drag image").css("backgroundColor", "orange");
}
}).on("dragstart", function(e) {
e.originalEvent.dataTransfer.setData("Text", "Foo");
});
});
I'm trying to write a simple html based drawing application (standalone simplified code attached bellow). I've tested this on the following devices:
iPad 1 and 2: Works great
ASUS T101 running Windows: Works great
Samsung Galaxy Tab: Extremely slow and patchy -- unusable.
Lenovo IdeaPad K1: Extremely slow and patchy -- unusable.
Asus Transformer Prime: Noticeable lag compare with the iPad -- close to usable.
The Asus tablet is running ICS, the other android tablets are running 3.1 and 3.2. I tested using the stock Android browser. I also tried the Android Chrome Beta, but that was even worse.
Here's a video which demonstrates the issue: http://www.youtube.com/watch?v=Wlh94FBNVEQ
My questions is why are the Android tablets so slow? Am I doing something wrong or is it an inherit problem with Android OS or browser, or is there anything I can do about it in my code?
multi.html:
<html>
<body>
<style media="screen">
canvas { border: 1px solid #CCC; }
</style>
<canvas style="" id="draw" height="450" width="922"></canvas>
<script class="jsbin" src="jquery.js"></script>
<script src="multi.js"></script>
</body>
</html>
multi.js:
var CanvasDrawr = function(options) {
// grab canvas element
var canvas = document.getElementById(options.id),
ctxt = canvas.getContext("2d");
canvas.style.width = '100%'
canvas.width = canvas.offsetWidth;
canvas.style.width = '';
// set props from options, but the defaults are for the cool kids
ctxt.lineWidth = options.size || Math.ceil(Math.random() * 35);
ctxt.lineCap = options.lineCap || "round";
ctxt.pX = undefined;
ctxt.pY = undefined;
var lines = [,,];
var offset = $(canvas).offset();
var eventCount = 0;
var self = {
// Bind click events
init: function() {
// Set pX and pY from first click
canvas.addEventListener('touchstart', self.preDraw, false);
canvas.addEventListener('touchmove', self.draw, false);
},
preDraw: function(event) {
$.each(event.touches, function(i, touch) {
var id = touch.identifier;
lines[id] = { x : this.pageX - offset.left,
y : this.pageY - offset.top,
color : 'black'
};
});
event.preventDefault();
},
draw: function(event) {
var e = event, hmm = {};
eventCount += 1;
$.each(event.touches, function(i, touch) {
var id = touch.identifier,
moveX = this.pageX - offset.left - lines[id].x,
moveY = this.pageY - offset.top - lines[id].y;
var ret = self.move(id, moveX, moveY);
lines[id].x = ret.x;
lines[id].y = ret.y;
});
event.preventDefault();
},
move: function(i, changeX, changeY) {
ctxt.strokeStyle = lines[i].color;
ctxt.beginPath();
ctxt.moveTo(lines[i].x, lines[i].y);
ctxt.lineTo(lines[i].x + changeX, lines[i].y + changeY);
ctxt.stroke();
ctxt.closePath();
return { x: lines[i].x + changeX, y: lines[i].y + changeY };
},
};
return self.init();
};
$(function(){
var drawr = new CanvasDrawr({ id: "draw", size: 5 });
});
Looking at your code, you should do some optimizing. Right off the bat, never use jQuery's $.each() to do loops. Also, every time you poll the left, top, width or height of something, you're causing the browser to stop what it's doing, repaint the entire screen, and fetch you the most accurate values. Store these values in javascript variables instead. Use google chrome's timeline feature to find and eliminate unecessary paints and reflows. Here are some helpful links:
Nicholas C. Zakas Gives You Some Tips on avoiding Reflows.
http://oreilly.com/server-administration/excerpts/even-faster-websites/writing-efficient-javascript.html
Here is Zakas giving his presentation to Google programmers:
http://www.youtube.com/watch?v=mHtdZgou0qU
Paul Irish Speeds up a slow JavaScript in front of your eyes:
http://www.youtube.com/watch?v=Vp524yo0p44
Please note, at the time of that video, the timeline was a beta feature in Chrome. It's now standard in Chrome 20. If you don't see it, update your Chrome.
Unfortunately... Even with all these optimizations... as of 2012 ...
MOST ANDROID DEVICES ARE STILL SLOW :-(
The touch events don't fire as rapidly as they do in Apple devices because Apple devices just have better hardware than most devices running Android's OS. There are fast Android tablets and phones out there, but they usually cost as much as much as the Apple devices--probably because they have similar hardware specs. Apple devices have special floating-point math chips and graphics chips in addition to the main CPU. Many Android devices do not contain those extra chips, instead they have virtual floating-point math chips.
The only thing you can do to accommodate slower Android devices is to detect them and gracefully degrade the user experience. For example, I created a draggable product carousel. For Androids, I eliminate the drag option and add clickable scroll arrows that move the carousel to the left or right a fixed set of pixels at a time.
The only way to really know where and why your code is underperforming is to profile it.
Chrome Mobile lets you connect to the WebKit inspector from your desktop, giving you access to the fantastic debugging tools you're used to in Chrome's Developer Tools.
Once you're connected to Chrome Mobile, profile your script and see what functions are chewing up CPU time. Then you'll be able to start figuring out how to optimize those functions.