Skip to content

Commit b789781

Browse files
authored
Merge pull request #195 from sveltejs/gh-173
Transitions
2 parents feefe46 + dff59bc commit b789781

File tree

5 files changed

+208
-105
lines changed

5 files changed

+208
-105
lines changed

content/guide/07-element-directives.md

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ You can also access the `event` object in the method call:
6868
The target node can be referenced as `this`, meaning you can do this sort of thing:
6969

7070
```html
71-
<input on:focus='this.select()'>
71+
<input on:focus='this.select()' value='click to select'>
7272
```
7373

7474
### Custom events
@@ -180,6 +180,99 @@ Refs are a convenient way to store a reference to particular DOM nodes or compon
180180
> Since only one element or component can occupy a given `ref`, don't use them in `{{#each ...}}` blocks. It's fine to use them in `{{#if ...}}` blocks however.
181181
182182

183+
### Transitions
184+
185+
Transitions allow elements to enter and leave the DOM gracefully, rather than suddenly appearing and disappearing.
186+
187+
```html
188+
<input type='checkbox' bind:checked=visible> visible
189+
190+
{{#if visible}}
191+
<p transition:fade>fades in and out</p>
192+
{{/if}}
193+
194+
<script>
195+
import { fade } from 'svelte-transitions';
196+
197+
export default {
198+
transitions: { fade }
199+
};
200+
</script>
201+
```
202+
203+
Transitions can have parameters — typically `delay` and `duration`, but often others, depending on the transition in question. For example, here's the `fly` transition from the [svelte-transitions](https://github.com/sveltejs/svelte-transitions) package:
204+
205+
```html
206+
<input type='checkbox' bind:checked=visible> visible
207+
208+
{{#if visible}}
209+
<p transition:fly='{y: 200, duration: 1000}'>flies 200 pixels up, slowly</p>
210+
{{/if}}
211+
212+
<script>
213+
import { fly } from 'svelte-transitions';
214+
215+
export default {
216+
transitions: { fly }
217+
};
218+
</script>
219+
```
220+
221+
An element can have separate `in` and `out` transitions:
222+
223+
```html
224+
<input type='checkbox' bind:checked=visible> visible
225+
226+
{{#if visible}}
227+
<p in:fly='{y: 50}' out:fade>flies up, fades out</p>
228+
{{/if}}
229+
230+
<script>
231+
import { fade, fly } from 'svelte-transitions';
232+
233+
export default {
234+
transitions: { fade, fly }
235+
};
236+
</script>
237+
```
238+
239+
Transitions are simple functions that take a `node` and any provided `parameters` and return an object with the following properties:
240+
241+
* `duration` — how long the transition takes in milliseconds
242+
* `delay` — milliseconds before the transition starts
243+
* `easing` — an [easing function](https://github.com/rollup/eases-jsnext)
244+
* `css` — a function that accepts an argument `t` between 0 and 1 and returns the styles that should be applied at that moment
245+
* `tick` — a function that will be called on every frame, with the same `t` argument, while the transition is in progress
246+
247+
Of these, `duration` is required, as is *either* `css` or `tick`. The rest are optional. Here's how the `fade` transition is implemented, for example:
248+
249+
```html
250+
<input type='checkbox' bind:checked=visible> visible
251+
252+
{{#if visible}}
253+
<p transition:fade>fades in and out</p>
254+
{{/if}}
255+
256+
<script>
257+
export default {
258+
transitions: {
259+
fade(node, { delay = 0, duration = 400 }) {
260+
const o = +getComputedStyle(node).opacity;
261+
262+
return {
263+
delay,
264+
duration,
265+
css: t => `opacity: ${t * o}`
266+
};
267+
}
268+
}
269+
};
270+
</script>
271+
```
272+
273+
> If the `css` option is used, Svelte will create a CSS animation that runs efficiently off the main thread. Therefore if you can achieve an effect using `css` rather than `tick`, you should.
274+
275+
183276
### Two-way binding
184277

185278
It's currently fashionable to avoid two-way binding on the grounds that it creates all sorts of hard-to-debug problems and slows your application down, and that a one-way top-down data flow is 'easier to reason about'. This is in fact high grade nonsense. It's true that two-way binding done *badly* has all sorts of issues, and that very large apps benefit from the discipline of a not permitting deeply nested components to muck about with state that might affect distant parts of the app. But when used correctly, two-way binding simplifies things greatly.

content/guide/08-plugins.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ title: Plugins
44

55
Svelte can be extended with plugins and extra methods.
66

7-
### Transitions
7+
### Transition plugins
88

99
The [svelte-transitions](https://github.com/sveltejs/svelte-transitions) package includes a selection of officially supported transition plugins, such as [fade](https://github.com/sveltejs/svelte-transitions-fade), [fly](https://github.com/sveltejs/svelte-transitions-fly) and [slide](https://github.com/sveltejs/svelte-transitions-slide). You can include them in a component like so:
1010

routes/api/guide/_sections.js

Lines changed: 90 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -30,100 +30,102 @@ function unescape(str) {
3030
return String(str).replace(/&.+?;/g, match => unescaped[match] || match);
3131
}
3232

33-
export default fs
34-
.readdirSync(`content/guide`)
35-
.filter(file => file[0] !== '.' && path.extname(file) === '.md')
36-
.map(file => {
37-
const markdown = fs.readFileSync(`content/guide/${file}`, 'utf-8');
38-
39-
const { content, metadata } = process_markdown(markdown);
40-
41-
// syntax highlighting
42-
let uid = 0;
43-
const replComponents = {};
44-
const replData = {};
45-
const highlighted = {};
46-
47-
const tweaked_content = content.replace(
48-
/```([\w-]+)?\n([\s\S]+?)```/g,
49-
(match, lang, code) => {
50-
if (lang === 'hidden-data') {
51-
replData[uid] = JSON.parse(code);
52-
return '\n\n';
53-
}
33+
export default function() {
34+
return fs
35+
.readdirSync(`content/guide`)
36+
.filter(file => file[0] !== '.' && path.extname(file) === '.md')
37+
.map(file => {
38+
const markdown = fs.readFileSync(`content/guide/${file}`, 'utf-8');
39+
40+
const { content, metadata } = process_markdown(markdown);
41+
42+
// syntax highlighting
43+
let uid = 0;
44+
const replComponents = {};
45+
const replData = {};
46+
const highlighted = {};
47+
48+
const tweaked_content = content.replace(
49+
/```([\w-]+)?\n([\s\S]+?)```/g,
50+
(match, lang, code) => {
51+
if (lang === 'hidden-data') {
52+
replData[uid] = JSON.parse(code);
53+
return '\n\n';
54+
}
5455

55-
const syntax = lang.startsWith('html-nested-')
56-
? 'html'
57-
: langs[lang] || lang;
58-
const { value } = hljs.highlight(syntax, code);
59-
const name = lang.slice(12);
56+
const syntax = lang.startsWith('html-nested-')
57+
? 'html'
58+
: langs[lang] || lang;
59+
const { value } = hljs.highlight(syntax, code);
60+
const name = lang.slice(12);
61+
62+
if (lang.startsWith('html-nested-')) {
63+
replComponents[uid].push({
64+
name,
65+
source: code
66+
});
67+
68+
highlighted[uid] += `\n\n<h2>${name}.html</h2>${value}`;
69+
return '';
70+
} else {
71+
highlighted[++uid] = value;
72+
73+
if (lang === 'html') {
74+
replComponents[uid] = [
75+
{
76+
name: 'App',
77+
source: code
78+
}
79+
];
80+
81+
return `%%${uid}`;
82+
}
83+
84+
return `@@${uid}`;
85+
}
86+
}
87+
);
6088

61-
if (lang.startsWith('html-nested-')) {
62-
replComponents[uid].push({
63-
name,
64-
source: code
89+
const html = marked(tweaked_content)
90+
.replace(/<p>(<a class='open-in-repl'[\s\S]+?)<\/p>/g, '$1')
91+
.replace(/<p>@@(\d+)<\/p>/g, (match, id) => {
92+
return `<pre><code>${highlighted[id]}</code></pre>`;
93+
})
94+
.replace(/<p>%%(\d+)<\/p>/g, (match, id) => {
95+
const components = replComponents[id];
96+
const header = components.length > 1 ? `<h2>App.html</h2>` : '';
97+
const pre = `<pre><code>${header}${highlighted[id]}</code></pre>`;
98+
const data = replData[id] || {};
99+
100+
const json = JSON.stringify({
101+
gist: null,
102+
components,
103+
data
65104
});
66105

67-
highlighted[uid] += `\n\n<h2>${name}.html</h2>${value}`;
68-
return '';
69-
} else {
70-
highlighted[++uid] = value;
106+
const href = `/repl?data=${encodeURIComponent(btoa(json))}`;
107+
return `<a class='open-in-repl' href='${href}'></a>${pre}`;
108+
})
109+
.replace(/^\t+/gm, match => match.split('\t').join(' '));
71110

72-
if (lang === 'html') {
73-
replComponents[uid] = [
74-
{
75-
name: 'App',
76-
source: code
77-
}
78-
];
111+
const subsections = [];
112+
const pattern = /<h3 id="(.+?)">(.+?)<\/h3>/g;
113+
let match;
79114

80-
return `%%${uid}`;
81-
}
115+
while ((match = pattern.exec(html))) {
116+
const slug = match[1];
117+
const title = unescape(
118+
match[2].replace(/<\/?code>/g, '').replace(/\.(\w+)\W.*/, '.$1')
119+
);
82120

83-
return `@@${uid}`;
84-
}
121+
subsections.push({ slug, title });
85122
}
86-
);
87-
88-
const html = marked(tweaked_content)
89-
.replace(/<p>(<a class='open-in-repl'[\s\S]+?)<\/p>/g, '$1')
90-
.replace(/<p>@@(\d+)<\/p>/g, (match, id) => {
91-
return `<pre><code>${highlighted[id]}</code></pre>`;
92-
})
93-
.replace(/<p>%%(\d+)<\/p>/g, (match, id) => {
94-
const components = replComponents[id];
95-
const header = components.length > 1 ? `<h2>App.html</h2>` : '';
96-
const pre = `<pre><code>${header}${highlighted[id]}</code></pre>`;
97-
const data = replData[id] || {};
98-
99-
const json = JSON.stringify({
100-
gist: null,
101-
components,
102-
data
103-
});
104-
105-
const href = `/repl?data=${encodeURIComponent(btoa(json))}`;
106-
return `<a class='open-in-repl' href='${href}'></a>${pre}`;
107-
})
108-
.replace(/^\t+/gm, match => match.split('\t').join(' '));
109-
110-
const subsections = [];
111-
const pattern = /<h3 id="(.+?)">(.+?)<\/h3>/g;
112-
let match;
113-
114-
while ((match = pattern.exec(html))) {
115-
const slug = match[1];
116-
const title = unescape(
117-
match[2].replace(/<\/?code>/g, '').replace(/\.(\w+)\W.*/, '.$1')
118-
);
119-
120-
subsections.push({ slug, title });
121-
}
122123

123-
return {
124-
html,
125-
metadata,
126-
subsections,
127-
slug: file.replace(/^\d+-/, '').replace(/\.md$/, '')
128-
};
129-
});
124+
return {
125+
html,
126+
metadata,
127+
subsections,
128+
slug: file.replace(/^\d+-/, '').replace(/\.md$/, '')
129+
};
130+
});
131+
}

routes/api/guide/contents.js

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
1-
import sections from './_sections.js';
1+
import get_sections from './_sections.js';
22

3-
const contents = JSON.stringify(sections.map(section => {
4-
return {
5-
metadata: section.metadata,
6-
subsections: section.subsections,
7-
slug: section.slug
8-
};
9-
}));
3+
let json;
104

115
export function get(req, res) {
6+
if (!json || process.env.NODE_ENV !== 'production') {
7+
json = JSON.stringify(get_sections().map(section => {
8+
return {
9+
metadata: section.metadata,
10+
subsections: section.subsections,
11+
slug: section.slug
12+
};
13+
}));
14+
}
15+
1216
res.set({
13-
'Content-Type': 'application/json',
14-
'Cache-Control': `max-age=${30 * 60 * 1e3}` // 30 minutes
17+
'Content-Type': 'application/json'
1518
});
16-
res.end(contents);
19+
20+
res.end(json);
1721
}

routes/api/guide/index.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
import sections from './_sections.js';
1+
import get_sections from './_sections.js';
22

3-
const json = JSON.stringify(sections);
3+
let json;
44

55
export function get(req, res) {
6+
if (!json || process.env.NODE_ENV !== 'production') {
7+
json = JSON.stringify(get_sections());
8+
}
9+
610
res.set({
7-
'Content-Type': 'application/json',
8-
'Cache-Control': `max-age=${30 * 60 * 1e3}` // 30 minutes
11+
'Content-Type': 'application/json'
912
});
13+
1014
res.end(json);
1115
}

0 commit comments

Comments
 (0)