Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Built-in easing functions #549

Closed
Rich-Harris opened this issue May 1, 2017 · 12 comments
Closed

Built-in easing functions #549

Rich-Harris opened this issue May 1, 2017 · 12 comments

Comments

@Rich-Harris
Copy link
Member

Creating a separate issue for the section at the bottom of this comment.

For the sake of both a) extreme convenience and b) tree-shaking, should we include the standard Penner easing equations from eases-jsnext? That way, a transition can use a particular easing function without the user having to include it as a helper function (though they still could), or the transition author having to include a library of functions and then do a dynamic lookup.

It could be stringly typed, like so...

<div in:fly='{easing:"elasticOut"}'>...</div>

...or the identifiers could be effectively injected into the scope for the purposes of transition parameters:

<!-- I much prefer this version -->
<div in:fly='{easing: elasticOut}'>...</div>

If we did the second one we could also support custom cubic bezier functions:

<div in:fly='{easing: bezier(.17,.67,.83,.67)}'>...</div>

(Perhaps there's also a built-in spring function? Not sure if that's mathematically possible given how bidirectional transitions work, but maybe...)

This is, I'll concede, all a little bit magical. But the ergonomics are much better, I think — it offers a standard, easily documented, well-understood approach to easing functions which will result in the leanest possible output, and it doesn't prevent someone from doing something totally custom (we could prefer custom helpers over builtins in case of clashes, for example). And the Penner equations are fairly universal for this sort of thing.

@PaulBGD
Copy link
Member

PaulBGD commented May 1, 2017

With the 1st example is it possible to only provide the needed equations (like we do with helpers)?

@Rich-Harris
Copy link
Member Author

As long as it's a string literal then yeah. If it was "elastic" + "In" — no idea why it would be, but let's say it was — then the whole thing would presumably fail.

@PaulBGD
Copy link
Member

PaulBGD commented May 2, 2017

I've just realized that this issue is suggesting an ease of use substitute to users importing/adding the equations to their methods manually.

I don't see the reason for adding magical equation functions when it increases the complexity of learning svelte, potentially clashes with custom equation functions, and adds more code to maintain in the svelte codebase. Not to mention users have to refer to the docs to understand what equations their template compiler supports.

@Rich-Harris
Copy link
Member Author

I hear you. I'm going to try and do a better job of explaining where I'm coming from, but don't take it as dismissing those concerns because I share them — still trying to work out exactly what my opinion is.

Basically, the fundamental problem with web development is that for any given task there's a million ways of achieving it. Libraries will often abdicate the responsibility of having an opinion, which makes the library author's job easier but often makes the user's job harder, because now they have to research ever-changing community best practices around whatever it is they're trying to do and go away and read some new docs (which might not be current/well-written etc). And because the authors of those other libraries are also trying to do the small/focused/agnostic thing, it may not be immediately clear how to use the two things together (for example, a lot of easing libraries expose functions that expect (t, b, c, d) arguments, whereas the Penner equations — like the functions Svelte uses — expect a single t argument).

At the other end of the spectrum you have frameworks that do have opinions about a lot of this stuff, such as Angular. That might mean the framework is monolithic (Angular 1) or that it's split up into many sub-packages that you have to manage (Angular 2/4) — either way there's a cost involved.

Svelte occupies a fairly unique position (for now — hopefully people will start stealing some of its ideas). It can be opinionated, and do things to make users' lives easier, without compromising on a) flexibility relative to an unopinionated library, b) the leanness of the end result, or c) the number of packages you have to juggle. A few examples of how it might improve users' experience:

  • You open the documentation, Cmd-F for 'easing', and see high-quality documentation that's tailored to the experience of using Svelte, complete with code samples
  • The compiler sees you've misspelled qinticOut and, using the same contextual fuzzy matching logic that helps with other typos, offers a suggestion of what it should be
  • You're able to experiment with different easing functions without having to add a new entry to helpers (and possibly add another import declaration). I often find myself going back and forth between different functions when I'm trying to polish a UI, and it's nice to only have to change code in one place
  • It's 2018, and the Svelte extension for VSCode gives you a graphical preview of the easing function you've chosen that looks like this, complete with dropdowns. Or maybe even an editor that lets you play with bezier control points.

Hopefully that sheds some light on why I made this suggestion — it's sort of a statement about what the experience of developing for the web ought to be like, and where a project like Svelte can fit into it. Easing functions are a good place to express that because they're relatively standard (and about as 'done' as software gets). But I get that all this isn't totally uncontroversial!

@PaulBGD
Copy link
Member

PaulBGD commented May 2, 2017

The issue with being opinionated is that it's hard not being either too opinionated or not opinionated enough. Why not add hundreds of easing functions, for the sake of having them in one place and being tailored to svelte? Why not have built-in typescript support for components so that users don't have to figure out how to connect the two?

There's also a reason that libraries exist, for example currently I can use Redux with React, Vue, or Svelte. If I'm using an easing library, that also works with React, Vue, or Angular. I can learn a library once and integrate it with my project. Being opinionated and shipping libraries for the user isn't that helpful because it's another thing they have to learn to use a different tool.

