Measure a hover marker against the drawing, not the box around it
Some checks failed
CI / backend (push) Failing after 7m14s
CI / naming (push) Failing after 7m22s
CI / frontend (push) Failing after 7m14s
CI / migrations-mysql (push) Failing after 7m14s

Reported from the floor: some assets showed their hover mini-map marker way off
to the left, outside the map, while the SAME asset sat correctly on the main
map. The data was right; the preview was drawing it wrong.

The preview is a fixed 390x300 box and the drawing was painted into it with
object-fit: contain, which letterboxes or pillarboxes to keep the drawing's own
aspect ratio. The marker, though, was positioned at a percentage of the BOX. So
the marker and the drawing were measured in two different coordinate spaces, and
they only agreed when the level's aspect ratio happened to be 390:300.

A level near that ratio looked perfect - 3300x2550 is 1.294 against the box's
1.300 - which is why this read as "some devices" rather than "the feature is
broken". A taller drawing pillarboxes, every marker's x shifts, and one near the
left edge lands in the grey gutter beside the map.

The image now SIZES the frame instead of filling it: the transform box
shrink-wraps the image and the image scales down to fit. A percentage of the
frame is therefore a percentage of the drawing by construction, at any aspect
ratio, and the letterboxing moves out to the preview around it where it does no
harm. dimensionsFor still converts the stored pixels to percentages - that part
was always right.

NOT visually confirmed against a live instance. The reasoning is traced and the
build is clean, but the shape of this bug is that it looks correct on any level
close to the old box ratio, so it wants a look at an asset that was landing left
and one that was already fine.

Related and NOT addressed: the wheel zoom applies translate percentages that
resolve against the element and are then multiplied by scale, so a zoomed
preview may still drift. At rest, which is the reported case, it is exact.
This commit is contained in:
cproudlock
2026-08-21 09:50:46 -04:00
parent d0eeaa08d5
commit 2bf1531d5b

View File

@@ -111,6 +111,18 @@ const markerStyle = computed(() => ({
transform: `translate(-50%, -50%) scale(${1 / zoom.value})` transform: `translate(-50%, -50%) scale(${1 / zoom.value})`
})) }))
// The frame the marker's percentages are measured against MUST be the frame the
// drawing is painted in. The image used object-fit: contain inside a fixed
// 390x300 box, so a level whose drawing is not that aspect ratio was letterboxed
// or pillarboxed - and the marker, positioned at a percentage of the BOX, then
// sat in a different coordinate space from the drawing inside it. A tall
// drawing pillarboxes, every marker's x shifts, and one near the left edge
// lands in the grey gutter beside the map. Levels near 390:300 looked correct,
// which is why it only affected some assets.
//
// The fix is to let the IMAGE size the frame: the frame shrink-wraps the drawing,
// so a percentage of the frame IS a percentage of the drawing, whatever the
// level's aspect ratio. The letterboxing moves out to the preview around it.
// Transform style that centers on the marker and zooms toward it // Transform style that centers on the marker and zooms toward it
const transformStyle = computed(() => { const transformStyle = computed(() => {
// Calculate translation to center the marker in the preview // Calculate translation to center the marker in the preview
@@ -253,6 +265,12 @@ watch(currentTheme, () => {
.map-preview { .map-preview {
position: relative; position: relative;
/* Centres the aspect-fitted frame, which is where the letterboxing now
happens. The frame carries the drawing AND the marker, so the two can no
longer disagree. */
display: flex;
align-items: center;
justify-content: center;
/* Smaller than it was: this is a glance-and-move-on preview, and at 500px it /* Smaller than it was: this is a glance-and-move-on preview, and at 500px it
covered the row it was launched from. Aspect ratio kept. */ covered the row it was launched from. Aspect ratio kept. */
width: 390px; width: 390px;
@@ -263,15 +281,23 @@ watch(currentTheme, () => {
.map-transform { .map-transform {
position: relative; position: relative;
width: 100%; /* Shrink-wraps the image rather than filling the preview, so the box the
height: 100%; marker is positioned in is the box the drawing is painted in. */
max-width: 100%;
max-height: 100%;
transition: transform 0.15s ease-out; transition: transform 0.15s ease-out;
} }
.map-image { .map-image {
width: 100%; /* Sizes itself from the drawing, scaled down to fit the preview. NOT
height: 100%; width/height 100% with object-fit: contain - that painted the drawing
object-fit: contain; letterboxed inside a box the marker was measured against, which put the two
in different coordinate spaces on any level that is not 390:300. */
display: block;
max-width: 100%;
max-height: 100%;
width: auto;
height: auto;
} }
.marker-dot { .marker-dot {