Closed inaridarkfox4231 closed 1 month ago
The reason I brought up this issue is because a sketch I wrote in the past broke due to a version update.
hey i understand it can you please assign me this issue
thank you!
I'll explain why I consider this a bug.
According to the current specifications, if you only write mousePressed() and do not write touchStarted(), the contents of mousePressed() will be executed for a mouse down operation, but not for a touch start operation. However, this does not mean that it is not executed at all. For touch start operations, the contents of mousePressed() are executed after the touch end operation. If the content of mouseReleased() is also prepared, it will be executed after that. However, this is not the intended timing.
What kind of trouble does this cause? The following sketch is second example of reference site, but it doesn't work on my Android. Of course, a stylus pen won't work either. The color of the circle does not change even if I touch it.
function setup() {
createCanvas(100, 100);
// Style the circle.
fill('orange');
stroke('royalblue');
strokeWeight(10);
describe(
'An orange circle with a thick, blue border drawn on a gray background. When the user presses and holds the mouse, the border becomes thin and pink. When the user releases the mouse, the border becomes thicker and changes color to blue.'
);
}
function draw() {
background(220);
// Draw the circle.
circle(50, 50, 20);
}
// Set the stroke color and weight as soon as the user clicks.
function mousePressed() {
stroke('deeppink');
strokeWeight(3);
}
// Set the stroke and fill colors as soon as the user releases
// the mouse.
function mouseReleased() {
stroke('royalblue');
// This is never visible because fill() is called
// in mouseClicked() which runs immediately after
// mouseReleased();
fill('limegreen');
}
// Set the fill color and stroke weight after
// mousePressed() and mouseReleased() are called.
function mouseClicked() {
fill('orange');
strokeWeight(10);
}
Of course, if you expect it to work with touch operations, it is better to prepare functions for touch operations, touchStarted(), etc. However, the reference says that mousePressed() acts as a replacement for touchStarted(), if touchStarted() is not declared, so this behavior feels contrary to that description. That's the reason. If it is to truly function as a replacement, the timing of its activation must be appropriate.
However, as shown in the example above, you can avoid this problem by using touchStarted() and touchEnded() instead of mousePressed() and mouseReleased(). In fact, if you write it like this, you will get the intended behavior whether you use the mouse or touch operation.
In other words, I mean we should give up on expecting mousePressed() and mouseReleased() function as replacement of functions for touch operations.
This has caused some sketches I've written in the past to no longer work with touch. But it's not difficult to rewrite. It may not be necessary to fix this bug.
However, since there is definitely a behavior problem, we have decided to treat this as a bug.
I leave it up to the contributors to decide what to do about this situation.
I have a library for Interaction myself, and if I use that, I don't have any unnatural behavior like firing mouse down event after a touch end event, so I don't have any particular problems in that sense. I raised this issue for users who cannot help themselves.
In other words, I'm not in trouble, so I don't really have a problem if this issue continues to go unnoticed. If the cause is environmental and this phenomenon only occurs on my Android mobile phone or my stylus pen, then it's even less of a problem. But I can't judge that, so I've raised an issue.
Probably relevant, this PR is I think the last one that changed code related to this: https://github.com/processing/p5.js/pull/6738
For anyone attempting to write a fix, we should also double check that we aren't accidentally reintroducing the issue this was addressing with events double-firing.
One of the problems is that the reference for mousePressed() is lying. mousePressed
On touchscreen devices, mousePressed() will run when a user’s touch begins
if touchStarted() isn’t declared.
In reality, the contents of mousePressed() are not executed at the moment of touch. I think one solution would be to clearly state that in the reference. If you want it to work with touch operations, it is a reasonable decision to use touchStarted().
Another solution is to let it run (i.e. run the mousePressed() with touch start operation) by fix the current logic. Additionally, we need to make sure that previously resolved bugs don't come back, but I don't know what to do.
Here's an idea. First, set the base to 1.9.0 and make the following changes:
this._events = {
// keep track of user-events for unregistering later
mousemove: null,
mousedown: null,
mouseup: null,
dragend: null,
dragover: null,
click: null,
dblclick: null,
mouseover: null,
mouseout: null,
keydown: null,
keyup: null,
keypress: null,
touchstart: null,
touchmove: null,
touchend: null,
resize: null,
blur: null
};
this._millisStart = -1;
this._recording = false;
this._touchFlag = false; // flag for touch
Set this._touchFlag value to true at touchstart().
p5.prototype._ontouchstart = function(e) {
/* ~~~~~~ (Omit intermediate code) ~~~~~~ */
this._touchFlag = true;
}
};
mousedown() and mouseup() events will not be executed if this flag is set. Also, set this._touchFlag to false at the end of the mouseup() event.
p5.prototype._onmousedown = function(e) {
const context = this._isGlobal ? window : this;
let executeDefault;
this._setProperty('mouseIsPressed', true);
this._setMouseButton(e);
this._updateNextMouseCoords(e);
if (this._touchFlag) {
return;
}
/* ~~~~~~ (Omit intermediate code) ~~~~~~ */
};
[mouseup]()
p5.prototype._onmouseup = function(e) {
const context = this._isGlobal ? window : this;
let executeDefault;
this._setProperty('mouseIsPressed', false);
if (this._touchFlag) {
this._touchFlag = false;
return;
}
/* ~~~~~~ (Omit intermediate code) ~~~~~~ */
};
In addition to this, apparently the mouse event on end of touch is not fired when touchmove() is executed. Therefore, set this._touchFlag to false in the touchmove event. touchmove
p5.prototype._ontouchmove = function(e) {
this._touchFlag = false;
/* ~~~~~~ (Omit intermediate code) ~~~~~~ */
};
I have now confirmed that there is no problem with the behavior with the mouse, stylus pen, and touch. DEMO
I don't know if this will work or not. Bugs that are beyond our imagination may occur. Please consider this as a starting point only.
There is a Safari condition in the if statement, but it seems that because of this condition, the intended behavior is not achieved in Firefox. However, I don't know if this is correct.
before:
} else if (navigator.userAgent.toLowerCase().includes('safari') && typeof context.touchStarted === 'function') {
/* ~~~~~ */
} else if (navigator.userAgent.toLowerCase().includes('safari') && typeof context.mousePressed === 'function') {
after:
} else if (typeof context.touchStarted === 'function') {
/* ~~~~~ */
} else if (typeof context.mousePressed === 'function') {
As For Firefox, when the Safari condition was specified, the mouse operation worked as intended in the case of mouse only, and the touch operation worked as intended in the case of touch only. When both were specified, it worked without any problems.
It looks like there will be no problem if we describe both mouse and touch behavior like this, without having to change the current logic, but... If you want to accept operations from both a mouse and a stylus, the current logic will cause problems.
https://github.com/user-attachments/assets/f6125b0c-5fec-4831-a03c-aeef4470bf36
function setup() {
createCanvas(400, 400);
}
function draw() {
background(220);
if(mouseIsPressed){
console.log("is");
circle(mouseX, mouseY, 20);
}
}
function mousePressed(){
console.log("mousePressed_"+frameCount);
}
function mouseReleased(){
console.log("mouseReleased_"+frameCount);
}
function touchStarted(){
console.log("touchStarted_"+frameCount);
}
function touchEnded(){
console.log("touchEnded_"+frameCount);
}
If you use the mouse, then the stylus, then the mouse again, mousePressed() and mouseReleased() no longer work. Of course, the previous version of 1.9.0 was not without problems. When using a stylus, mousePressed() and mouseReleased() are called. The flag that rejects mouse operations cannot be turned off by the user, so there is no way to deal with this situation by writing external code.
In the demo I prepared, all of these issues are resolved. DEMO
That's all for my suggestion. Since the content seems to overlap with #7195, so I will close this issue.
Most appropriate sub-area of p5.js?
p5.js version
1.9.1
Web browser and version
Chrome
Operating system
Windows
Steps to reproduce this
Steps:
Snippet:
https://github.com/user-attachments/assets/cbb6c881-c4ff-4ada-8ef6-1db562feeee5
Note that I use a stylus pen for touch because to check on a desktop, but the results are the same on a mobile phone.
In the above code, in the case of touch, only the processing at mouseIsPressed is executed, and at the timing of release, the contents of mousePressed and mouseReleased are executed in this order.
However, the following code achieves the intended behavior even on touch operations. In other words, first mousePressed is executed, then the process for mouseIsPressed is executed in the draw loop, and finally mouseReleased is executed.
Regarding the mouse, either code results in the intended behavior.
In other words, if you only describe the interaction when using the mouse, mousePressed() will not be executed when touch is started.
Furthermore, in the case of the first code above, if the start and end positions of the touch are different, neither mousePressed nor mouseReleased seems to be executed.
https://github.com/user-attachments/assets/134b441a-2b6c-4a55-86a9-bb79c6395357
It's hard to see in the video, but if you touch it, move it, and then release it, none of the processes will be executed. Any second code will be executed.
OpenProcessing: Interaction bug