I get that there's a lot of good things that come from integrating everything into one tool, tt's a common theme in the recent centralization of the web. This however introduces the issue of shipping and maintaining everything yourself. I don't see the reason for including everything someone needs to make every type of webpage in svelte. As a compiler I feel that Svelte should instead provide the tools to support everything, rather than providing all these things itself.

Then finally one of the biggest reasons people use react is that it isn't opinionated. You take the tools you need, without react forcing you to have them. It defines the format, but you get to define what it does. I feel that being a compiler, svelte should not be opinionated and instead let the code you're compiling choose its own opinion. Svelte may be in the unique position to introduce itself as an opinionated framework, but once you go opinionated you don't go back to unopinionated.

@jslegers
Copy link

jslegers commented Aug 7, 2017

IMO, the best libraries and frameworks are typically that do as little as possible but do it better than every competitor. IMO transitions are way beyond the scope of a project like Svelte and adding too much of these features in core would actually be a reason for me to avoid using Svelte.

I also don't think that transitions are something that belong in a plugin. Instead, there are the kind of features I'd expect to find in a framework independent utility library that I can plug into React, Vue, or Svelte or whichever framework I'm using. So I think I'm on the same page as @PaulBGD here.

I'm also not convinced that JS is the right place for parameterizable easing functions that generate CSS. This is precisely the kind of use cases CSS preprocessors are created for. If transitions with custom easing is a feature important to you, why not focus on adding Less or Sass support to Svelte first (#181) and then import transitions & easing from a separate Less or Sass library that can also be plugged into React, Vue & other JS frameworks? See also #525 (comment)

This was referenced Aug 7, 2017
@PaulBGD
Copy link
Member

PaulBGD commented Aug 7, 2017

I think you're changing my words a bit. The transition engine in Svelte is fantastic, my issue was with adding lots of functions that we have to maintain in Svelte's core codebase.

@Rich-Harris
Copy link
Member Author

Will close this, the arguments against it make sense

@MrBryanBell
Copy link

Hi. It's 2022 and i wonder if it's possible to use cubic-bezier in svelte transitions. Thanks for the reply.

<div in:fly='{easing: bezier(.17,.67,.83,.67)}'>...</div>

@regnaio
Copy link

regnaio commented Apr 20, 2022

Hi. It's 2022 and i wonder if it's possible to use cubic-bezier in svelte transitions. Thanks for the reply.

<div in:fly='{easing: bezier(.17,.67,.83,.67)}'>...</div>

Is this possible?

If not, can there be a Documentation example of how to achieve this the Svelte way?

@regnaio
Copy link

regnaio commented Apr 29, 2022

A Sveltey way to do this:

<div in:fadeIn out:fadeOut>

</div>
interface Point {
	x: number;
	y: number;
}

/*
	ease-in: cubic-bezier(0.4, 0, 1, 1)
	ease-out: cubic-bezier(0, 0, 0.2, 1)

	https://tailwindcss.com/docs/transition-timing-function
*/
const easeInP1: Point = {
	x: 0.4,
	y: 0
};
const easeInP2: Point = {
	x: 1,
	y: 1
};

const easeOutP1: Point = {
	x: 0,
	y: 0
};
const easeOutP2: Point = {
	x: 0.2,
	y: 1
};

function cubicBezier(t: number, p1: Point, p2: Point): Point {
	const p0: Point = {
		x: 0,
		y: 0
	};

	const p3: Point = {
		x: 1,
		y: 1
	};

	const x = (1 - t) ** 3 * p0.x + (1 - t) ** 2 * t * 3 * p1.x + (1 - t) * t ** 2 * 3 * p2.x + t ** 3 * p3.x;
	const y = (1 - t) ** 3 * p0.y + (1 - t) ** 2 * t * 3 * p1.y + (1 - t) * t ** 2 * 3 * p2.y + t ** 3 * p3.y;

	return { x, y };
}

export function easeIn(t: number): number {
	return cubicBezier(t, easeInP1, easeInP2).y;
}

export function easeOut(t: number): number {
	return cubicBezier(t, easeOutP1, easeOutP2).y;
}

export function fadeIn(node: HTMLElement, params: any) {
	return {
		duration: 1000,
		easing: easeOut,
		css: (t: number, u: number) => `opacity: ${t}; transform: scale(${0.95 + t * 0.05})`
	};
}

export function fadeOut(node: HTMLElement, params: any) {
	return {
		duration: 1000,
		easing: easeIn,
		css: (t: number, u: number) => `opacity: ${t}; transform: scale(${0.95 + t * 0.05})`
	};
}

References:

@inviter42
Copy link

If you, like me, wonder why @regnaio solution doesn't match css's cubic-bezier, thats because it's imprecise. If one wants precision - look towards this tiny lib

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants
@jslegers @Rich-Harris @PaulBGD @inviter42 @MrBryanBell @regnaio and others