mirror of https://github.com/sveltejs/svelte
commit
5648269485
@ -1,7 +1,14 @@
|
||||
src/shared
|
||||
shared.js
|
||||
store.js
|
||||
test/test.js
|
||||
test/setup.js
|
||||
**/_actual.js
|
||||
**/expected.js
|
||||
test/*/samples/*/output.js
|
||||
node_modules
|
||||
|
||||
# output files
|
||||
animate/*.js
|
||||
esing/*.js
|
||||
internal/*.js
|
||||
motion/*.js
|
||||
store/*.js
|
||||
transition/*.js
|
||||
index.js
|
||||
compiler.js
|
||||
|
@ -0,0 +1,99 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
rules: {
|
||||
indent: 'off',
|
||||
'no-unused-vars': 'off',
|
||||
semi: [2, 'always'],
|
||||
'keyword-spacing': [2, { before: true, after: true }],
|
||||
'space-before-blocks': [2, 'always'],
|
||||
'no-mixed-spaces-and-tabs': [2, 'smart-tabs'],
|
||||
'no-cond-assign': 0,
|
||||
'object-shorthand': [2, 'always'],
|
||||
'no-const-assign': 2,
|
||||
'no-class-assign': 2,
|
||||
'no-this-before-super': 2,
|
||||
'no-var': 2,
|
||||
'no-unreachable': 2,
|
||||
'valid-typeof': 2,
|
||||
'quote-props': [2, 'as-needed'],
|
||||
'one-var': [2, 'never'],
|
||||
'prefer-arrow-callback': 2,
|
||||
'prefer-const': [2, { destructuring: 'all' }],
|
||||
'arrow-spacing': 2,
|
||||
'no-inner-declarations': 0,
|
||||
'require-atomic-updates': 'off',
|
||||
'@typescript-eslint/indent': [
|
||||
'error',
|
||||
'tab',
|
||||
{
|
||||
SwitchCase: 1,
|
||||
ignoredNodes: ['TemplateLiteral']
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/camelcase': 'off',
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
'@typescript-eslint/array-type': ['error', 'array-simple'],
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/explicit-member-accessibility': 'off',
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
argsIgnorePattern: '^_'
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/no-object-literal-type-assertion': [
|
||||
'error',
|
||||
{
|
||||
allowAsParameter: true
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/no-unused-vars': 'off'
|
||||
},
|
||||
globals: {
|
||||
globalThis: false
|
||||
},
|
||||
env: {
|
||||
es6: true,
|
||||
browser: true,
|
||||
node: true,
|
||||
mocha: true
|
||||
},
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:import/errors',
|
||||
'plugin:import/warnings',
|
||||
'plugin:import/typescript',
|
||||
'plugin:@typescript-eslint/recommended'
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 9,
|
||||
sourceType: 'module'
|
||||
},
|
||||
plugins: ['svelte3'],
|
||||
settings: {
|
||||
'import/core-modules': [
|
||||
'svelte',
|
||||
'svelte/internal',
|
||||
'svelte/store',
|
||||
'svelte/easing',
|
||||
'estree'
|
||||
],
|
||||
'svelte3/compiler': require('./compiler')
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.js'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-var-requires': 'off'
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['*.svelte'],
|
||||
processor: 'svelte3/svelte3',
|
||||
rules: {
|
||||
'@typescript-eslint/indent': 'off'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
@ -1,44 +0,0 @@
|
||||
{
|
||||
"root": true,
|
||||
"rules": {
|
||||
"indent": [2, "tab", { "SwitchCase": 1 }],
|
||||
"semi": [2, "always"],
|
||||
"keyword-spacing": [2, { "before": true, "after": true }],
|
||||
"space-before-blocks": [2, "always"],
|
||||
"no-mixed-spaces-and-tabs": [2, "smart-tabs"],
|
||||
"no-cond-assign": 0,
|
||||
"no-unused-vars": 2,
|
||||
"object-shorthand": [2, "always"],
|
||||
"no-const-assign": 2,
|
||||
"no-class-assign": 2,
|
||||
"no-this-before-super": 2,
|
||||
"no-var": 2,
|
||||
"no-unreachable": 2,
|
||||
"valid-typeof": 2,
|
||||
"quote-props": [2, "as-needed"],
|
||||
"one-var": [2, "never"],
|
||||
"prefer-arrow-callback": 2,
|
||||
"prefer-const": [2, { "destructuring": "all" }],
|
||||
"arrow-spacing": 2,
|
||||
"no-inner-declarations": 0
|
||||
},
|
||||
"env": {
|
||||
"es6": true,
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"mocha": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:import/errors",
|
||||
"plugin:import/warnings"
|
||||
],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 9,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"settings": {
|
||||
"import/core-modules": ["svelte"],
|
||||
"svelte3/extensions": ["html"]
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
[ignore]
|
||||
<PROJECT_ROOT>/dist/.*
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
|
||||
[options]
|
||||
strip_root=true
|
@ -1 +0,0 @@
|
||||
*.svelte linguist-language=HTML
|
@ -0,0 +1,23 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: 'New Feature'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. For example: I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**How important is this feature to you?**
|
||||
Note: the more honest and specific you are here the more we will take you seriously.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
@ -0,0 +1,12 @@
|
||||
---
|
||||
name: Questions and help
|
||||
about: If you think you need help with something related to Svelte
|
||||
title: ''
|
||||
labels: 'Question'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
This issue tracker is intended to collect bug reports and feature requests.
|
||||
|
||||
For help with installation, information on how features work, or questions about specific features of Svelte, please come and join us in the [Svelte Discord](https://svelte.dev/chat), or ask your question on [Stack Overflow](https://stackoverflow.com/questions/tagged/svelte). Any issues open for help requests will be closed to keep from clogging up the issue tracker.
|
@ -1,7 +1,8 @@
|
||||
<!--
|
||||
Thank you for creating a pull request. Before submitting, please note the following:
|
||||
|
||||
* If your pull request implements a new feature, please raise an issue to discuss it before sending code. In many cases features are absent for a reason.
|
||||
* This message body should clearly illustrate what problems it solves. If there are related issues, remember to reference them.
|
||||
* Ideally, include a test that fails without this PR but passes with it. PRs will only be merged once they pass CI. (Remember to `npm run lint`!)
|
||||
-->
|
||||
|
||||
### Before submitting the PR, please make sure you do the following
|
||||
- [ ] It's really useful if your PR relates to an outstanding issue, so please reference it in your PR, or create an explanatory one for discussion. In many cases features are absent for a reason.
|
||||
- [ ] This message body should clearly illustrate what problems it solves. If there are related issues, remember to reference them.
|
||||
- [ ] Ideally, include a test that fails without this PR but passes with it. PRs will only be merged once they pass CI. (Remember to `npm run lint`!)
|
||||
### Tests
|
||||
- [ ] Run the tests tests with `npm test` or `yarn test`)
|
||||
|
@ -1,25 +0,0 @@
|
||||
import { cubicOut } from './easing';
|
||||
import { is_function } from './internal';
|
||||
|
||||
export function flip(node, animation, params) {
|
||||
const style = getComputedStyle(node);
|
||||
const transform = style.transform === 'none' ? '' : style.transform;
|
||||
|
||||
const dx = animation.from.left - animation.to.left;
|
||||
const dy = animation.from.top - animation.to.top;
|
||||
|
||||
const d = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
const {
|
||||
delay = 0,
|
||||
duration = d => Math.sqrt(d) * 120,
|
||||
easing = cubicOut
|
||||
} = params;
|
||||
|
||||
return {
|
||||
delay,
|
||||
duration: is_function(duration) ? duration(d) : duration,
|
||||
easing,
|
||||
css: (t, u) => `transform: ${transform} translate(${u * dx}px, ${u * dy}px);`
|
||||
};
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,52 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
rules: {
|
||||
indent: [2, 'tab', { SwitchCase: 1 }],
|
||||
semi: [2, 'always'],
|
||||
'keyword-spacing': [2, { before: true, after: true }],
|
||||
'space-before-blocks': [2, 'always'],
|
||||
'no-mixed-spaces-and-tabs': [2, 'smart-tabs'],
|
||||
'no-cond-assign': 0,
|
||||
'no-unused-vars': 2,
|
||||
'object-shorthand': [2, 'always'],
|
||||
'no-const-assign': 2,
|
||||
'no-class-assign': 2,
|
||||
'no-this-before-super': 2,
|
||||
'no-var': 2,
|
||||
'no-unreachable': 2,
|
||||
'valid-typeof': 2,
|
||||
'quote-props': [2, 'as-needed'],
|
||||
'one-var': [2, 'never'],
|
||||
'prefer-arrow-callback': 2,
|
||||
'prefer-const': [2, { destructuring: 'all' }],
|
||||
'arrow-spacing': 2,
|
||||
'no-inner-declarations': 0,
|
||||
'require-atomic-updates': 0
|
||||
},
|
||||
env: {
|
||||
es6: true,
|
||||
browser: true,
|
||||
node: true,
|
||||
mocha: true
|
||||
},
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:import/errors',
|
||||
'plugin:import/warnings'
|
||||
],
|
||||
plugins: ['svelte3'],
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.svelte'],
|
||||
processor: 'svelte3/svelte3'
|
||||
}
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 9,
|
||||
sourceType: 'module'
|
||||
},
|
||||
settings: {
|
||||
'import/core-modules': ['svelte'],
|
||||
'svelte3/compiler': require('svelte/compiler')
|
||||
}
|
||||
};
|
@ -1,44 +0,0 @@
|
||||
{
|
||||
"root": true,
|
||||
"rules": {
|
||||
"indent": [2, "tab", { "SwitchCase": 1 }],
|
||||
"semi": [2, "always"],
|
||||
"keyword-spacing": [2, { "before": true, "after": true }],
|
||||
"space-before-blocks": [2, "always"],
|
||||
"no-mixed-spaces-and-tabs": [2, "smart-tabs"],
|
||||
"no-cond-assign": 0,
|
||||
"no-unused-vars": 2,
|
||||
"object-shorthand": [2, "always"],
|
||||
"no-const-assign": 2,
|
||||
"no-class-assign": 2,
|
||||
"no-this-before-super": 2,
|
||||
"no-var": 2,
|
||||
"no-unreachable": 2,
|
||||
"valid-typeof": 2,
|
||||
"quote-props": [2, "as-needed"],
|
||||
"one-var": [2, "never"],
|
||||
"prefer-arrow-callback": 2,
|
||||
"prefer-const": [2, { "destructuring": "all" }],
|
||||
"arrow-spacing": 2,
|
||||
"no-inner-declarations": 0
|
||||
},
|
||||
"env": {
|
||||
"es6": true,
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"mocha": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:import/errors",
|
||||
"plugin:import/warnings"
|
||||
],
|
||||
"plugins": ["svelte3"],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 9,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"settings": {
|
||||
"import/core-modules": ["svelte"]
|
||||
}
|
||||
}
|
@ -1,9 +1,24 @@
|
||||
<script>
|
||||
// `value` is updated whenever the prop value changes...
|
||||
export let value;
|
||||
// `current` is updated whenever the prop value changes...
|
||||
export let current;
|
||||
|
||||
// ...but `valueAtStart` is fixed upon initialisation
|
||||
const valueAtStart = value;
|
||||
// ...but `initial` is fixed upon initialisation
|
||||
const initial = current;
|
||||
</script>
|
||||
|
||||
<p>{valueAtStart} / {value}</p>
|
||||
<p>
|
||||
<span style="background-color: {initial}">initial</span>
|
||||
<span style="background-color: {current}">current</span>
|
||||
</p>
|
||||
|
||||
<style>
|
||||
span {
|
||||
display: inline-block;
|
||||
padding: 0.2em 0.5em;
|
||||
margin: 0 0.2em 0.2em 0;
|
||||
width: 4em;
|
||||
text-align: center;
|
||||
border-radius: 0.2em;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
@ -1,12 +1,12 @@
|
||||
<script>
|
||||
import marked from 'marked';
|
||||
let value = `Some words are *italic*, some are **bold**`;
|
||||
let text = `Some words are *italic*, some are **bold**`;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
textarea { width: 100%; height: 200px; }
|
||||
</style>
|
||||
|
||||
<textarea bind:value></textarea>
|
||||
<textarea bind:value={text}></textarea>
|
||||
|
||||
{@html marked(value)}
|
||||
{@html marked(text)}
|
@ -1,140 +1,153 @@
|
||||
<script>
|
||||
import { quintOut } from 'svelte/easing';
|
||||
import crossfade from './crossfade.js'; // TODO put this in svelte/transition!
|
||||
|
||||
const { send, receive } = crossfade({
|
||||
fallback(node, params) {
|
||||
const style = getComputedStyle(node);
|
||||
const transform = style.transform === 'none' ? '' : style.transform;
|
||||
|
||||
return {
|
||||
duration: 600,
|
||||
easing: quintOut,
|
||||
css: t => `
|
||||
transform: ${transform} scale(${t});
|
||||
opacity: ${t}
|
||||
`
|
||||
};
|
||||
}
|
||||
import { crossfade, scale } from 'svelte/transition';
|
||||
import images from './images.js';
|
||||
|
||||
const [send, receive] = crossfade({
|
||||
duration: 200,
|
||||
fallback: scale
|
||||
});
|
||||
|
||||
let todos = [
|
||||
{ id: 1, done: false, description: 'write some docs' },
|
||||
{ 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
|
||||
};
|
||||
let selected = null;
|
||||
let loading = null;
|
||||
|
||||
todos = [todo, ...todos];
|
||||
input.value = '';
|
||||
}
|
||||
const ASSETS = `https://svelte-assets.surge.sh/crossfade`;
|
||||
|
||||
function remove(todo) {
|
||||
todos = todos.filter(t => t !== todo);
|
||||
}
|
||||
const load = image => {
|
||||
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>
|
||||
|
||||
<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>
|
||||
.new-todo {
|
||||
font-size: 1.4em;
|
||||
.container {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
margin: 2em 0 1em 0;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.board {
|
||||
max-width: 36em;
|
||||
margin: 0 auto;
|
||||
.phone {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 52vmin;
|
||||
height: 76vmin;
|
||||
border: 2vmin solid #ccc;
|
||||
border-bottom-width: 10vmin;
|
||||
padding: 3vmin;
|
||||
border-radius: 2vmin;
|
||||
}
|
||||
|
||||
.left, .right {
|
||||
float: left;
|
||||
width: 50%;
|
||||
padding: 0 1em 0 0;
|
||||
box-sizing: border-box;
|
||||
h1 {
|
||||
font-weight: 300;
|
||||
text-transform: uppercase;
|
||||
font-size: 5vmin;
|
||||
margin: 0.2em 0 0.5em 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 2em;
|
||||
font-weight: 200;
|
||||
user-select: none;
|
||||
.grid {
|
||||
display: grid;
|
||||
flex: 1;
|
||||
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;
|
||||
left: 0;
|
||||
display: block;
|
||||
font-size: 1em;
|
||||
line-height: 1;
|
||||
padding: 0.5em;
|
||||
margin: 0 auto 0.5em auto;
|
||||
border-radius: 2px;
|
||||
background-color: #eee;
|
||||
user-select: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
input { margin: 0 }
|
||||
.photo {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
justify-content: flex-end;
|
||||
flex-direction: column;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.right label {
|
||||
background-color: rgb(180,240,100);
|
||||
img {
|
||||
object-fit: cover;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button {
|
||||
float: right;
|
||||
height: 1em;
|
||||
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;
|
||||
.credit {
|
||||
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);
|
||||
}
|
||||
|
||||
label:hover button {
|
||||
opacity: 1;
|
||||
.credit a, .credit a:visited {
|
||||
color: white;
|
||||
}
|
||||
</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'>
|
||||
<h2>done</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>
|
@ -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
|
||||
}
|
||||
];
|
@ -0,0 +1,106 @@
|
||||
<script>
|
||||
import { interpolateString as interpolate } from 'd3-interpolate';
|
||||
import { tweened } from 'svelte/motion';
|
||||
|
||||
import Grid from './Grid.svelte';
|
||||
import Controls from './Controls.svelte';
|
||||
|
||||
import { eases, types } from './eases.js';
|
||||
|
||||
let current_type = 'In';
|
||||
let current_ease = 'sine';
|
||||
let duration = 2000;
|
||||
let current = eases.get(current_ease)[current_type];
|
||||
let playing = false;
|
||||
let width;
|
||||
|
||||
const ease_path = tweened(current.shape, { interpolate });
|
||||
const time = tweened(0);
|
||||
const value = tweened(1000);
|
||||
|
||||
async function runAnimations() {
|
||||
playing = true;
|
||||
|
||||
value.set(1000, {duration: 0});
|
||||
time.set(0, {duration: 0});
|
||||
|
||||
await ease_path.set(current.shape);
|
||||
await Promise.all([
|
||||
time.set(1000, {duration, easing: x => x}),
|
||||
value.set(0, {duration, easing: current.fn})
|
||||
]);
|
||||
|
||||
playing = false;
|
||||
}
|
||||
|
||||
$: current = eases.get(current_ease)[current_type];
|
||||
$: current && runAnimations();
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.easing-vis {
|
||||
display: flex;
|
||||
max-height: 95%;
|
||||
max-width: 800px;
|
||||
margin: auto;
|
||||
padding: 10px;
|
||||
border: 1px solid #333;
|
||||
border-radius: 2px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 100%;
|
||||
margin: 0 20px 0 0;
|
||||
}
|
||||
|
||||
.graph {
|
||||
transform: translate(200px,400px)
|
||||
}
|
||||
|
||||
@media (max-width:600px) {
|
||||
.easing-vis {
|
||||
flex-direction: column;
|
||||
max-height: calc(100% - 3rem);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div bind:offsetWidth={width} class="easing-vis">
|
||||
<svg viewBox="0 0 1400 1802">
|
||||
<g class="canvas">
|
||||
<Grid x={$time} y={$value}/>
|
||||
<g class="graph">
|
||||
<path
|
||||
d={$ease_path}
|
||||
stroke="#333"
|
||||
stroke-width="2"
|
||||
fill="none"
|
||||
/>
|
||||
|
||||
<path d="M0,23.647C0,22.41 27.014,0.407 28.496,0.025C29.978,-0.357 69.188,3.744 70.104,4.744C71.02,5.745 71.02,41.499 70.104,42.5C69.188,43.501 29.978,47.601 28.496,47.219C27.014,46.837 0,24.884 0,23.647Z"
|
||||
fill="#ff3e00"
|
||||
style="transform: translate(1060px, {($value - 24)}px)"
|
||||
/>
|
||||
|
||||
<circle
|
||||
cx="{$time}"
|
||||
cy="{$value}"
|
||||
r="15"
|
||||
fill="#ff3e00"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
<Controls
|
||||
{eases}
|
||||
{types}
|
||||
{playing}
|
||||
{width}
|
||||
bind:duration
|
||||
bind:current_ease
|
||||
bind:current_type
|
||||
on:play={runAnimations}
|
||||
/>
|
||||
</div>
|
@ -0,0 +1,186 @@
|
||||
<script>
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
export let current_ease;
|
||||
export let current_type;
|
||||
export let eases;
|
||||
export let types;
|
||||
export let duration;
|
||||
export let playing;
|
||||
export let width;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
$: mobile = width && width < 600;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.easing-sidebar {
|
||||
width: 11em;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
li {
|
||||
padding: 5px 10px;
|
||||
background: #eee;
|
||||
border-radius: 2px;
|
||||
margin: 3px 0;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
li:hover {
|
||||
background: #676778;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.selected {
|
||||
background: #ff3e00;
|
||||
color: white;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0 10px 0 0;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
select {
|
||||
display: inline;
|
||||
padding: 0.2em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.duration {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.duration span {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.duration input {
|
||||
width: 80px;
|
||||
margin: 10px 10px 10px 0 ;
|
||||
}
|
||||
|
||||
.duration button {
|
||||
margin: 10px 5px;
|
||||
}
|
||||
|
||||
.duration .number {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.duration .play {
|
||||
margin: 0 5px 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width:600px) {
|
||||
.easing-types {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.easing-sidebar {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.duration .play {
|
||||
margin-left: auto;
|
||||
width: unset;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 0.9em;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
h3:nth-of-type(2) {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
ul li {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="easing-sidebar">
|
||||
<div class="easing-types">
|
||||
<h3>Ease</h3>
|
||||
{#if mobile}
|
||||
<select bind:value={current_ease}>
|
||||
{#each [...eases] as [name]}
|
||||
<option
|
||||
value={name}
|
||||
class:selected={name === current_ease}
|
||||
>
|
||||
{name}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
{:else}
|
||||
<ul>
|
||||
{#each [...eases] as [name]}
|
||||
<li
|
||||
class:selected={name === current_ease}
|
||||
on:click={() => current_ease = name}
|
||||
>
|
||||
{name}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
<h3>Type</h3>
|
||||
{#if mobile }
|
||||
<select bind:value={current_type}>
|
||||
{#each types as [name, type]}
|
||||
<option
|
||||
value={type}
|
||||
>
|
||||
{name}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
{:else}
|
||||
<ul>
|
||||
{#each types as [name, type]}
|
||||
<li
|
||||
class:selected={type === current_type}
|
||||
on:click={() => current_type = type}
|
||||
>
|
||||
{name}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
</div>
|
||||
<h4>
|
||||
Duration
|
||||
</h4>
|
||||
<div class="duration">
|
||||
<span>
|
||||
<input type="number" bind:value={duration} min="0" step="100"/>
|
||||
<button class="number" on:click={() => duration -= 100}>-</button>
|
||||
<button class="number" on:click={() => duration += 100}>+</button>
|
||||
</span>
|
||||
<button class="play" on:click={() => dispatch('play')}>
|
||||
{playing ? 'Restart' : 'Play'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,62 @@
|
||||
<script>
|
||||
export let x, y;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.grid-line {
|
||||
stroke:#ccc;
|
||||
opacity: 0.5;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.grid-line-xy {
|
||||
stroke: tomato;
|
||||
stroke-width: 2;
|
||||
}
|
||||
</style>
|
||||
|
||||
<svelte:options namespace="svg" />
|
||||
|
||||
<rect
|
||||
x=0
|
||||
y=0
|
||||
width=1400
|
||||
height=1800
|
||||
stroke=#ccc
|
||||
style="opacity: 0.5"
|
||||
fill=none
|
||||
stroke-width=2
|
||||
/>
|
||||
|
||||
{#each { length: 8 } as _, i}
|
||||
{#if i < 6}
|
||||
<path
|
||||
d="M{(i+1) * 200} 0 L{(i+1)*200} 1802"
|
||||
class="grid-line"
|
||||
/>
|
||||
{/if}
|
||||
<path
|
||||
d="M0 {(i+1) * 200} L1400 {(i+1)*200} "
|
||||
class="grid-line"
|
||||
/>
|
||||
{/each}
|
||||
|
||||
<path
|
||||
style="transform: translateX({x+200}px)"
|
||||
d="M0 0 L0 1800"
|
||||
class="grid-line-xy"
|
||||
/>
|
||||
<path
|
||||
style="transform: translateY({y}px)"
|
||||
d="M0 400 L1400 400"
|
||||
class="grid-line-xy"
|
||||
/>
|
||||
<rect
|
||||
x=200
|
||||
y=400
|
||||
width=1000
|
||||
height=1000
|
||||
stroke=#999
|
||||
fill=none
|
||||
stroke-width=2
|
||||
/>
|
@ -0,0 +1,43 @@
|
||||
import * as eases from 'svelte/easing';
|
||||
|
||||
const processed_eases = {};
|
||||
|
||||
for (const ease in eases) {
|
||||
if (ease === "linear") {
|
||||
processed_eases.linear = eases.linear;
|
||||
} else {
|
||||
const name = ease.replace(/In$|InOut$|Out$/, '');
|
||||
const type = ease.match(/In$|InOut$|Out$/)[0];
|
||||
|
||||
if (!(name in processed_eases)) processed_eases[name] = {};
|
||||
processed_eases[name][type] = {};
|
||||
processed_eases[name][type].fn = eases[ease];
|
||||
|
||||
let shape = 'M0 1000';
|
||||
for (let i = 1; i <= 1000; i++) {
|
||||
shape = `${shape} L${(i / 1000) * 1000} ${1000 - eases[ease](i / 1000) * 1000} `;
|
||||
processed_eases[name][type].shape = shape;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const sorted_eases = new Map([
|
||||
['sine', processed_eases.sine],
|
||||
['quad', processed_eases.quad],
|
||||
['cubic', processed_eases.cubic],
|
||||
['quart', processed_eases.quart],
|
||||
['quint', processed_eases.quint],
|
||||
['expo', processed_eases.expo],
|
||||
['circ', processed_eases.circ],
|
||||
['back', processed_eases.back],
|
||||
['elastic', processed_eases.elastic],
|
||||
['bounce', processed_eases.bounce],
|
||||
]);
|
||||
|
||||
export const types = [
|
||||
['Ease In', 'In'],
|
||||
['Ease Out', 'Out'],
|
||||
['Ease In Out', 'InOut']
|
||||
];
|
||||
|
||||
export { sorted_eases as eases };
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"title": "Ease Visualiser"
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"title": "Easing"
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue