Skip to content

Commit 96a75e0

Browse files
committed
avoid scroll glitches in Safari
1 parent 1b272dd commit 96a75e0

File tree

1 file changed

+37
-39
lines changed

1 file changed

+37
-39
lines changed

docs/reactivity.md

Lines changed: 37 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -162,22 +162,18 @@ const name = Generators.observe((notify) => {
162162
});
163163
```
164164

165-
As another example, here is using `Generators.observe` to expose the current pointer coordinates:
165+
As another example, here is using `Generators.observe` to expose the current pointer coordinates as `pointer` = <span style="font-variant-numeric: tabular-nums;">[${pointer.join(", ")}]</span>:
166166

167167
```js echo
168-
const pointer = Generators.observe((change) => {
169-
const pointermoved = (event) => change([event.clientX, event.clientY]);
168+
const pointer = Generators.observe((notify) => {
169+
const pointermoved = (event) => notify([event.clientX, event.clientY]);
170170
addEventListener("pointermove", pointermoved);
171-
change([0, 0]);
171+
notify([0, 0]);
172172
return () => removeEventListener("pointermove", pointermoved);
173173
});
174174
```
175175

176-
```js echo
177-
pointer.map(Math.round) // try moving your mouse
178-
```
179-
180-
And here’s a generator `j` that increments once a second, defined directly by an immediately-invoked async generator function.
176+
And here’s a generator `j` = <span style="font-variant-numeric: tabular-nums;">${j}</div> that increments once a second, defined directly by an immediately-invoked async generator function.
181177

182178
```js echo
183179
const j = (async function* () {
@@ -188,11 +184,7 @@ const j = (async function* () {
188184
})();
189185
```
190186

191-
```js echo
192-
j
193-
```
194-
195-
If a generator does not explicitly `await`, it will yield once every [animation frame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame), typically 60 times per second. Generators also automatically pause when the page is put in a background tab.
187+
If a generator does not explicitly `await`, as `i` = <span style="font-variant-numeric: tabular-nums;">${i}</div> below, it will yield once every [animation frame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame), typically 60 times per second. Generators also automatically pause when the page is put in a background tab.
196188

197189
```js echo
198190
const i = (function* () {
@@ -202,47 +194,53 @@ const i = (function* () {
202194
})();
203195
```
204196

205-
```js echo
206-
i
207-
```
208-
209197
As you might imagine, you can use such a generator to drive an animation. A generator is typically easier than a `requestAnimationFrame` loop because the animation is declarative — the code runs automatically whenever `i` changes — and because you don’t have to handle [invalidation](#invalidation) to terminate the loop.
210198

211-
```svg echo
212-
<svg width="640" height="32">
213-
<rect fill="#4269d0" width="32" height="32" x=${(i % (640 + 32)) - 32}></rect>
214-
</svg>
199+
<canvas id="canvas0" width="640" height="30" style="max-width: 100%; height: 30px;"></canvas>
200+
201+
```js
202+
const context0 = canvas0.getContext("2d");
215203
```
216204

217-
You can also use a generator to stream live data. Here is a WebSocket that listens for the current price of Bitcoin, keeping the last minute of data in memory.
205+
```js echo
206+
context0.clearRect(0, 0, canvas0.width, canvas0.height);
207+
context0.fillStyle = "#4269d0";
208+
context0.fillRect((i % (640 + 32)) - 32, 0, 32, 32);
209+
```
210+
211+
You can also use a generator to stream live data. Here is a WebSocket that reports the current price of Bitcoin via Unicorn Data Services.
218212

219213
```js echo
220214
const socket = new WebSocket("wss://ws.eodhistoricaldata.com/ws/crypto?api_token=demo");
221215
invalidation.then(() => socket.close());
222216
socket.addEventListener("open", () => socket.send(JSON.stringify({action: "subscribe", symbols: "BTC-USD"})));
223-
const messages = Generators.observe((change) => {
224-
const messages = [];
225-
const duration = messages.duration = 60_000;
217+
const btc = Generators.observe((notify) => {
218+
let currentValue;
226219
const messaged = (event) => {
227220
const m = JSON.parse(event.data);
228-
const t = m.t;
229-
if (t == null) return;
230-
while ((t - messages[0]?.t) > duration) messages.shift();
231-
messages.push(m);
232-
change(messages);
221+
const v = +m.p;
222+
if (isNaN(v) || v === currentValue) return;
223+
notify((currentValue = v));
233224
};
234225
socket.addEventListener("message", messaged);
235226
return () => socket.removeEventListener("message", messaged);
236227
});
237228
```
238229

239-
```js echo
240-
Plot.plot({
241-
marginLeft: 50,
242-
x: {type: "time", domain: [now - messages.duration, now]},
243-
y: {type: "linear", label: "price", inset: 10},
244-
marks: [Plot.lineY(messages, {x: "t", y: "p", curve: "step", clip: true})]
245-
})
230+
<div class="grid grid-cols-4">
231+
<div class="card">
232+
<h2>Bitcoin price (USD/BTC)</h2>
233+
<div class="big">${btc.toLocaleString("en-US", {style: "currency", currency: "USD"})}</div>
234+
</div>
235+
</div>
236+
237+
```html run=false
238+
<div class="grid grid-cols-4">
239+
<div class="card">
240+
<h2>Bitcoin price (USD/BTC)</h2>
241+
<div class="big">${btc.toLocaleString("en-US", {style: "currency", currency: "USD"})}</div>
242+
</div>
243+
</div>
246244
```
247245

248246
## Inputs
@@ -288,7 +286,7 @@ const penguin = view(Plot.dot(penguins, {x: "culmen_length_mm", y: "flipper_leng
288286
```
289287

290288
```js echo
291-
penguin
289+
penguin // try hovering the chart above
292290
```
293291

294292
In the future, Plot will support more interaction methods, including brushing. Please upvote [#5](https://github.com/observablehq/plot/issues/5) if you are interested in this feature.

0 commit comments

Comments
 (0)