mirror of
https://github.com/rstudio/shiny.git
synced 2026-04-29 03:00:45 -04:00
Click handler on scaled image getting clipped (#4094)
* Fix #3234: Click handler on scaled image getting clipped There were two related problems here, both happening in the same scenario: when an imageOutput with click handlers is showing an image at less than its natural size (e.g. a 1000x1000 px .png file, being displayed in the web page at 500x500 due to max-width or for whatever other reason), any click where the image coordinate (1000x1000) exceeds the display size (500x500). In the example above, a user clicks at 300x300 in the 500x500 displayed image. We call 300x300 the "CSS coordinates". This gets scaled up into the position in the PNG's own coordinate system, "image coordinates": in this case, 600x600. Since the 600x600 image coordinate is greater than the 500x500 CSS coordinate limit, the following issues were triggered. 1. When imageOutput(click=clickOpts(clip=TRUE)) (the default), these clicks weren't registering at all. There was code that detected clicks that were inside the imageOutput but outside the actual image, but this code didn't take scaling into account. 2. Even with clip=FALSE, the click would be triggered BUT the `x` and `y` values on the click event were incorrect--they would max out at the CSS coordinate limit. This because plot and image output divide the world into "panels" and clicks snap to the nearest panel. In the case of image outputs, the server doesn't provide any panels, so the client makes one big panel that covers the whole image--but that code was erroneously using CSS sizes, not image sizes. * Update NEWS
This commit is contained in:
1
NEWS.md
1
NEWS.md
@@ -27,6 +27,7 @@ If either 1 or 2 leads to undesirable behavior in your app, you can disable them
|
|||||||
* Output bindings that are removed, invalidated, then inserted again (while invalidated) now correctly include the `.recalculating` CSS class. (#4039)
|
* Output bindings that are removed, invalidated, then inserted again (while invalidated) now correctly include the `.recalculating` CSS class. (#4039)
|
||||||
|
|
||||||
* Fixed a recent issue with `uiOutput()` and `conditionalPanel()` not properly lower opacity when recalculation (in a Bootstrap 5 context). (#4027)
|
* Fixed a recent issue with `uiOutput()` and `conditionalPanel()` not properly lower opacity when recalculation (in a Bootstrap 5 context). (#4027)
|
||||||
|
* Image outputs that were scaled by CSS had certain regions that were unresponsive to hover/click/brush handlers. (#3234)
|
||||||
|
|
||||||
# shiny 1.8.1.1
|
# shiny 1.8.1.1
|
||||||
|
|
||||||
|
|||||||
@@ -14507,8 +14507,8 @@
|
|||||||
var bounds = {
|
var bounds = {
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
right: img.clientWidth - 1,
|
right: img.naturalWidth - 1,
|
||||||
bottom: img.clientHeight - 1
|
bottom: img.naturalHeight - 1
|
||||||
};
|
};
|
||||||
coordmap_.panels[0] = {
|
coordmap_.panels[0] = {
|
||||||
domain: bounds,
|
domain: bounds,
|
||||||
@@ -14581,10 +14581,15 @@
|
|||||||
};
|
};
|
||||||
var matches = [];
|
var matches = [];
|
||||||
var dists = [];
|
var dists = [];
|
||||||
var b3;
|
|
||||||
var i5;
|
var i5;
|
||||||
for (i5 = 0; i5 < coordmap.panels.length; i5++) {
|
for (i5 = 0; i5 < coordmap.panels.length; i5++) {
|
||||||
b3 = coordmap.panels[i5].range;
|
var panelRange = coordmap.panels[i5].range;
|
||||||
|
var b3 = {
|
||||||
|
top: panelRange.top * cssToImgRatio.y,
|
||||||
|
bottom: panelRange.bottom * cssToImgRatio.y,
|
||||||
|
left: panelRange.left * cssToImgRatio.x,
|
||||||
|
right: panelRange.right * cssToImgRatio.x
|
||||||
|
};
|
||||||
if (x2 <= b3.right + expandImg.x && x2 >= b3.left - expandImg.x && y4 <= b3.bottom + expandImg.y && y4 >= b3.top - expandImg.y) {
|
if (x2 <= b3.right + expandImg.x && x2 >= b3.left - expandImg.x && y4 <= b3.bottom + expandImg.y && y4 >= b3.top - expandImg.y) {
|
||||||
matches.push(coordmap.panels[i5]);
|
matches.push(coordmap.panels[i5]);
|
||||||
var xdist = 0;
|
var xdist = 0;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
2
inst/www/shared/shiny.min.js
vendored
2
inst/www/shared/shiny.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -162,8 +162,8 @@ function initCoordmap(
|
|||||||
const bounds = {
|
const bounds = {
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
right: img.clientWidth - 1,
|
right: img.naturalWidth - 1,
|
||||||
bottom: img.clientHeight - 1,
|
bottom: img.naturalHeight - 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
coordmap_.panels[0] = {
|
coordmap_.panels[0] = {
|
||||||
@@ -290,11 +290,16 @@ function initCoordmap(
|
|||||||
|
|
||||||
const matches = []; // Panels that match
|
const matches = []; // Panels that match
|
||||||
const dists = []; // Distance of offset to each matching panel
|
const dists = []; // Distance of offset to each matching panel
|
||||||
let b;
|
|
||||||
let i;
|
let i;
|
||||||
|
|
||||||
for (i = 0; i < coordmap.panels.length; i++) {
|
for (i = 0; i < coordmap.panels.length; i++) {
|
||||||
b = coordmap.panels[i].range;
|
const panelRange = coordmap.panels[i].range;
|
||||||
|
const b = {
|
||||||
|
top: panelRange.top * cssToImgRatio.y,
|
||||||
|
bottom: panelRange.bottom * cssToImgRatio.y,
|
||||||
|
left: panelRange.left * cssToImgRatio.x,
|
||||||
|
right: panelRange.right * cssToImgRatio.x,
|
||||||
|
};
|
||||||
|
|
||||||
if (
|
if (
|
||||||
x <= b.right + expandImg.x &&
|
x <= b.right + expandImg.x &&
|
||||||
@@ -413,5 +418,5 @@ function initCoordmap(
|
|||||||
return coordmap;
|
return coordmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export { findOrigin, initCoordmap };
|
||||||
export type { Coordmap, CoordmapInit };
|
export type { Coordmap, CoordmapInit };
|
||||||
export { initCoordmap, findOrigin };
|
|
||||||
|
|||||||
2
srcts/types/src/imageutils/initCoordmap.d.ts
vendored
2
srcts/types/src/imageutils/initCoordmap.d.ts
vendored
@@ -48,5 +48,5 @@ type Coordmap = {
|
|||||||
mouseCoordinateSender: (inputId: string, clip?: boolean, nullOutside?: boolean) => (e: JQuery.MouseDownEvent | JQuery.MouseMoveEvent | null) => void;
|
mouseCoordinateSender: (inputId: string, clip?: boolean, nullOutside?: boolean) => (e: JQuery.MouseDownEvent | JQuery.MouseMoveEvent | null) => void;
|
||||||
};
|
};
|
||||||
declare function initCoordmap($el: JQuery<HTMLElement>, coordmap_: CoordmapInit): Coordmap;
|
declare function initCoordmap($el: JQuery<HTMLElement>, coordmap_: CoordmapInit): Coordmap;
|
||||||
|
export { findOrigin, initCoordmap };
|
||||||
export type { Coordmap, CoordmapInit };
|
export type { Coordmap, CoordmapInit };
|
||||||
export { initCoordmap, findOrigin };
|
|
||||||
|
|||||||
Reference in New Issue
Block a user