Is there any difference between writing JS touch events for iPad vs. iPhone? I have read a ton of documentation and as far as I can tell it should work the same way for both.
I have a drag-and-drop game, basically you grab a coin from under the dragon and drag it over to your vault. The dragging works on iPad, but not on iPhone. I'm trying to figure out why.
The game, for reference: https://codeeverydamnday.com/projects/dragondrop/dragondrop.html
The JS, abridged to just the relevant code for this question (with comments for clarity):
var dragndrop = (function() {
var myX = "";
var myY = "";
// The coin's starting X and Y coordinate positions
var coin = "";
// The coin you start touching / dragging
function touchStart(e) {
e.preventDefault();
// Prevents default behavior of scrolling when you touch/drag on mobile
var coin = e.target;
var touch = e.touches[0];
var moveOffsetX = coin.offsetLeft - touch.pageX;
var moveOffsetY = coin.offsetTop - touch.pageY;
// Defines offset between left edge of coin and where you place your finger on it
coin.addEventListener('touchmove', function() {
var positionX = touch.pageX+moveOffsetX;
var positionY = touch.pageY+moveOffsetY;
// Defines the X-Y coordinates of wherever you stop dragging
coin.style.left = positionX + 'px';
coin.style.top = positionY + 'px';
// Updates the coin's X-Y coordinates with the new positions
}, false)
}
document.querySelector('body').addEventListener('touchstart', touchStart, false);
})();
If it helps, I am getting this console log error every time I click / tap on the iPad screen in the Chrome Dev Tools emulator:
[Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive.
This error doesn't seem to prevent the dragging from working on iPad, but I'm not sure if it has anything to do with the dragging not working on mobile? I tried adding a few things to fix the error based on other Stack Overflow questions I saw (ex. adding touch-action: none; in my CSS, but the error persisted).
Anyone see anything wrong in my code? I would love to get this game playable on mobile, as that's how most people would access it!
The default value of the passive option is set to true for touch-start and touch-move events, and it being true means your function won't call preventDefault to disable scrolling.
Simply set the passive value to false to solve your issue.
var dragndrop = (function() {
var myX;
var myY;
var coin;
function touchStart(e) {
e.preventDefault();
coin = e.target;
const touch = e.touches[0];
const moveOffsetX = whichArt.offsetLeft - touch.pageX;
const moveOffsetY = whichArt.offsetTop - touch.pageY;
coin.addEventListener("touchmove", touchMove, { passive: false });
function touchMove(e) {
const touch = e.touches[0];
const positionX = touch.pageX + moveOffsetX;
const positionY = touch.pageY + moveOffsetY;
coin.style.left = `${positionX}px`;
coin.style.top = `${positionY}px`;
}
}
document.body.addEventListener('touchstart', touchStart, { passive: false });
})();
Edit
I looked at the code from the website you linked, and I realized that one reason the coin wasn't dragged was because of touch we were using and also because of the once option I passed to the touchmove event.
Whenever the touchmove event is used, we have to use the new touch to get the pageX and pageY positions on the screen, I decided to create a function for the touchmove event, because whenever the touchstart function is called, a new touchmove event is registered because of the anonymous function handler.
So creating and naming a function for it will prevent the same function from being added.
Related
I want to simulate clicks on a canvas with javascript\jQuery for testing reasons, but I didn't find a solution. Here's my code:
var e = new jQuery.Event("click");
e.pageX = X[index];
e.pageY = Y[index];
$("#gamecanvas").trigger(e);
Is it possible to do that ?
For example this game (I searched randomly on the web) How can I click from JS\jQuery ?
It depends on the event's used on the canvas , whether it's a click , mousedown , .... etc
In the example you just mentioned , the lucn event uses two event :
One (mousemove) for calculating coordinate to get clientX and clientY
Second (mousedown) for lunching ball using last calculated coordinate
So your code should be like :
var mousemove = new jQuery.Event("mousemove");
mousemove.clientX = x;//passed valuue
mousemove.clientY =y;//passed valuue
var mousedown = new jQuery.Event("mousedown");
$("#canvas").trigger(mousemove);
$("#canvas").trigger(mousedown);
Here a pluncker where I created a script to luanch a ball with passed input coordinate or jus throw the ball in the basket directly :)
See here livePlunker
See url code plunker
Hope this will help :
This example may help you
$('#canvas_element').on("mousedown mouseup", function(e) {
$('#output').text($('#output').text() + (e.type + " event fired at coords: " + e.pageX + ", " + e.pageY));
});
x_coord = 1;
y_coord = 1;
var e = jQuery.Event( "mousedown", { pageX: x_coord, pageY: y_coord } );
$('#canvas_element').trigger(e);
// execute more code
var e = jQuery.Event( "mouseup", { pageX: 255, pageY: 255 } );
$('#canvas_element').trigger(e);
working link
This is because, pageX and pageY doesn't get the coordinates of the canvas, I had the same issue my self when trying to create a signature pad.
use this instead:
var e = new jQuery.Event("click");
//for IE, safari, opera, chrome
if(e.offsetX != null) {
e.offsetX= X[index];
e.offsetY= Y[index];
}
//for firefox
if(e.layerX!= null) {
e.layerX= X[index];
e.layery= Y[index];
}
$("#gamecanvas").trigger(e);
Without know you use case the documentation for jQuery tells me yes:
http://api.jquery.com/trigger/
Why you don't search by your self through the very well documented API for jquery?
I have a custom built fabric.js bundle with touch support. Now I can scale any object with the pinch-zoom gesture. The problem is the zoom is really really sensitive, I barely move my fingers and the object is hugely scaled.
I couldn't find much information in the documents about how I can change the sensitivity. I know Event.js is used to handle the touch events within fabric.js. Is there any way I can change this sensitivity?
Ok, I ended up implementing touch controls myself, this is the code I made. This code was placed on the added event of my custom fabric.js object.
////////////////////////////// Touch event handlers
// Add listener event for pinch-zoom
var bbScope = this;
var hammer = new Hammer.Manager(this.canvas.upperCanvasEl);
var pinch = new Hammer.Pinch();
hammer.add([pinch]);
hammer.on('pinch', function (ev) {
// Set the scale and render only if we have a valid pinch (inside the object)
if (bbScope._validPinch) {
bbScope.set('scaleX', ev.scale);
bbScope.set('scaleY', ev.scale);
bbScope.canvas.renderAll();
}
});
hammer.on('pinchend', function (ev) {
bbScope._validPinch = false;
});
hammer.on('pinchcancel', function (ev) {
bbScope._validPinch = false;
});
hammer.on('pinchstart', function (ev) {
// Convert mouse coordinates to canvas coordinates
ev.clientX = ev.center.x;
ev.clientY = ev.center.y;
// Check if the pinch was started inside this object
if (bbScope.canvas) {
var p = bbScope.canvas.getPointer(ev);
bbScope._validPinch = bbScope.containsPoint(p);
}
else {
bbScope._validPinch = false;
}
});
I am trying to learn Javascript by following a guide on creating a Minesweeper game using a canvas. It's all going great apart from the fact that when I click on the options bar, it registers it as a click on the canvas.This is a screenshot of the whole game so far. I can still click on the canvas and it works as it should, but clicking on the options bar (The part with the smiley face), it also registers as a click on the canvas, and removes one of the tiles.
Here is the code that registers the mouse click:
'
if(e.offsetX) { //Get's mouse pos relative to the canvas pos
mouseX = e.offsetX;
mouseY = e.offsetY;
}
else if(e.layerX) {
mouseX = e.layerX;
mouseY = e.layerY;
}
//mouseX = e.pageX; //Gets mouse pos relatiove to page
//mouseY = e.pageY;
//console.log("Mouse Pos on screen (x, y) = " + mouseX + "," + mouseY);//Validate that the mouse position is being recorded.
//Algorithm to tell which cube the click was on.
if (Math.floor(mouseX/settings.width) < settings.columns && Math.floor(mouseY/settings.height) < settings.rows){
clickX = Math.floor(mouseX/settings.width);
clickY = Math.floor(mouseY/settings.height);
console.log("Coords of clicked box (x,y)" + clickX + "," + clickY);
}
`
Hopefully this is enough for someone to find a problem with it, because I can't.
I think the problem is that you're capturing the click events for the whole window in one function, so whether you're clicking on the canvas or the new game div, or elsewhere in the window, you're trying to apply that click to your canvas. You'd be better advised to have a click event for the canvas itself, and another click event for the new game (which should really be a button, not a div, but I digress)
If you really want to carry on this way, you could explicitly limit your function to only care about your canvas. if you make gCanvas global (shudder)
var gCanvas = null;
function canvasLoad() {
gCanvas = document.getElementById("gameCanvas");
...
}
Then in your event function you can check the target of the event and only action on things that fired from your canvas
window.onclick = function(e){
...
//Algorithm to tell which cube the click was on.
if (
(e.target === gCanvas )
)
{
if (Math.floor(mouseX/settings.width) < settings.columns &&
Math.floor(mouseY/settings.height) < settings.rows)
{
clickX = Math.floor(mouseX/settings.width);
clickY = Math.floor(mouseY/settings.height);
console.log("Coords of clicked box (x,y)" + clickX + "," + clickY);
}
}
Note that this isn't the solution I'd go with. A tighter binding of the event to the object that fired it is much more satisfactory.
<canvas id = "gameCanvas" width="153" height="153"
style="border:1px solid #000000;" #
onclick='return myCanvasEventHandler()'></canvas>
This will explicitly limit the myCanvasEventHandler() function to fire only on events from the canvas itself.
Inside an object (created based on this tutorial), I have the following code. In this code, the lines:
event.target.x = evt.stageX;
event.target.y = evt.stageY;
are wrong. What should they be to access the mouse movement?:
(function() {
....
var p = createjs.extend(Card, createjs.Container);
p.setup = function() {
this.on("pressmove", this.handlePressMove);
....
p.handlePressMove = function (event) {
event.target.x = evt.stageX; //"Event" is wrong. So is "evt"
event.target.y = evt.stageY;
stage.setChildIndex(this, stage.getNumChildren()-1);
stage.update();
}
}
}());
Your code sample uses evt.stageX instead of event.stageX. All MouseEvents will have a stageX and stageY, which is the position the mouse was in when it fired the event. I think your code came from this tutorial which uses evt exlusively.
Additionally, MouseEvents have a rawX and rawY on pressMove events, which give you the x/y outside of the canvas element. There is no clientX or clientY on EaselJS MouseEvents.
Here is a quick sample using the stageX/stageY.
http://jsfiddle.net/lannymcnie/suva8vt3/
Snippet:
shape.on("pressmove", function(event) {
shape.x = event.stageX;
shape.y = event.stageY;
});
I'm making a 3D game where the camera needs to be pretty much identical to the World of Warcraft one. That means, that once the screen is clicked, the cursor disappears, and when the mouse is moved, the camera rotates around a focus point (the player).
I made most of the code, the cursor does disappear when the screen is clicked, but the problem is that it is still moving, even though it isn't shown. That feels unnatural, and I'd like to have the cursor to stay at the same spot for the whole time.
So, how do I achieve this with Javascript?
The only support reqs are the latest versions of Chrome and Firefox.
You can't manipulate the position of the cursor like that in JavaScript because of the security implications that it incurs.
If I am getting your question right,
Not possible via javascript, you will need flash.
But yes, some progess is really going on,
Pointer lock api
Update: (I had free time, so I played with it)
You can try something like this, its not perfect,not recommended and it fails when original mouse reaches the screen borders (however you can restrict the mouse movements in a wrapper, that will solve the problem).
Html:
<body style='width:100%;min-height:800px;overflow:hidden'>
<img id='fakeCursor' src='http://i50.tinypic.com/359d3km.jpg' style='z-index:1000;position:absolute;display:none' />
<div href='javascript:void(0);' style='position:absolute;left:50px;top:10px;width:100px;height:100px;background:gray;' onclick='console.log("fake click 1");return false;'>Fake click 1</div>
<div href='javascript:void(0);' style='position:absolute;left:50px;top:130px;width:100px;height:100px;background:gray;' onclick='console.log("fake click 2");return false;'>Fake click 2</div>
</body>
Javascript:
var clientX,clientY;
var fakeCursor = document.getElementById('fakeCursor');
var isFakeMouse = false;
document.onclick = function(e){
if(isFakeMouse){
if(document.elementFromPoint(fakeCursor.style.left,fakeCursor.style.top)!=null){
document.elementFromPoint(fakeCursor.style.left,fakeCursor.style.top).click();
return false;
}
fakeCursor.style.display = 'inline';
fakeLeft = clientX;
fakeTop = clientY;
var mouseLastLeft = -1;
var mouseLastTop = -1;
document.onmousemove = function(e){
if(mouseLastLeft ===-1 && mouseLastTop ===-1){
mouseLastLeft = e.clientX;
mouseLastTop = e.clientY;
return;
}
fakeCursor.style.left = (parseInt(fakeCursor.style.left) + ((mouseLastLeft - e.clientX)*-1)) + 'px';
fakeCursor.style.top = (parseInt(fakeCursor.style.top) + ((mouseLastTop - e.clientY)*-1)) + 'px';
mouseLastLeft = e.clientX;
mouseLastTop = e.clientY;
}
}
else{
isFakeMouse = true;
document.body.style.cursor = "none";
fakeCursor.style.display = 'none';
fakeCursor.style.left = clientX = e.clientX;
fakeCursor.style.top = clientY = e.clientY;
document.onmousemove = null;
}
}
Here, at first click on document, the real mouse hides. When user clicks document again, real mouse would be still hidden and a new fake mouse (an image) will take its place. Position of the fake mouse would be the same where user has left the real mouse. fake mouse works (tries) to work like real mouse.
Sorry for inline css and javascrict