跳到主要內容

Context API 提供了一種機制,讓元件之間可以「溝通」,而無需將資料和函式作為 props 傳遞,或發送大量事件。這是一項進階功能,但很有用。在這個練習中,我們將使用 Context API 重現 George Nees 的 Schotter,他是生成藝術的先驅之一。

Canvas.svelte 內部,有一個 addItem 函式,可以將項目新增到畫布中。我們可以透過 setContext,讓 <Canvas> 內部的元件(如 <Square>)可以使用它

畫布
import { setContext } from 'svelte';
import { SvelteSet } from 'svelte/reactivity';

let { width = 100, height = 100, children } = $props();

let canvas;
let items = new SvelteSet();

setContext('canvas', { addItem });

function addItem(fn) {
	$effect(() => {
		items.add(fn);
		return () => items.delete(fn);
	});
}

在子元件內部,我們現在可以使用 getContext 取得 context

正方形
import { getContext } from 'svelte';

let { x, y, size, rotate } = $props();

getContext('canvas').addItem(draw);

到目前為止,還很...無聊。讓我們為網格添加一些隨機性

應用程式
<div class="container">
	<Canvas width={800} height={1200}>
		{#each Array(12) as _, c}
			{#each Array(22) as _, r}
				<Square
					x={180 + c * 40 + jitter(r * 2)}
					y={180 + r * 40 + jitter(r * 2)}
					size={40}
					rotate={jitter(r * 0.05)}
				/>
			{/each}
		{/each}
	</Canvas>
</div>

setContextgetContext 必須在元件初始化期間呼叫,這樣才能正確綁定 context。鍵值 (在這個例子中是 'canvas') 可以是任何你喜歡的值,包括非字串,這對於控制誰可以存取 context 非常有用。

你的 context 物件可以包含任何內容,包括響應式狀態。這允許你將隨著時間變化的值傳遞給子元件

// in a parent component
import { setContext } from 'svelte';

let context = $state({...});
setContext('my-context', context);
// in a child component
import { getContext } from 'svelte';

const context = getContext('my-context');

在 GitHub 上編輯此頁面

上一頁 下一頁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<script>
	import Canvas from './Canvas.svelte';
	import Square from './Square.svelte';
 
	// we use a seeded random number generator to get consistent jitter
	let seed = 1;
 
	function random() {
		seed *= 16807;
		seed %= 2147483647;
		return (seed - 1) / 2147483646;
	}
 
	function jitter(amount) {
		return amount * (random() - 0.5);
	}
</script>
 
<div class="container">
	<Canvas width={800} height={1200}>
		{#each Array(12) as _, c}
			{#each Array(22) as _, r}
				<Square
					x={180 + c * 40}
					y={180 + r * 40}
					size={40}
				/>
			{/each}
		{/each}
	</Canvas>
</div>
 
<style>
	.container {
		height: 100%;
		aspect-ratio: 2 / 3;
		margin: 0 auto;
		background: rgb(224, 219, 213);
		filter: drop-shadow(0.5em 0.5em 1em rgba(0, 0, 0, 0.1));
	}
</style>