Skip to content

Commit d52201b

Browse files
author
Rich Harris
committed
update onMount exercise
1 parent 0c9060a commit d52201b

File tree

5 files changed

+100
-71
lines changed

5 files changed

+100
-71
lines changed

content/tutorial/01-svelte/07-lifecycle/01-onmount/README.md

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,55 @@
22
title: onMount
33
---
44

5-
> The images in this exercise don't currently work. You can switch to the old tutorial instead: https://svelte.dev/tutorial/onmount
5+
Every component has a _lifecycle_ that starts when it is created, and ends when it is destroyed. There are a handful of functions that allow you to run code at key moments during that lifecycle. The one you'll use most frequently is `onMount`, which runs after the component is first rendered to the DOM.
66

7-
Every component has a _lifecycle_ that starts when it is created, and ends when it is destroyed. There are a handful of functions that allow you to run code at key moments during that lifecycle.
7+
In this exercise, we have a `<canvas>` that we'd like to animate, using the `paint` function in `gradient.js`. Begin by importing the function from `svelte`:
88

9-
The one you'll use most frequently is `onMount`, which runs after the component is first rendered to the DOM.
9+
```svelte
10+
/// file: App.svelte
11+
<script>
12+
+++import { onMount } from 'svelte';+++
13+
import { paint } from './gradient.js';
14+
</script>
15+
```
1016

11-
We'll add an `onMount` handler that loads some data over the network:
17+
Then, add a function that runs when the component mounts:
1218

1319
```svelte
1420
/// file: App.svelte
1521
<script>
1622
import { onMount } from 'svelte';
23+
import { paint } from './gradient.js';
1724
18-
let photos = [];
25+
+++ onMount(() => {
26+
const canvas = document.querySelector('canvas')
27+
const context = canvas.getContext('2d');+++
1928
20-
onMount(async () => {
21-
const res = await fetch(`https://jsonplaceholder.typicode.com/photos?_limit=20`);
22-
photos = await res.json();
23-
});
29+
+++ requestAnimationFrame(function loop(t) {
30+
requestAnimationFrame(loop);
31+
paint(context, t);
32+
});
33+
});+++
2434
</script>
2535
```
2636

27-
> It's recommended to put the `fetch` in `onMount` rather than at the top level of the `<script>` because of server-side rendering (SSR). With the exception of `onDestroy`, lifecycle functions don't run during SSR, which means we can avoid fetching data that should be loaded lazily once the component has been mounted in the DOM.
37+
So far so good — you should see gently undulating colours in the shape of the Svelte logo. But there's one problem — the loop will continue even after the component has been destroyed. To fix that, we need to return a cleanup function from `onMount`:
38+
39+
```js
40+
/// file: App.svelte
41+
onMount(() => {
42+
const canvas = document.querySelector('canvas')
43+
const context = canvas.getContext('2d');
44+
45+
+++let frame =+++ requestAnimationFrame(function loop(t) {
46+
+++frame =+++ requestAnimationFrame(loop);
47+
paint(context, t);
48+
});
2849

29-
Lifecycle functions must be called while the component is initialising so that the callback is bound to the component instance — not (say) in a `setTimeout`.
50+
loop();
3051

31-
If the `onMount` callback returns a function, that function will be called when the component is destroyed.
52+
+++ return () => {
53+
cancelAnimationFrame(frame);
54+
};+++
55+
});
56+
```
Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,23 @@
11
<script>
2-
let photos = [];
2+
import { paint } from './gradient.js';
33
</script>
44

5-
<h1>Photo album</h1>
6-
7-
<div class="photos">
8-
{#each photos as photo}
9-
<figure>
10-
<img
11-
src={photo.thumbnailUrl}
12-
alt={photo.title}
13-
/>
14-
<figcaption>{photo.title}</figcaption>
15-
</figure>
16-
{:else}
17-
<!-- this block renders when photos.length === 0 -->
18-
<p>loading...</p>
19-
{/each}
20-
</div>
5+
<canvas
6+
width={32}
7+
height={32}
8+
/>
219

2210
<style>
23-
.photos {
24-
width: 100%;
25-
display: grid;
26-
grid-template-columns: repeat(5, 1fr);
27-
grid-gap: 8px;
28-
}
29-
30-
figure,
31-
img {
11+
canvas {
12+
position: fixed;
13+
left: 0;
14+
top: 0;
3215
width: 100%;
33-
margin: 0;
16+
height: 100%;
17+
background-color: #666;
18+
mask: url(./svelte-logo-mask.svg) 50% 50% no-repeat;
19+
mask-size: 60vmin;
20+
-webkit-mask: url(./svelte-logo-mask.svg) 50% 50% no-repeat;
21+
-webkit-mask-size: 60vmin;
3422
}
3523
</style>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
export function paint(context, t) {
2+
const { width, height } = context.canvas;
3+
const imageData = context.getImageData(0, 0, width, height);
4+
5+
for (let p = 0; p < imageData.data.length; p += 4) {
6+
const i = p / 4;
7+
const x = i % width;
8+
const y = (i / width) >>> 0;
9+
10+
const red = 64 + (128 * x) / width + 64 * Math.sin(t / 1000);
11+
const green = 64 + (128 * y) / height + 64 * Math.cos(t / 1000);
12+
const blue = 128;
13+
14+
imageData.data[p + 0] = red;
15+
imageData.data[p + 1] = green;
16+
imageData.data[p + 2] = blue;
17+
imageData.data[p + 3] = 255;
18+
}
19+
20+
context.putImageData(imageData, 0, 0);
21+
}
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,38 @@
11
<script>
22
import { onMount } from 'svelte';
3+
import { paint } from './gradient.js';
34
4-
let photos = [];
5+
onMount(() => {
6+
const canvas = document.querySelector('canvas')
7+
const context = canvas.getContext('2d');
58
6-
onMount(async () => {
7-
const res = await fetch(
8-
`https://jsonplaceholder.typicode.com/photos?_limit=20`
9-
);
10-
photos = await res.json();
9+
let frame = requestAnimationFrame(function loop(t) {
10+
frame = requestAnimationFrame(loop);
11+
paint(context, t);
12+
});
13+
14+
return () => {
15+
cancelAnimationFrame(frame);
16+
};
1117
});
1218
</script>
1319

14-
<h1>Photo album</h1>
15-
16-
<div class="photos">
17-
{#each photos as photo}
18-
<figure>
19-
<img
20-
src={photo.thumbnailUrl}
21-
alt={photo.title}
22-
/>
23-
<figcaption>{photo.title}</figcaption>
24-
</figure>
25-
{:else}
26-
<!-- this block renders when photos.length === 0 -->
27-
<p>loading...</p>
28-
{/each}
29-
</div>
20+
<canvas
21+
width={32}
22+
height={32}
23+
/>
3024

3125
<style>
32-
.photos {
33-
width: 100%;
34-
display: grid;
35-
grid-template-columns: repeat(5, 1fr);
36-
grid-gap: 8px;
37-
}
38-
39-
figure,
40-
img {
26+
canvas {
27+
position: fixed;
28+
left: 0;
29+
top: 0;
4130
width: 100%;
42-
margin: 0;
31+
height: 100%;
32+
background-color: #666;
33+
mask: url(./svelte-logo-mask.svg) 50% 50% no-repeat;
34+
mask-size: 60vmin;
35+
-webkit-mask: url(./svelte-logo-mask.svg) 50% 50% no-repeat;
36+
-webkit-mask-size: 60vmin;
4337
}
4438
</style>

0 commit comments

Comments
 (0)