Skip to content

Commit 1847655

Browse files
committed
fix(potentiometer): broken in Firefox
see #133
1 parent 46cf4df commit 1847655

File tree

3 files changed

+50
-4
lines changed

3 files changed

+50
-4
lines changed

src/potentiometer-element.stories.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ export default {
77
component: 'wokwi-potentiometer',
88
};
99

10-
const Template = ({ degrees = 0, zoom = 1 }) => html`
11-
<span style="transform: rotate(${degrees}deg); zoom: ${zoom}; display: inline-block;">
10+
const Template = ({ transform = '' }) => html`
11+
<span style="transform: ${transform}; display: inline-block;">
1212
<wokwi-potentiometer @input=${action('input')} />
1313
</span>
1414
`;
@@ -17,7 +17,7 @@ export const Default = Template.bind({});
1717
Default.args = {};
1818

1919
export const Rotated = Template.bind({});
20-
Rotated.args = { ...Default.args, degrees: 90 };
20+
Rotated.args = { ...Default.args, transform: 'rotate(90deg)' };
2121

2222
export const Zoomed = Template.bind({});
23-
Zoomed.args = { ...Default.args, zoom: 1.5 };
23+
Zoomed.args = { ...Default.args, transform: 'translate(25px, 25px) scale(1.5)' };

src/potentiometer-element.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { customElement, property } from 'lit/decorators.js';
33
import { styleMap } from 'lit/directives/style-map.js';
44
import { analog, ElementPin } from './pin';
55
import { clamp } from './utils/clamp';
6+
import { calculateBoundingRect } from './utils/geometry';
67

78
const knobCenter = {
89
x: 9.91,
@@ -152,6 +153,7 @@ export class PotentiometerElement extends LitElement {
152153
</g>
153154
<ellipse cx="9.95" cy="8.06" rx="6.60" ry="6.58" fill="#c3c2c3" stroke-width=".15" />
154155
<rect id="rotating" x="10" y="2" width=".42" height="3.1" stroke-width=".15" />
156+
<rect x="0" y="9.5" width="1" height="1" fill="none" id="firefox-workaround" />
155157
</svg>
156158
`;
157159
}
@@ -192,6 +194,39 @@ export class PotentiometerElement extends LitElement {
192194
private updateKnobMatrix() {
193195
const knob = this.shadowRoot?.querySelector<SVGRectElement>('#knob');
194196
this.pageToKnobMatrix = knob?.getScreenCTM()?.inverse() ?? null;
197+
198+
if (navigator.userAgent.indexOf('Firefox') >= 0) {
199+
// Firefox's getScreenCTM() is broken: https://bugzilla.mozilla.org/show_bug.cgi?id=1610093
200+
const firefoxWorkaround =
201+
this.shadowRoot?.querySelector<SVGRectElement>('#firefox-workaround');
202+
const boundingRect = firefoxWorkaround?.getBoundingClientRect();
203+
const svgRect = firefoxWorkaround?.ownerSVGElement?.getBoundingClientRect();
204+
if (!boundingRect || !svgRect) {
205+
return;
206+
}
207+
208+
const cx = svgRect.x + svgRect.width / 2;
209+
const cy = svgRect.y + svgRect.height / 2;
210+
const deltaX = cx - (boundingRect.x + boundingRect.width / 2);
211+
const deltaY = cy - (boundingRect.y + boundingRect.height / 2);
212+
const angle = (Math.atan2(deltaY, deltaX) / Math.PI) * 180;
213+
const rotation = new DOMMatrix().rotate(angle);
214+
const rotatedRect = calculateBoundingRect(new DOMRect(0, 9.5, 1, 1), rotation);
215+
const sx = rotatedRect.width / boundingRect.width;
216+
const sy = rotatedRect.height / boundingRect.height;
217+
this.pageToKnobMatrix = rotation
218+
.inverse()
219+
.multiply(
220+
new DOMMatrix([
221+
sx,
222+
0,
223+
0,
224+
sy,
225+
rotatedRect.left - boundingRect.left * sx,
226+
rotatedRect.top - boundingRect.top * sy,
227+
])
228+
);
229+
}
195230
}
196231

197232
private rotateHandler(event: MouseEvent | TouchEvent) {

src/utils/geometry.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export function calculateBoundingRect(rect: DOMRect, transform: DOMMatrix) {
2+
const topLeft = transform.transformPoint({ x: rect.left, y: rect.top });
3+
const topRight = transform.transformPoint({ x: rect.right, y: rect.top });
4+
const bottomLeft = transform.transformPoint({ x: rect.left, y: rect.bottom });
5+
const bottomRight = transform.transformPoint({ x: rect.right, y: rect.bottom });
6+
const minX = Math.min(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x);
7+
const minY = Math.min(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y);
8+
const maxX = Math.max(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x);
9+
const maxY = Math.max(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y);
10+
return new DOMRect(minX, minY, maxX - minX, maxY - minY);
11+
}

0 commit comments

Comments
 (0)