From faa556f33f14008b5dde62539440ab899898966d Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 10 Sep 2025 10:30:19 -0400 Subject: [PATCH] index.html.in: finish the QR focus toggle Get the QR focus toggle working "completely." * Don't show ticket resize transitions when the page loads (Commuter bus / MARC). * Fade out the origin/destination and zone when the QR is focused (Commuter bus / MARC). * Resize the bottom portion of the ticket when the QR code is focused, so that the QR sits in the "middle" like it does on CharmPass. * Don't recompute dimensions if we're going to toggle the QR code _off_. BaltimoreLink tickets are essentially correct. I have yet to compare against real Commuter bus and MARC train tickets, but the current behavior is reasonable. --- index.html.in | 97 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 76 insertions(+), 21 deletions(-) diff --git a/index.html.in b/index.html.in index 3cea854..3fe1294 100644 --- a/index.html.in +++ b/index.html.in @@ -203,15 +203,24 @@ /*****************/ #ticketbottombg { - transform-box: fill-box; + transform-box: fill-box; /* don't reposition on scale */ transform-origin: top; /* scale down only */ } + #ticketbottombg.loaded { + /* The transitions are for the QR focus animation. These are + * specified in a class that is assigned only after commuter + * bus / MARC train tickets are initially (re)sized, so that + * the transition animation doesn't play prematurely. */ + transition-property: transform; + transition-duration: 0.25s; + } + /*****************/ /* QR code focus */ /*****************/ - #serviceid, #servicename, #codebg, #codetext { + #codebg, #codetext, #origindest, #serviceid, #servicename, #zone { transition-property: opacity; transition-duration: 0.25s; } @@ -621,6 +630,10 @@ t.setAttribute("transform", "translate(0 -67.17)"); sn.setAttribute("transform", "translate(0 131.0)"); } + + /* Enable CSS transitions for future scalings, namely + * for the QR focus animation. */ + tbbg.setAttribute("class", "loaded"); } @@ -778,50 +791,92 @@ * must first convert between the two using an object whose * size is known. We use the same trick when centering the * security code within its box. + * + * A posteriori, using CSS was the right way to do this, because + * the commuter bus and MARC train tickets are initially sized + * with the transform _attribute_. The CSS property takes + * precedence, but when we unset the CSS property, whatever + * scaling factor was set in the attribute kicks back in. So + * ultimately we don't have to worry about the initial ticket + * size: the scaled-up size for the QR focus is always bigger + * than the ticket; everything just works. */ function toggle_qr_focus(event) { /* Don't swap night/day if the tap was on the QR code. */ event.stopPropagation(); + const codetext = document.getElementById("codetext"); + const codebg = document.getElementById("codebg"); + const origindest = document.getElementById("origindest"); const qr = document.getElementById("qr"); const serviceid = document.getElementById("serviceid"); const servicename = document.getElementById("servicename"); - const codetext = document.getElementById("codetext"); - const codebg = document.getElementById("codebg"); - - /* Compute the QR translation amount in pixels using - * the (known) width of the security code box. */ - const r1 = codebg.getBoundingClientRect(); - const svg_to_css = r1.width / parseFloat(codebg.getAttribute("width")); - const offset = -6.56 * svg_to_css; + const t = document.getElementById("ticket"); + const tbbg = document.getElementById("ticketbottombg"); + const zone = document.getElementById("zone"); - let ticket_class; /* transparent, or none (opaque) */ - let qr_class; /* big, or none (normal sized) */ - let qr_transform; /* text of the transform style to be set */ + let ticket_class; /* transparent, or none (opaque) */ + let qr_class; /* big, or none (normal sized) */ + let qr_transform; /* text of the transform style to be set */ + let tbbg_transform; /* transform (resize) of ticket bottom */ + let t_transform; /* upwards ticket translation */ if (qr.getAttribute("class") == "big") { ticket_class = ""; qr_class = ""; qr_transform = ""; + tbbg_transform = ""; + t_transform = ""; } else { ticket_class = "transparent"; qr_class = "big"; - /* The scale factor can easily be measured in CharmPass by - * taking two screenshots, pre- and post-tap. The magic offset + /* Compute the QR x-translation amount in pixels using the + * (known) width of the security code box. The magic offset * was measured by scaling up the QR code in the SVG, and - * then centering it on the ticket. This gives you the offset - * in SVG units, which we then have to DIVIDE by 4.1333333, - * because the scale factor affects the translation. */ - qr_transform = "scale(4.1333333) translate(" + offset + "px, 0px)"; + * then centering it on the ticket. This gives you the + * offset in SVG units, which we then have to DIVIDE by the + * scaling factor, because when we scale the QR code in a + * second, the scaling is going to affect the translation. + * + * The y-translations for the QR code and ticket by contrast + * were pretty much chosen on vibes. + * + * Note: the fucked up spacing around the QR code is there in + * CharmPass, too -- that's not my fault. + */ + const client_width = codebg.getBoundingClientRect().width; + const svg_width = parseFloat(codebg.getAttribute("width")); + const svg_to_client = client_width / svg_width; + const qr_xoffset = -6.56 * svg_to_client; + const qr_yoffset = 43 * svg_to_client; + const t_yoffset = -40 * svg_to_client; + + /* The _CSS_ translate transform takes comma-separated strings, + * and is specified in px. */ + const qr_translate = `${qr_xoffset}px, ${qr_yoffset}px`; + const t_translate = `0, ${t_yoffset}px`; + + /* The scale factors can easily be measured in CharmPass by + * taking two screenshots, pre- and post-tap. */ + qr_transform = `scale(4.13333333) translate(${qr_translate})`; + tbbg_transform = "scale(1, 1.47)"; + t_transform = `translate(${t_translate})`; } - serviceid.setAttribute("class", ticket_class); - servicename.setAttribute("class", ticket_class); + /* The origindest and zone are only visible on commuter bus and + * MARC train tickets, but they're hidden with "display: none", + * so we don't have to worry about making them opaque. */ codetext.setAttribute("class", ticket_class); codebg.setAttribute("class", ticket_class); + origindest.setAttribute("class", ticket_class); + serviceid.setAttribute("class", ticket_class); + servicename.setAttribute("class", ticket_class); + zone.setAttribute("class", ticket_class); + tbbg.style.transform = tbbg_transform; + t.style.transform = t_transform; qr.style.transform = qr_transform; qr.setAttribute("class", qr_class); } -- 2.49.0