mirror of https://github.com/sveltejs/svelte
parent
8669c76921
commit
4f26363fe0
@ -1,140 +1,153 @@
|
|||||||
<script>
|
<script>
|
||||||
import { quintOut } from 'svelte/easing';
|
import { crossfade, scale } from 'svelte/transition';
|
||||||
import crossfade from './crossfade.js'; // TODO put this in svelte/transition!
|
import images from './images.js';
|
||||||
|
|
||||||
const { send, receive } = crossfade({
|
const [send, receive] = crossfade({
|
||||||
fallback(node, params) {
|
duration: 200,
|
||||||
const style = getComputedStyle(node);
|
fallback: scale
|
||||||
const transform = style.transform === 'none' ? '' : style.transform;
|
|
||||||
|
|
||||||
return {
|
|
||||||
duration: 600,
|
|
||||||
easing: quintOut,
|
|
||||||
css: t => `
|
|
||||||
transform: ${transform} scale(${t});
|
|
||||||
opacity: ${t}
|
|
||||||
`
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let todos = [
|
let selected = null;
|
||||||
{ id: 1, done: false, description: 'write some docs' },
|
let loading = null;
|
||||||
{ id: 2, done: false, description: 'start writing JSConf talk' },
|
|
||||||
{ id: 3, done: true, description: 'buy some milk' },
|
|
||||||
{ id: 4, done: false, description: 'mow the lawn' },
|
|
||||||
{ id: 5, done: false, description: 'feed the turtle' },
|
|
||||||
{ id: 6, done: false, description: 'fix some bugs' },
|
|
||||||
];
|
|
||||||
|
|
||||||
let uid = todos.length + 1;
|
|
||||||
|
|
||||||
function add(input) {
|
|
||||||
const todo = {
|
|
||||||
id: uid++,
|
|
||||||
done: false,
|
|
||||||
description: input.value
|
|
||||||
};
|
|
||||||
|
|
||||||
todos = [todo, ...todos];
|
const ASSETS = `https://svelte-assets.surge.sh/crossfade`;
|
||||||
input.value = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function remove(todo) {
|
const load = image => {
|
||||||
todos = todos.filter(t => t !== todo);
|
const timeout = setTimeout(() => loading = image, 100);
|
||||||
}
|
|
||||||
|
const img = new Image();
|
||||||
|
|
||||||
|
img.onload = () => {
|
||||||
|
selected = image;
|
||||||
|
clearTimeout(timeout);
|
||||||
|
loading = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
img.src = `${ASSETS}/${image.id}.jpg`;
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="phone">
|
||||||
|
<h1>Photo gallery</h1>
|
||||||
|
|
||||||
|
<div class="grid">
|
||||||
|
{#each images as image}
|
||||||
|
<div class="square">
|
||||||
|
{#if selected !== image}
|
||||||
|
<button
|
||||||
|
style="background-color: {image.color};"
|
||||||
|
on:click="{() => load(image)}"
|
||||||
|
in:receive={{key:image.id}}
|
||||||
|
out:send={{key:image.id}}
|
||||||
|
>{loading === image ? '...' : image.id}</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if selected}
|
||||||
|
{#await selected then d}
|
||||||
|
<div class="photo" in:receive={{key:d.id}} out:send={{key:d.id}}>
|
||||||
|
<img
|
||||||
|
alt={d.alt}
|
||||||
|
src="{ASSETS}/{d.id}.jpg"
|
||||||
|
on:click="{() => selected = null}"
|
||||||
|
>
|
||||||
|
|
||||||
|
<p class='credit'>
|
||||||
|
<a target="_blank" href="https://www.flickr.com/photos/{d.path}">via Flickr</a> –
|
||||||
|
<a target="_blank" href={d.license.url}>{d.license.name}</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{/await}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.new-todo {
|
.container {
|
||||||
font-size: 1.4em;
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 2em 0 1em 0;
|
height: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.board {
|
.phone {
|
||||||
max-width: 36em;
|
position: relative;
|
||||||
margin: 0 auto;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 52vmin;
|
||||||
|
height: 76vmin;
|
||||||
|
border: 2vmin solid #ccc;
|
||||||
|
border-bottom-width: 10vmin;
|
||||||
|
padding: 3vmin;
|
||||||
|
border-radius: 2vmin;
|
||||||
}
|
}
|
||||||
|
|
||||||
.left, .right {
|
h1 {
|
||||||
float: left;
|
font-weight: 300;
|
||||||
width: 50%;
|
text-transform: uppercase;
|
||||||
padding: 0 1em 0 0;
|
font-size: 5vmin;
|
||||||
box-sizing: border-box;
|
margin: 0.2em 0 0.5em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
.grid {
|
||||||
font-size: 2em;
|
display: grid;
|
||||||
font-weight: 200;
|
flex: 1;
|
||||||
user-select: none;
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
grid-template-rows: repeat(4, 1fr);
|
||||||
|
grid-gap: 2vmin;
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
button {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
color: white;
|
||||||
|
font-size: 5vmin;
|
||||||
|
border: none;
|
||||||
|
margin: 0;
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo, img {
|
||||||
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
display: block;
|
width: 100%;
|
||||||
font-size: 1em;
|
height: 100%;
|
||||||
line-height: 1;
|
overflow: hidden;
|
||||||
padding: 0.5em;
|
|
||||||
margin: 0 auto 0.5em auto;
|
|
||||||
border-radius: 2px;
|
|
||||||
background-color: #eee;
|
|
||||||
user-select: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input { margin: 0 }
|
.photo {
|
||||||
|
display: flex;
|
||||||
.right label {
|
align-items: stretch;
|
||||||
background-color: rgb(180,240,100);
|
justify-content: flex-end;
|
||||||
|
flex-direction: column;
|
||||||
|
will-change: transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
img {
|
||||||
float: right;
|
object-fit: cover;
|
||||||
height: 1em;
|
cursor: pointer;
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 0 0.5em;
|
|
||||||
line-height: 1;
|
|
||||||
background-color: transparent;
|
|
||||||
border: none;
|
|
||||||
color: rgb(170,30,30);
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.2s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
label:hover button {
|
.credit {
|
||||||
opacity: 1;
|
text-align: right;
|
||||||
|
font-size: 2.5vmin;
|
||||||
|
padding: 1em;
|
||||||
|
margin: 0;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
opacity: 0.6;
|
||||||
|
background: rgba(0,0,0,0.4);
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class='board'>
|
|
||||||
<input class="new-todo" placeholder="what needs to be done?" on:keydown="{event => event.which === 13 && add(event.target)}">
|
|
||||||
|
|
||||||
<div class='left'>
|
|
||||||
<h2>todo</h2>
|
|
||||||
{#each todos.filter(t => !t.done) as todo (todo.id)}
|
|
||||||
<label
|
|
||||||
in:receive="{{key: todo.id}}"
|
|
||||||
out:send="{{key: todo.id}}"
|
|
||||||
>
|
|
||||||
<input type=checkbox bind:checked={todo.done}>
|
|
||||||
{todo.description}
|
|
||||||
<button on:click="{() => remove(todo)}">x</button>
|
|
||||||
</label>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class='right'>
|
.credit a, .credit a:visited {
|
||||||
<h2>done</h2>
|
color: white;
|
||||||
{#each todos.filter(t => t.done) as todo (todo.id)}
|
}
|
||||||
<label
|
</style>
|
||||||
in:receive="{{key: todo.id}}"
|
|
||||||
out:send="{{key: todo.id}}"
|
|
||||||
>
|
|
||||||
<input type=checkbox bind:checked={todo.done}>
|
|
||||||
{todo.description}
|
|
||||||
<button on:click="{() => remove(todo)}">x</button>
|
|
||||||
</label>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,65 +0,0 @@
|
|||||||
import { quintOut } from 'svelte/easing';
|
|
||||||
|
|
||||||
export default function crossfade({ send, receive, fallback }) {
|
|
||||||
let requested = new Map();
|
|
||||||
let provided = new Map();
|
|
||||||
|
|
||||||
function crossfade(from, node) {
|
|
||||||
const to = node.getBoundingClientRect();
|
|
||||||
const dx = from.left - to.left;
|
|
||||||
const dy = from.top - to.top;
|
|
||||||
|
|
||||||
const style = getComputedStyle(node);
|
|
||||||
const transform = style.transform === 'none' ? '' : style.transform;
|
|
||||||
|
|
||||||
return {
|
|
||||||
duration: 400,
|
|
||||||
easing: quintOut,
|
|
||||||
css: (t, u) => `
|
|
||||||
opacity: ${t};
|
|
||||||
transform: ${transform} translate(${u * dx}px,${u * dy}px);
|
|
||||||
`
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
@ -0,0 +1,102 @@
|
|||||||
|
const BY = {
|
||||||
|
name: 'CC BY 2.0',
|
||||||
|
url: 'https://creativecommons.org/licenses/by/2.0/'
|
||||||
|
};
|
||||||
|
|
||||||
|
const BY_SA = {
|
||||||
|
name: 'CC BY-SA 2.0',
|
||||||
|
url: 'https://creativecommons.org/licenses/by-sa/2.0/'
|
||||||
|
};
|
||||||
|
|
||||||
|
const BY_ND = {
|
||||||
|
name: 'CC BY-ND 2.0',
|
||||||
|
url: 'https://creativecommons.org/licenses/by-nd/2.0/'
|
||||||
|
};
|
||||||
|
|
||||||
|
// via http://labs.tineye.com/multicolr
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
color: '#001f3f',
|
||||||
|
id: '1',
|
||||||
|
alt: 'Crepuscular rays',
|
||||||
|
path: '43428526@N03/7863279376',
|
||||||
|
license: BY
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#0074D9',
|
||||||
|
id: '2',
|
||||||
|
alt: 'Lapland winter scene',
|
||||||
|
path: '25507134@N00/6527537485',
|
||||||
|
license: BY
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#7FDBFF',
|
||||||
|
id: '3',
|
||||||
|
alt: 'Jellyfish',
|
||||||
|
path: '37707866@N00/3354331318',
|
||||||
|
license: BY
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#39CCCC',
|
||||||
|
id: '4',
|
||||||
|
alt: 'A man scuba diving',
|
||||||
|
path: '32751486@N00/4608886209',
|
||||||
|
license: BY_SA
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#3D9970',
|
||||||
|
id: '5',
|
||||||
|
alt: 'Underwater scene',
|
||||||
|
path: '25483059@N08/5548569010',
|
||||||
|
license: BY
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#2ECC40',
|
||||||
|
id: '6',
|
||||||
|
alt: 'Ferns',
|
||||||
|
path: '8404611@N06/2447470760',
|
||||||
|
license: BY
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#01FF70',
|
||||||
|
id: '7',
|
||||||
|
alt: 'Posters in a bar',
|
||||||
|
path: '33917831@N00/114428206',
|
||||||
|
license: BY_SA
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#FFDC00',
|
||||||
|
id: '8',
|
||||||
|
alt: 'Daffodil',
|
||||||
|
path: '46417125@N04/4818617089',
|
||||||
|
license: BY_ND
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#FF851B',
|
||||||
|
id: '9',
|
||||||
|
alt: 'Dust storm in Sydney',
|
||||||
|
path: '56068058@N00/3945496657',
|
||||||
|
license: BY
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#FF4136',
|
||||||
|
id: '10',
|
||||||
|
alt: 'Postbox',
|
||||||
|
path: '31883499@N05/4216820032',
|
||||||
|
license: BY
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#85144b',
|
||||||
|
id: '11',
|
||||||
|
alt: 'Fireworks',
|
||||||
|
path: '8484971@N07/2625506561',
|
||||||
|
license: BY_ND
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#B10DC9',
|
||||||
|
id: '12',
|
||||||
|
alt: 'The Stereophonics',
|
||||||
|
path: '58028312@N00/5385464371',
|
||||||
|
license: BY_ND
|
||||||
|
}
|
||||||
|
];
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 8.8 KiB |
Loading…
Reference in new issue