mirror of
https://github.com/kevin-DL/sapper-template.git
synced 2026-01-20 21:45:10 +00:00
basic page transitions between /blog and /blog/[slug]
This commit is contained in:
@@ -13,13 +13,16 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"compression": "^1.7.1",
|
"compression": "^1.7.1",
|
||||||
|
"eases-jsnext": "^1.0.10",
|
||||||
"polka": "^0.4.0",
|
"polka": "^0.4.0",
|
||||||
"sapper": "^0.14.0",
|
"sapper": "^0.14.0",
|
||||||
"sirv": "^0.1.1"
|
"sirv": "^0.1.1",
|
||||||
|
"svelte-transitions": "^1.2.0",
|
||||||
|
"yootils": "^0.0.9"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"npm-run-all": "^4.1.2",
|
"npm-run-all": "^4.1.2",
|
||||||
"svelte": "^2.0.0",
|
"svelte": "^2.9.0",
|
||||||
"svelte-loader": "^2.9.0",
|
"svelte-loader": "^2.9.0",
|
||||||
"webpack": "^4.7.0"
|
"webpack": "^4.7.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,23 @@
|
|||||||
<title>{post.title}</title>
|
<title>{post.title}</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<h1>{post.title}</h1>
|
<div style="position: absolute">
|
||||||
|
<h1
|
||||||
|
in:receive="{key: post.title}"
|
||||||
|
out:send="{key: post.title}"
|
||||||
|
>{post.title}</h1>
|
||||||
|
|
||||||
<div class='content'>
|
<div transition:fade="{duration: 100}" class='content'>
|
||||||
{@html post.html}
|
{@html post.html}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
h1 {
|
||||||
|
display: inline-block;
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
By default, CSS is locally scoped to the component,
|
By default, CSS is locally scoped to the component,
|
||||||
and any unused styles are dead-code-eliminated.
|
and any unused styles are dead-code-eliminated.
|
||||||
@@ -45,6 +55,9 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { fade } from 'svelte-transitions';
|
||||||
|
import { send, receive } from './_transitions.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
async preload({ params, query }) {
|
async preload({ params, query }) {
|
||||||
// the `slug` parameter is available because
|
// the `slug` parameter is available because
|
||||||
@@ -57,6 +70,12 @@
|
|||||||
} else {
|
} else {
|
||||||
this.error(res.status, data.message);
|
this.error(res.status, data.message);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
transitions: {
|
||||||
|
fade,
|
||||||
|
send,
|
||||||
|
receive
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
82
routes/blog/_crossfade.js
Normal file
82
routes/blog/_crossfade.js
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import * as eases from 'eases-jsnext';
|
||||||
|
import * as yootils from 'yootils';
|
||||||
|
|
||||||
|
export default function crossfade({ fallback }) {
|
||||||
|
let requested = new Map();
|
||||||
|
let provided = new Map();
|
||||||
|
|
||||||
|
function crossfade(from, node) {
|
||||||
|
const to = node.getBoundingClientRect();
|
||||||
|
console.log({ from, to });
|
||||||
|
const dx = from.left - to.left;
|
||||||
|
const dy = from.top - to.top;
|
||||||
|
|
||||||
|
const dsx = (from.right - from.left) / (to.right - to.left);
|
||||||
|
const dsy = (from.bottom - from.top) / (to.bottom - to.top);
|
||||||
|
|
||||||
|
console.log({ dsx, dsy });
|
||||||
|
|
||||||
|
const sx = yootils.linearScale([0, 1], [dsx, 1]);
|
||||||
|
const sy = yootils.linearScale([0, 1], [dsy, 1]);
|
||||||
|
|
||||||
|
const style = getComputedStyle(node);
|
||||||
|
const transform = style.transform === 'none' ? '' : style.transform;
|
||||||
|
|
||||||
|
return {
|
||||||
|
duration: 4000,
|
||||||
|
easing: eases.quintOut,
|
||||||
|
css: (t, u) => `
|
||||||
|
opacity: ${t};
|
||||||
|
transform-origin: 0 0;
|
||||||
|
transform: ${transform} translate(${u * dx}px,${u * dy}px) scale(${sx(t)}, ${sy(t)});
|
||||||
|
`,
|
||||||
|
tick: (t, u) => {
|
||||||
|
// console.log({
|
||||||
|
// sx: 1 + u * dsx,
|
||||||
|
// sy: 1 + u * dsy,
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
send(node, params) {
|
||||||
|
provided.set(params.key, {
|
||||||
|
rect: node.getBoundingClientRect()
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (requested.has(params.key)) {
|
||||||
|
const { rect } = requested.get(params.key);
|
||||||
|
requested.delete(params.key);
|
||||||
|
|
||||||
|
return crossfade(rect, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the node is disappearing altogether
|
||||||
|
// (i.e. wasn't claimed by the other list)
|
||||||
|
// then we need to supply an outro
|
||||||
|
provided.delete(params.key);
|
||||||
|
return fallback(node, params);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
receive(node, params) {
|
||||||
|
requested.set(params.key, {
|
||||||
|
rect: node.getBoundingClientRect()
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (provided.has(params.key)) {
|
||||||
|
const { rect } = provided.get(params.key);
|
||||||
|
provided.delete(params.key);
|
||||||
|
|
||||||
|
return crossfade(rect, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
requested.delete(params.key);
|
||||||
|
return fallback(node, params);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
75
routes/blog/_move.js
Normal file
75
routes/blog/_move.js
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import * as eases from 'eases-jsnext';
|
||||||
|
import * as yootils from 'yootils';
|
||||||
|
|
||||||
|
export default function move({ fallback }) {
|
||||||
|
let requested = new Map();
|
||||||
|
let provided = new Map();
|
||||||
|
|
||||||
|
function move(from, node) {
|
||||||
|
const to = node.getBoundingClientRect();
|
||||||
|
const dx = from.left - to.left;
|
||||||
|
const dy = from.top - to.top;
|
||||||
|
|
||||||
|
const dsx = (from.right - from.left) / (to.right - to.left);
|
||||||
|
const dsy = (from.bottom - from.top) / (to.bottom - to.top);
|
||||||
|
|
||||||
|
const sx = yootils.linearScale([0, 1], [dsx, 1]);
|
||||||
|
const sy = yootils.linearScale([0, 1], [dsy, 1]);
|
||||||
|
|
||||||
|
const style = getComputedStyle(node);
|
||||||
|
const transform = style.transform === 'none' ? '' : style.transform;
|
||||||
|
|
||||||
|
return {
|
||||||
|
duration: 400,
|
||||||
|
easing: eases.quintOut,
|
||||||
|
css: (t, u) => `
|
||||||
|
transform-origin: 0 0;
|
||||||
|
transform: ${transform} translate(${u * dx}px,${u * dy}px) scale(${sx(t)}, ${sy(t)});
|
||||||
|
`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
send(node, params) {
|
||||||
|
provided.set(params.key, {
|
||||||
|
rect: node.getBoundingClientRect()
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (requested.has(params.key)) {
|
||||||
|
const { rect } = requested.get(params.key);
|
||||||
|
requested.delete(params.key);
|
||||||
|
|
||||||
|
return {
|
||||||
|
duration: 0,
|
||||||
|
css: () => `opacity: 0`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the node is disappearing altogether
|
||||||
|
// (i.e. wasn't claimed by the other list)
|
||||||
|
// then we need to supply an outro
|
||||||
|
provided.delete(params.key);
|
||||||
|
return fallback(node, params);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
receive(node, params) {
|
||||||
|
requested.set(params.key, {
|
||||||
|
rect: node.getBoundingClientRect()
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (provided.has(params.key)) {
|
||||||
|
const { rect } = provided.get(params.key);
|
||||||
|
provided.delete(params.key);
|
||||||
|
|
||||||
|
return move(rect, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
requested.delete(params.key);
|
||||||
|
return fallback(node, params);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
12
routes/blog/_transitions.js
Normal file
12
routes/blog/_transitions.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import move from './_move.js';
|
||||||
|
|
||||||
|
const { send, receive } = move({
|
||||||
|
fallback(node) {
|
||||||
|
return {
|
||||||
|
duration: 0,
|
||||||
|
css: t => `opacity: ${t}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export { send, receive };
|
||||||
@@ -2,31 +2,54 @@
|
|||||||
<title>Blog</title>
|
<title>Blog</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<h1>Recent posts</h1>
|
<div style="position: absolute">
|
||||||
|
<h1 transition:fade="{duration: 100}">Recent posts</h1>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
{#each posts as post}
|
{#each posts as post}
|
||||||
<!-- we're using the non-standard `rel=prefetch` attribute to
|
<!-- we're using the non-standard `rel=prefetch` attribute to
|
||||||
tell Sapper to load the data for the page as soon as
|
tell Sapper to load the data for the page as soon as
|
||||||
the user hovers over the link or taps it, instead of
|
the user hovers over the link or taps it, instead of
|
||||||
waiting for the 'click' event -->
|
waiting for the 'click' event -->
|
||||||
<li><a rel='prefetch' href='blog/{post.slug}'>{post.title}</a></li>
|
<li out:fade={duration:100}>
|
||||||
{/each}
|
<a
|
||||||
</ul>
|
rel='prefetch'
|
||||||
|
href='blog/{post.slug}'
|
||||||
|
in:receive="{key: post.title}"
|
||||||
|
out:send="{key: post.title}"
|
||||||
|
>{post.title}</a>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
ul {
|
ul {
|
||||||
margin: 0 0 1em 0;
|
margin: 0 0 1em 0;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: inline-block;
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { fade } from 'svelte-transitions';
|
||||||
|
import { send, receive } from './_transitions.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
preload({ params, query }) {
|
preload({ params, query }) {
|
||||||
return this.fetch(`blog.json`).then(r => r.json()).then(posts => {
|
return this.fetch(`blog.json`).then(r => r.json()).then(posts => {
|
||||||
return { posts };
|
return { posts };
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
transitions: {
|
||||||
|
fade,
|
||||||
|
send,
|
||||||
|
receive
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -20,7 +20,8 @@ module.exports = {
|
|||||||
options: {
|
options: {
|
||||||
dev: isDev,
|
dev: isDev,
|
||||||
hydratable: true,
|
hydratable: true,
|
||||||
hotReload: true
|
hotReload: true,
|
||||||
|
nestedTransitions: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user