Skip to content

Commit d049e1b

Browse files
committed
fix(potentiometer): usability issues when rotated #70
also mentioned in a comment in #133
1 parent 760fe9b commit d049e1b

File tree

2 files changed

+46
-37
lines changed

2 files changed

+46
-37
lines changed

src/potentiometer-element.stories.ts

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
1-
import { action } from '@storybook/addon-actions';
2-
import { storiesOf } from '@storybook/web-components';
31
import { html } from 'lit';
2+
import { action } from '@storybook/addon-actions';
43
import './potentiometer-element';
54

6-
storiesOf('Potentiometer', module)
7-
.addParameters({ component: 'wokwi-potentiometer' })
8-
.add(
9-
'Potentiometer',
10-
() =>
11-
html`
12-
<wokwi-potentiometer min="0" max="200" @input=${action('input')}></wokwi-potentiometer>
13-
`
14-
);
5+
export default {
6+
title: 'Potentiometer',
7+
component: 'wokwi-potentiometer',
8+
};
9+
10+
const Template = ({ degrees = 0, zoom = 1 }) => html`
11+
<span style="transform: rotate(${degrees}deg); zoom: ${zoom}; display: inline-block;">
12+
<wokwi-potentiometer @input=${action('input')} />
13+
</span>
14+
`;
15+
16+
export const Default = Template.bind({});
17+
Default.args = {};
18+
19+
export const Rotated = Template.bind({});
20+
Rotated.args = { ...Default.args, degrees: 90 };
21+
22+
export const Zoomed = Template.bind({});
23+
Zoomed.args = { ...Default.args, zoom: 1.5 };

src/potentiometer-element.ts

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,24 @@ import { styleMap } from 'lit/directives/style-map.js';
44
import { analog, ElementPin } from './pin';
55
import { clamp } from './utils/clamp';
66

7-
interface Point {
8-
x: number;
9-
y: number;
10-
}
7+
const knobCenter = {
8+
x: 9.91,
9+
y: 8.18,
10+
};
1111

1212
/** The potentiometer SVG is taken from https://freesvg.org/potentiometer and some of the
1313
functions are taken from https://github.com/vitaliy-bobrov/js-rocks knob component */
1414
@customElement('wokwi-potentiometer')
1515
export class PotentiometerElement extends LitElement {
1616
@property({ type: Number }) min = 0;
17-
@property({ type: Number }) max = 100;
17+
@property({ type: Number }) max = 1023;
1818
@property() value = 0;
1919
@property() step = 1;
2020
@property() startDegree = -135;
2121
@property() endDegree = 135;
22-
private center: Point = { x: 0, y: 0 };
22+
2323
private pressed = false;
24+
private pageToKnobMatrix: SVGMatrix | null = null;
2425

2526
readonly pinInfo: ElementPin[] = [
2627
{ name: 'GND', x: 29, y: 68.5, number: 1, signals: [{ type: 'power', signal: 'GND' }] },
@@ -117,8 +118,8 @@ export class PotentiometerElement extends LitElement {
117118
<rect x="5.4" y=".70" width="9.1" height="1.9" fill="#ccdae3" stroke-width=".15" />
118119
<ellipse
119120
id="knob"
120-
cx="9.91"
121-
cy="8.18"
121+
cx=${knobCenter.x}
122+
cy=${knobCenter.y}
122123
rx="7.27"
123124
ry="7.43"
124125
fill="#e4e8eb"
@@ -169,7 +170,11 @@ export class PotentiometerElement extends LitElement {
169170
private down(event: MouseEvent) {
170171
if (event.button === 0 || window.navigator.maxTouchPoints) {
171172
this.pressed = true;
172-
this.updatePotentiometerPosition(event);
173+
174+
event.stopPropagation();
175+
event.preventDefault();
176+
177+
this.updateKnobMatrix();
173178
}
174179
}
175180

@@ -184,38 +189,33 @@ export class PotentiometerElement extends LitElement {
184189
this.pressed = false;
185190
}
186191

187-
private updatePotentiometerPosition(event: MouseEvent | TouchEvent) {
188-
event.stopPropagation();
189-
event.preventDefault();
190-
191-
const potentiometerRect = this.shadowRoot?.querySelector('#knob')?.getBoundingClientRect();
192-
193-
if (potentiometerRect) {
194-
this.center = {
195-
x: window.scrollX + potentiometerRect.left + potentiometerRect.width / 2,
196-
y: window.scrollY + potentiometerRect.top + potentiometerRect.height / 2,
197-
};
198-
}
192+
private updateKnobMatrix() {
193+
const knob = this.shadowRoot?.querySelector<SVGRectElement>('#knob');
194+
this.pageToKnobMatrix = knob?.getScreenCTM()?.inverse() ?? null;
199195
}
200196

201197
private rotateHandler(event: MouseEvent | TouchEvent) {
202198
event.stopPropagation();
203199
event.preventDefault();
204200

201+
if (!this.pageToKnobMatrix) {
202+
return;
203+
}
204+
205205
const isTouch = event.type === 'touchmove';
206206
const pageX = isTouch ? (event as TouchEvent).touches[0].pageX : (event as MouseEvent).pageX;
207207
const pageY = isTouch ? (event as TouchEvent).touches[0].pageY : (event as MouseEvent).pageY;
208-
const x = this.center.x - pageX;
209-
const y = this.center.y - pageY;
208+
const localPosition = new DOMPointReadOnly(pageX, pageY).matrixTransform(this.pageToKnobMatrix);
209+
const x = knobCenter.x - localPosition.x;
210+
const y = knobCenter.y - localPosition.y;
210211
let deg = Math.round((Math.atan2(y, x) * 180) / Math.PI);
211-
212-
if (deg <= 0) {
212+
if (deg < 0) {
213213
deg += 360;
214214
}
215215

216216
deg -= 90;
217217

218-
if (x > 0 && y <= 0) {
218+
if (x > 0 && y <= 0 && deg > 0) {
219219
deg -= 360;
220220
}
221221

0 commit comments

Comments
 (0)