Skip to content

Commit e403f2a

Browse files
committed
Give each gradient its own id so codes stop sharing one
1 parent 28eda10 commit e403f2a

3 files changed

Lines changed: 19 additions & 4 deletions

File tree

‎src/qr/render.ts‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,11 @@ export function renderSvg(design: QrDesign, extras: RenderExtras = {}): string {
9191
}
9292
}
9393

94+
/* Several codes share one document — the preview and every preset thumbnail —
95+
so a fixed id would make them all resolve to whichever rendered first. */
96+
const gradientId = `qz-${design.fg}${design.fg2}`.replace(/[^a-z0-9]/gi, '')
9497
const gradient = design.fg2
95-
? `<linearGradient id="qz-g" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="${design.fg}"/><stop offset="1" stop-color="${design.fg2}"/></linearGradient>`
98+
? `<linearGradient id="${gradientId}" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="${design.fg}"/><stop offset="1" stop-color="${design.fg2}"/></linearGradient>`
9699
: ''
97100
const eyeColor = design.eye ?? design.fg
98101
const markColor = resolveMarkColor(design, extras.brandHex)
@@ -109,7 +112,7 @@ export function renderSvg(design: QrDesign, extras: RenderExtras = {}): string {
109112
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${view} ${view}" shape-rendering="geometricPrecision">` +
110113
`<defs>${gradient}</defs>` +
111114
`<rect width="${view}" height="${view}" fill="${design.bg}"/>` +
112-
`<g fill="${design.fg2 ? 'url(#qz-g)' : design.fg}">${mods}</g>` +
115+
`<g fill="${design.fg2 ? `url(#${gradientId})` : design.fg}">${mods}</g>` +
113116
`<g color="${eyeColor}" fill="${eyeColor}">${finders.map(([r, c]) => eye(design.eyeFrame, design.eyeDot, r + q, c + q)).join('')}</g>` +
114117
mark +
115118
'</svg>'

‎src/ui/steps/Style.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,10 @@ export default function Style({ design, onSet, onPreset }: Props) {
9797
<summary className="eyebrow" style={{ cursor: 'pointer' }}>More: gradient, eye colour, quiet zone</summary>
9898
<div className="two" style={{ marginTop: 12 }}>
9999
<div className="field">
100-
<label htmlFor="c-fg2">Gradient end</label>
100+
<label htmlFor="c-fg2">Second colour</label>
101101
<div className="tf">
102102
<input id="c-fg2" type="color" value={design.fg2 ?? design.fg} onChange={e => onSet({ fg2: e.target.value })} />
103-
<input value={design.fg2 ?? ''} placeholder="off" onChange={e => onSet({ fg2: e.target.value || null })} />
103+
<input value={design.fg2 ?? ''} placeholder="no gradient" onChange={e => onSet({ fg2: e.target.value || null })} />
104104
{design.fg2 && (
105105
<button className="btn btn-ghost btn-sm" onClick={() => onSet({ fg2: null })}>Clear</button>
106106
)}

‎test/decode.test.ts‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,15 @@ describe("the app's own defaults", () => {
118118
expect(await verifyScannable(svg, resvgRasterizer, zxingDecoder)).toBe(encodePayload(design))
119119
})
120120
})
121+
122+
describe('gradients', () => {
123+
it('gives each colour pair its own gradient id, so codes on one page do not steal each others', () => {
124+
const a = renderSvg(baseDesign({ fg: '#2B4BF2', fg2: '#8B2BF2' }))
125+
const b = renderSvg(baseDesign({ fg: '#1B6B4A', fg2: '#E78200' }))
126+
const idOf = (svg: string) => svg.match(/<linearGradient id="([^"]+)"/)?.[1]
127+
expect(idOf(a)).toBeTruthy()
128+
expect(idOf(a)).not.toBe(idOf(b))
129+
expect(a).toContain(`url(#${idOf(a)})`)
130+
expect(b).toContain(`url(#${idOf(b)})`)
131+
})
132+
})

0 commit comments

Comments
 (0)