pull/4742/head
pushkine 5 years ago
parent 291703d291
commit e9f0ffbd29

@ -1,35 +1,184 @@
<script> <script>
import { spring } from 'svelte/motion'; import { spring } from "svelte/motion";
import { framerate } from "svelte/environment";
import { derived } from "svelte/store";
let coords = spring({ x: 50, y: 50 }, { const s = spring(50);
stiffness: 0.1, let prev_time = now();
damping: 0.25 let prev_value = 50;
}); const velocity = derived(
s,
(v) =>
(-prev_value + (prev_value = v)) / (-prev_time + (prev_time = now())),
0.0
);
let l_time;
$: canvas && Draw($velocity, $s);
let { mass, stiffness, damping, soft } = s;
$: s.soft = soft;
$: s.mass = mass;
$: s.stiffness = stiffness;
$: s.damping = damping;
let solver;
let size = spring(10); function solve_spring(target, prev_velocity) {
const target_ = target;
const delta = target - $s;
if (soft || 1 <= damping / (2.0 * Math.sqrt(stiffness * mass))) {
const angular_frequency = -Math.sqrt(stiffness / mass);
solver = (t) =>
target_ -
(delta + t * (-angular_frequency * delta - prev_velocity)) *
Math.exp(t * angular_frequency);
} else {
const damping_frequency = Math.sqrt(
4.0 * mass * stiffness - damping ** 2
);
const leftover =
(damping * delta - 2.0 * mass * prev_velocity) / damping_frequency;
const dfm = (0.5 * damping_frequency) / mass;
const dm = -(0.5 * damping) / mass;
let f = 0.0;
solver = (t) =>
target_ -
(Math.cos((f = t * dfm)) * delta + Math.sin(f) * leftover) *
Math.exp(t * dm);
}
reset_time = now();
s.set((target__ = target));
}
let target__ = 50;
let canvas;
const start_time = now();
let reset_time = start_time;
const canvas_history = [];
let max_x, min_x, max_y, min_y, canvas_width, canvas_height;
let step, length;
let last_index;
let ctx;
const XC = (x) => ((x - min_x) / (max_x - min_x)) * canvas_width;
const YC = (y) =>
canvas_height - ((y - min_y) / (max_y - min_y)) * canvas_height;
const get_index = (i = 0) =>
(i + Math.floor((prev_time - start_time) / framerate)) % length;
function Draw() {
if (!step) {
max_y = canvas_height / 2;
min_y = -canvas_height / 2;
max_x = canvas_width / 1000;
min_x = -max_x;
step = framerate / 1000; //framerate / canvas_width;
length = Math.floor(max_x / step);
canvas_history.length = length;
canvas_history.fill(0);
ctx = canvas.getContext("2d");
}
ctx.lineWidth = 12;
let offset = (prev_time - reset_time) / 1000;
const start_index = get_index(0);
if (last_index === (last_index = start_index) || !solver) return;
ctx.clearRect(0, 0, canvas_width, canvas_height);
ctx.beginPath();
let x = min_x,
y = 0,
i = start_index + 1;
ctx.moveTo(XC(x), YC(y));
for (x += step; i <= length; i++)
ctx.lineTo(XC(x), YC(canvas_history[i])), (x += step);
for (i = 0; i < start_index; i++)
ctx.lineTo(XC(x), YC(canvas_history[i])), (x += step);
ctx.lineTo(
XC(x),
YC((canvas_history[start_index] = canvas_height / 2 - prev_value))
);
if (Math.abs(prev_value - solver(offset)) > 20) {
solve_spring(target__, $velocity);
offset = 0;
}
x = 0;
while (x <= max_x)
ctx.lineTo(
XC((x += step)),
canvas_height / 2 - YC(solver(x + step + offset))
);
ctx.stroke();
}
</script> </script>
<style> <style>
svg { width: 100%; height: 100%; margin: -8px } :global(body) {
circle { fill: #ff3e00 } width: 100vw;
height: 100vh;
overflow: hidden;
}
svg {
width: 100vw;
height: 100vh;
}
circle {
fill: tomato;
}
canvas {
width: 100vw;
height: 100vh;
position: absolute;
left: 0;
top: 0;
z-index: -1;
}
</style> </style>
<svelte:window
on:mousemove={(e) => solve_spring(e.clientY, $velocity)}
bind:innerWidth={canvas_width}
bind:innerHeight={canvas_height} />
<div style="position: absolute; right: 1em;"> <div style="position: absolute; right: 1em;">
<label> <label>
<h3>stiffness ({coords.stiffness})</h3> <h3>velocity</h3>
<input bind:value={coords.stiffness} type="range" min="0" max="1" step="0.01"> <progress
</label> value={!Number.isNaN($velocity) && Number.isFinite($velocity) && $velocity + 10}
min={0}
max={20} />
</label>
<label>
<h3>speed</h3>
<progress
value={!Number.isNaN($velocity) && Number.isFinite($velocity) && Math.abs($velocity)}
min={0}
max={20} />
</label>
<label>
<h3>y {$s.toFixed(0)}</h3>
<progress value={!Number.isNaN($s) && $s} min={0} max={1000} />
</label>
<label>
<h3>target {target__}</h3>
<progress value={!Number.isNaN(target__) && target__} min={0} max={1000} />
</label>
<label>
<h3>stiffness ({stiffness})</h3>
<input bind:value={stiffness} type="range" min="10" max="200" step="0.01" />
</label>
<label> <label>
<h3>damping ({coords.damping})</h3> <h3>damping ({damping})</h3>
<input bind:value={coords.damping} type="range" min="0" max="1" step="0.01"> <input bind:value={damping} type="range" min="0.1" max="20" step="0.01" />
</label> </label>
<label>
<h3>mass ({mass})</h3>
<input bind:value={mass} type="range" min="0.1" max="20" step="0.01" />
</label>
<label>
<h3>
soft
<input bind:checked={soft} type="checkbox" />
</h3>
</label>
</div> </div>
<svg>
<svg <circle
on:mousemove="{e => coords.set({ x: e.clientX, y: e.clientY })}" cx={-15+canvas_width / 2}
on:mousedown="{() => size.set(30)}" cy={$s}
on:mouseup="{() => size.set(10)}" r={30} />
>
<circle cx={$coords.x} cy={$coords.y} r={$size}/>
</svg> </svg>
<canvas bind:this={canvas} width={canvas_width} height={canvas_height} />

@ -1,147 +1,159 @@
<script> <script>
import { quintOut } from 'svelte/easing'; import { quintOut } from "svelte/easing";
import { crossfade } from 'svelte/transition'; import { crossfade } from "svelte/transition";
import { flip } from 'svelte/animate'; import { flip } from "svelte/animate";
const [send, receive] = crossfade({ const [send, receive] = crossfade({
fallback(node, params) { fallback(node, params) {
const style = getComputedStyle(node); const style = getComputedStyle(node);
const transform = style.transform === 'none' ? '' : style.transform; const transform = style.transform === "none" ? "" : style.transform;
return {
return { duration: 600,
duration: 600, easing: quintOut,
easing: quintOut, css: (t) => `
css: t => `
transform: ${transform} scale(${t}); transform: ${transform} scale(${t});
opacity: ${t} opacity: ${t}
` `,
}; };
} },
}); });
let todos = [ let todos = [
{ id: 1, done: false, description: 'write some docs' }, { key: 1, done: false, description: "write some docs" },
{ id: 2, done: false, description: 'start writing JSConf talk' }, { key: 2, done: false, description: "start writing JSConf talk" },
{ id: 3, done: true, description: 'buy some milk' }, { key: 3, done: true, description: "buy some milk" },
{ id: 4, done: false, description: 'mow the lawn' }, { key: 4, done: false, description: "mow the lawn" },
{ id: 5, done: false, description: 'feed the turtle' }, { key: 5, done: false, description: "feed the turtle" },
{ id: 6, done: false, description: 'fix some bugs' }, { key: 6, done: false, description: "fix some bugs" },
]; ];
let uid = todos.length + 1; let ukey = todos.length + 1;
function add(input) { function add(input) {
const todo = { todos = [{ key: ukey++, done: false, description: input.value }, ...todos];
id: uid++, input.value = "";
done: false, }
description: input.value
}; function remove(key1) {
todos = todos.filter(({ key }) => key !== key1);
todos = [todo, ...todos]; }
input.value = ''; $: list = todos.reduce((prev, td) => (prev[+td.done].push(td), prev), [
} [],
[],
function remove(todo) { ]);
todos = todos.filter(t => t !== todo); const animating = new Map();
} let lastShuffle = Date.now();
let t = 0;
</script> </script>
<style> <style>
.new-todo { .new-todo {
font-size: 1.4em; font-size: 1.4em;
width: 100%; width: 100%;
margin: 2em 0 1em 0; margin: 2em 0 1em 0;
} }
.board {
.board { max-width: 36em;
max-width: 36em; margin: 0 auto;
margin: 0 auto; }
} .left,
.right {
.left, .right { float: left;
float: left; width: 50%;
width: 50%; padding: 0 1em 0 0;
padding: 0 1em 0 0; box-sizing: border-box;
box-sizing: border-box; }
} h2 {
font-size: 2em;
h2 { font-weight: 200;
font-size: 2em; user-select: none;
font-weight: 200; }
user-select: none; label {
} top: 0;
left: 0;
label { display: block;
top: 0; font-size: 1em;
left: 0; line-height: 1;
display: block; padding: 0.5em;
font-size: 1em; margin: 0 auto 0.5em auto;
line-height: 1; border-radius: 2px;
padding: 0.5em; background-color: #eee;
margin: 0 auto 0.5em auto; user-select: none;
border-radius: 2px; }
background-color: #eee; input {
user-select: none; margin: 0;
} }
.right label {
input { margin: 0 } background-color: rgb(180, 240, 100);
}
.right label { button {
background-color: rgb(180,240,100); float: right;
} height: 1em;
box-sizing: border-box;
button { padding: 0 0.5em;
float: right; line-height: 1;
height: 1em; background-color: transparent;
box-sizing: border-box; border: none;
padding: 0 0.5em; color: rgb(170, 30, 30);
line-height: 1; opacity: 0;
background-color: transparent; transition: opacity 0.2s;
border: none; }
color: rgb(170,30,30); label:hover button {
opacity: 0; opacity: 1;
transition: opacity 0.2s; }
}
label:hover button {
opacity: 1;
}
</style> </style>
<div class='board'> <svelte:window
<input on:keydown={(e) => {
class="new-todo" let [{ length: a }, { length: b }] = list;
placeholder="what needs to be done?" if (e.keyCode == 32) {
on:keydown="{event => event.which === 13 && add(event.target)}" if (lastShuffle > Date.now() - 100) {
> return;
}
<div class='left'> lastShuffle = Date.now();
<h2>todo</h2> e = 0;
{#each todos.filter(t => !t.done) as todo (todo.id)} if (!a) e += 1;
<label if (!b) e -= 2;
in:receive="{{key: todo.id}}" if (!e) e += t = 0.5 + (b - a) / (a + b);
out:send="{{key: todo.id}}" s: if (~e) {
animate:flip a += b;
> let w = 1;
<input type=checkbox bind:checked={todo.done}> while (animating.get((e = (e = list[(e = +(Math.random() + e > 0.5))])[Math.floor(Math.random() * e.length)]).key)) if (w++ > a) break s;
{todo.description} e.done = !e.done;
<button on:click="{() => remove(todo)}">x</button> list = list;
</label> }
{/each} }
</div> }} />
<div class="board">
<div class='right'> <input
<h2>done</h2> class="new-todo"
{#each todos.filter(t => t.done) as todo (todo.id)} placeholder="what needs to be done?"
<label on:keydown={(event) => void (event.key === 'Enter' && add(event.target))} />
in:receive="{{key: todo.id}}" {#each list as todo, i (i)}
out:send="{{key: todo.id}}" <div class={!i ? 'left' : 'right'}>
animate:flip <h2>{!i ? 'todo' : 'done'}</h2>
> {#each todo as { key, description, done: checked } (key)}
<input type=checkbox bind:checked={todo.done}> <label
{todo.description} in:receive={{ key }}
<button on:click="{() => remove(todo)}">x</button> out:send={{ key }}
</label> animate:flip
{/each} on:outrostart={() => {
</div> animating.set(key, true);
}}
on:outroend={() => {
animating.set(key, false);
}}
on:introstart={() => {
animating.set(key, true);
}}
on:introend={() => {
animating.set(key, false);
}}>
<input type="checkbox" bind:checked />
{description}
<button on:click={remove.bind(null, key)}>x</button>
</label>
{/each}
</div>
{/each}
</div> </div>

@ -1,106 +1,107 @@
<script> <script>
import { interpolateString as interpolate } from 'd3-interpolate'; import { strings } from "svelte/interpolate";
import { tweened } from 'svelte/motion'; import { tweened } from "svelte/motion";
import { cubicBezier, easeOut } from "svelte/easing";
import Grid from './Grid.svelte'; import { onDestroy } from "svelte";
import Controls from './Controls.svelte';
import Grid from "./Grid.svelte";
import { eases, types } from './eases.js'; import Controls from "./Controls.svelte";
let current_type = 'In'; import { eases, types, generate } from "./eases.js";
let current_ease = 'sine';
let duration = 2000; let current_type = "In";
let current = eases.get(current_ease)[current_type]; let current_ease = "sine";
let playing = false; let duration = 2000;
let width; let current = eases.get(current_ease)[current_type];
let playing = false;
const ease_path = tweened(current.shape, { interpolate }); let width;
const time = tweened(0);
const value = tweened(1000); const ease_path = tweened(current.shape, {
interpolate: strings,
async function runAnimations() { easing: easeOut,
playing = true; });
const time = tweened(0);
value.set(1000, {duration: 0}); const value = tweened(1000);
time.set(0, {duration: 0});
async function runAnimations() {
await ease_path.set(current.shape); playing = true;
await Promise.all([ value.setImmediate(1000);
time.set(1000, {duration, easing: x => x}), time.setImmediate(0);
value.set(0, {duration, easing: current.fn}) ease_path.set(is_custom ? generate(current_bezier) : current.shape);
]); time.set(1000, { duration });
value.set(0, { duration, easing: is_custom ? current_bezier : current.fn });
playing = false; }
}
onDestroy(time.onRest(() => (playing = false)));
$: current = eases.get(current_ease)[current_type]; $: is_custom = current_ease.includes("Bezier");
$: current && runAnimations(); $: current = !is_custom && eases.get(current_ease)[current_type];
$: current_bezier, current, runAnimations();
let eq;
let x1, x2, y1, y2;
$: current_bezier = bezier && cubicBezier(...bezier);
let bezier;
let div;
</script> </script>
<style> <style>
.easing-vis { .easing-vis {
display: flex; display: flex;
max-height: 95%; max-height: 95%;
max-width: 800px; max-width: 800px;
margin: auto; margin: auto;
padding: 10px; padding: 10px;
border: 1px solid #333; border: 1px solid #333;
border-radius: 2px; border-radius: 2px;
padding: 20px; padding: 20px;
} }
svg { .svg1 {
width: 100%; width: 100%;
margin: 0 20px 0 0; margin: 0 20px 0 0;
} }
.graph { .graph {
transform: translate(200px,400px) transform: translate(200px, 400px);
} }
@media (max-width:600px) { @media (max-width: 600px) {
.easing-vis { .easing-vis {
flex-direction: column; flex-direction: column;
max-height: calc(100% - 3rem); max-height: calc(100% - 3rem);
} }
} }
circle {
z-index: 10;
position: absolute;
}
</style> </style>
<div bind:offsetWidth={width} class="easing-vis"> <div bind:offsetWidth={width} bind:this={div} class="easing-vis">
<svg viewBox="0 0 1400 1802"> {#if is_custom}
<g class="canvas"> <div
<Grid x={$time} y={$value}/> style="position:absolute;z-index:10;left:120px;top:150px;font-size:24px;">
<g class="graph"> cubicBezier({bezier})
<path </div>
d={$ease_path} {/if}
stroke="#333" <svg class="svg1" viewBox="0 0 1400 1802">
stroke-width="2" <g class="canvas">
fill="none" <Grid {is_custom} bind:bezier x={$time} y={$value}>
/> <g class="graph">
<path d={$ease_path} stroke="tomato" stroke-width="10" 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" <circle cx={$time} cy={$value} r="15" fill="#333" />
fill="#ff3e00" </g>
style="transform: translate(1060px, {($value - 24)}px)" </Grid>
/> </g>
</svg>
<circle <Controls
cx="{$time}" {is_custom}
cy="{$value}" {eases}
r="15" {types}
fill="#ff3e00" {playing}
/> {width}
</g> {bezier}
</g> bind:duration
</svg> bind:current_ease
bind:current_type
<Controls on:play={runAnimations} />
{eases}
{types}
{playing}
{width}
bind:duration
bind:current_ease
bind:current_type
on:play={runAnimations}
/>
</div> </div>

@ -1,186 +1,175 @@
<script> <script>
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from "svelte";
export let current_ease; export let current_ease;
export let current_type; export let current_type;
export let eases; export let eases;
export let types; export let types;
export let duration; export let duration;
export let playing; export let playing;
export let width; export let width;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
$: mobile = width && width < 600; $: mobile = width && width < 600;
</script> </script>
<style> <style>
.easing-sidebar { .easing-sidebar {
width: 11em; width: 11em;
} }
ul { ul {
list-style: none; list-style: none;
padding: 0; padding: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
font-size: 18px; font-size: 18px;
} }
li { li {
padding: 5px 10px; padding: 5px 10px;
background: #eee; background: #eee;
border-radius: 2px; border-radius: 2px;
margin: 3px 0; margin: 3px 0;
cursor:pointer; cursor: pointer;
} }
li:hover { li:hover {
background: #676778; background: #676778;
color: white; color: white;
} }
.selected { .selected {
background: #ff3e00; background: #ff3e00;
color: white; color: white;
} }
h3 { h3 {
margin: 0 10px 0 0; margin: 0 10px 0 0;
} }
h4 { h4 {
margin-bottom: 0; margin-bottom: 0;
} }
select { select {
display: inline; display: inline;
padding: 0.2em; padding: 0.2em;
margin: 0; margin: 0;
} }
.duration { .duration {
width: 100%; width: 100%;
display: flex; display: flex;
align-items: center; align-items: center;
flex-wrap: wrap; flex-wrap: wrap;
} }
.duration span { .duration span {
display: flex; display: flex;
} }
.duration input { .duration input {
width: 80px; width: 80px;
margin: 10px 10px 10px 0 ; margin: 10px 10px 10px 0;
} }
.duration button { .duration button {
margin: 10px 5px; margin: 10px 5px;
} }
.duration .number { .duration .number {
width: 30px; width: 30px;
} }
.duration .play { .duration .play {
margin: 0 5px 0 auto; margin: 0 5px 0 auto;
width: 100%; width: 100%;
} }
@media (max-width:600px) { @media (max-width: 600px) {
.easing-types { .easing-types {
display: flex; display: flex;
align-items: center; align-items: center;
margin-top: 10px; margin-top: 10px;
} }
.easing-sidebar { .easing-sidebar {
width: 100%; width: 100%;
} }
.duration .play { .duration .play {
margin-left: auto; margin-left: auto;
width: unset; width: unset;
} }
h3 { h3 {
font-size: 0.9em; font-size: 0.9em;
display: inline; display: inline;
} }
h3:nth-of-type(2) { h3:nth-of-type(2) {
margin-left: auto; margin-left: auto;
} }
ul li { ul li {
margin-right: 10px; margin-right: 10px;
} }
} }
</style> </style>
<div class="easing-sidebar"> <div class="easing-sidebar">
<div class="easing-types"> <div class="easing-types">
<h3>Ease</h3> <h3>Ease</h3>
{#if mobile} {#if mobile}
<select bind:value={current_ease}> <select bind:value={current_ease}>
{#each [...eases] as [name]} {#each [...eases, [`cubicBezier`]] as [name]}
<option <option value={name} class:selected={name === current_ease}>
value={name} {name}
class:selected={name === current_ease} </option>
> {/each}
{name} </select>
</option> {:else}
{/each} <ul>
</select> {#each [...eases, [`cubicBezier`]] as [name]}
{:else} <li
<ul> class:selected={name === current_ease}
{#each [...eases] as [name]} on:click={() => (current_ease = name)}>
<li {name}
class:selected={name === current_ease} </li>
on:click={() => current_ease = name} {/each}
> </ul>
{name} {/if}
</li> <h3>Type</h3>
{/each} {#if mobile}
</ul> <select bind:value={current_type}>
{/if} {#each types as [name, type]}
<h3>Type</h3> <option value={type}>{name}</option>
{#if mobile } {/each}
<select bind:value={current_type}> </select>
{#each types as [name, type]} {:else}
<option <ul>
value={type} {#each types as [name, type]}
> <li
{name} class:selected={type === current_type}
</option> on:click={() => (current_type = type)}>
{/each} {name}
</select> </li>
{:else} {/each}
<ul> </ul>
{#each types as [name, type]} {/if}
<li </div>
class:selected={type === current_type} <h4>Duration</h4>
on:click={() => current_type = type} <div class="duration">
> <span>
{name} <input type="number" bind:value={duration} min="0" step="100" />
</li> <button class="number" on:click={() => (duration -= 100)}>-</button>
{/each} <button class="number" on:click={() => (duration += 100)}>+</button>
</ul> </span>
{/if} <button class="play" on:click={() => dispatch('play')}>
</div> {playing ? 'Restart' : 'Play'}
<h4> </button>
Duration </div>
</h4> </div>
<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>

@ -1,62 +1,98 @@
<script> <script>
export let x, y; export let is_custom;
export let x, y;
let rect;
let rect2;
export let x1 = 200,
y1 = 1400,
x2 = 1200,
y2 = 400;
let selected1 = false,
selected2 = false;
export let bezier = [0, 0, 0, 0];
const radius = 30;
$: {
if (rect2) {
const { x, y, width, height } = rect2.getBoundingClientRect();
bezier = [
Math.max(0, Math.min(1, (x1 - 200) / 1000)),
-(y1 - 1400) / 1000,
Math.max(0, Math.min(1, 1 + (x2 - 1200) / 1000)),
1 - (y2 - 400) / 1000,
].map((v) => Math.round(v * 100) / 100);
}
}
</script> </script>
<style> <style>
.grid-line { .grid-line {
stroke:#ccc; stroke: #ccc;
opacity: 0.5; opacity: 0.5;
stroke-width: 2; stroke-width: 2;
} }
circle {
.grid-line-xy { z-index: 10;
stroke: tomato; position: absolute;
stroke-width: 2; }
}
</style> </style>
<svelte:options namespace="svg" /> <svelte:options namespace="svg" />
<rect <rect
x=0 bind:this={rect}
y=0 x="0"
width=1400 y="0"
height=1800 width="1400"
stroke=#ccc height="1800"
style="opacity: 0.5" stroke="#ccc"
fill=none style="opacity: 0.5"
stroke-width=2 fill="none"
/> stroke-width="2" />
{#each { length: 8 } as _, i} {#each { length: 8 } as _, i}
{#if i < 6} {#if i < 6}
<path <path d="M{(i + 1) * 200} 0 L{(i + 1) * 200} 1802" class="grid-line" />
d="M{(i+1) * 200} 0 L{(i+1)*200} 1802" {/if}
class="grid-line" <path d="M0 {(i + 1) * 200} L1400 {(i + 1) * 200} " class="grid-line" />
/>
{/if}
<path
d="M0 {(i+1) * 200} L1400 {(i+1)*200} "
class="grid-line"
/>
{/each} {/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 <rect
x=200 bind:this={rect2}
y=400 x="200"
width=1000 y="400"
height=1000 width="1000"
stroke=#999 height="1000"
fill=none stroke="#999"
stroke-width=2 fill="none"
/> stroke-width="4" />
<svelte:window
on:mousemove={(e) => {
const { x, y, width, height } = rect.getBoundingClientRect();
const _x1 = Math.min(1200, Math.max(200, (e.clientX - x) * (1400 / width)));
const _y1 = Math.min(1800, Math.max(0, (e.clientY - y) * (1800 / height)));
if (selected1) (x1 = _x1), (y1 = _y1);
else if (selected2) (x2 = _x1), (y2 = _y1);
}}
on:mouseup={() => {
selected1 = selected2 = false;
}} />
<slot />
{#if is_custom}
<path d="M200 1400 L{x1} {y1} " stroke="#333333d9" stroke-width="10" />
<path d="M1200 400 L{x2} {y2} " stroke="#333333d9" stroke-width="10" />
<circle
cx={x1}
cy={y1}
r={radius}
fill="#333"
stroke="transparent"
stroke-width="100"
on:mousedown={() => (selected1 = true)} />
<circle
cx={x2}
cy={y2}
r={radius}
fill="#333"
stroke="transparent"
stroke-width="100"
on:mousedown={() => (selected2 = true)} />
{/if}

@ -1,25 +1,25 @@
import * as eases from 'svelte/easing'; import * as eases from 'svelte/easing'
const processed_eases = {}; const processed_eases = {}
let ease
for (const ease in eases) { for (ease in eases) {
if (ease === "linear") { if (ease === 'linear') {
processed_eases.linear = eases.linear; processed_eases.linear = eases.linear
} else if (ease === 'cubicBezier') {
continue
} else { } else {
const name = ease.replace(/In$|InOut$|Out$/, ''); const name = ease.replace(/In$|InOut$|Out$/, '')
const type = ease.match(/In$|InOut$|Out$/)[0]; ;(processed_eases[name] || (processed_eases[name] = {}))[ease.match(/In$|InOut$|Out$/)[0]] = {
fn: eases[ease],
if (!(name in processed_eases)) processed_eases[name] = {}; shape: generate(eases[ease]),
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;
} }
} }
} }
export function generate(easing, from = 0, to = 1000) {
let shape = `M${from} ${to}`
for (let i = from + 1; i <= to; i++) shape += ` L${i} ${to - to * easing(i / to)}`
return shape
}
const sorted_eases = new Map([ const sorted_eases = new Map([
['sine', processed_eases.sine], ['sine', processed_eases.sine],
@ -32,12 +32,12 @@ const sorted_eases = new Map([
['back', processed_eases.back], ['back', processed_eases.back],
['elastic', processed_eases.elastic], ['elastic', processed_eases.elastic],
['bounce', processed_eases.bounce], ['bounce', processed_eases.bounce],
]); ])
export const types = [ export const types = [
['Ease In', 'In'], ['Ease In', 'In'],
['Ease Out', 'Out'], ['Ease Out', 'Out'],
['Ease In Out', 'InOut'] ['Ease In Out', 'InOut'],
]; ]
export { sorted_eases as eases }; export { sorted_eases as eases }

Loading…
Cancel
Save