Giter Club home page Giter Club logo

material-motion-js's Introduction

logo Material Motion for JavaScript

Current version: Test status Code coverage
HEAD: Test status Code coverage

Chat

This repo houses the JavaScript implementation of Material Motion. For more information about the project as a whole, check the Starmap.

High-level Goals

  • To make gestural interactions as easy to reuse across applications as UI components already are.

  • To enable the motions and gestures described in the Material Spec to be easily implemented by application authors in the JS ecosystem.

  • To make prototyping new animated experiences simpler.

  • To yield a system that feels robust by default. Fragile interactions erode user trust in the overall system: “should I enter my password in a glitchy app?”

  • To allow interactions to be inspected and tweaked with visual tools.

  • To allow interactions to be easily ported across platforms.

  • To allow authors to write views in terms of URLs, and have the system guide the transitions between them.

    • Note: This is an eventual goal. In the near term, Material Motion is focused specifically on aiding the creation of reusable gestural interactions.

Contributing

Want to contribute? Awesome - thanks for helping!

To get started, just run these commands:

git clone [email protected]:material-motion/material-motion-js.git
cd material-motion-js
yarn
$( yarn bin )/lerna bootstrap

They will check out the repo, install the dependencies for each package, and link the packages to one another. Then, find the package you want to work on in packages and start coding!

License

Apache 2.0

material-motion-js's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

material-motion-js's Issues

Replicas and interactivity

Even if #15 and #16 were solved, you'd have to make sure the replica was seamlessly interactive (e.g. worked just like the original). Even if #22 says elements shouldn't be interactive mid-transition, there's the time at the end where the transition feels complete, even if the spring is still technically settling.

Ensure TimeStream treats each observer equally

Manual performers should match the execution of delegated performers. Thus, if element.animate starts moving on the same frame it's called, we should too. If it doesn't, we shouldn't.

There are a lot of little details to get right here:

  • Does it do the right thing for the first observer?
  • Does it for the second?
  • How do you ensure that each observer gets exactly one next on every frame?

Trying to make this perfect is a distraction from the greater goal of prototyping a manual performer, so I'm punting on this for now.

One solution might be to make each manual performer responsible for tracking how long it's been since its own next was called and doing the right thing. Then, it's not the concern of the system, and each performer can decide whether to start on frame 0 or frame 1.

Pretty documentation + tutorials

Should take the time to design and plan proper educational materials for material-motion. Deliberately separating this out into its own issue to unblock #31.

Assess value of isIdle vs isIdleStream

We're currently using Observables for event dispatch. Because RxJS produces cold observables by default, we're subscribing to isIdleStream and caching its results on {performer, scheduler}.isIdle to make the observable hot.

If we figure out the idiomatic way to do hot/replayable observables in RxJS 5, the subscriptions are no longer necessary (which should be great for garbage collection). In that situation, the only reason we'd need to store isIdle is to provide an easy way for authors to synchronously check if we're idle without buying into Observables.

So, we should probably either:

  1. Kill isIdle and force authors to use isIdleStream. Since the primary use case for isIdle is doing something when the system is at rest, authors will probably need to use observers anyway.
  2. Make sure scheduler and every performer have dispose methods to clean up their subscriptions. This feels gross, and it means we'd need to keep a reference to every performer to make sure dispose cascades. On the other hand, we made need to hold references to the performers for at least some period of time to solve #41.

Replicas and Style Inheritance

Replicas on the Web are very low on the priority list because of stacking contexts (#15). Even if we solved that, style inheritance would still make replicas hard to do correctly.

If you recursively ran getComputedStyle on a source element, could you use that information to faithfully create a replica? How expensive would this be (e.g. how many elements could you replicate before jank becomes apparent)?

Automatic transitions

If we know what kinds of transitionables are in the from and to views, can we pick the right director to go between them?

Gesture recognition

iOS has an awesome gesture recognition model (a state machine) that lets the Material Motion implementation there do a lot with very little code.

We should investigate existing JS implementations (e.g. hammer.js), but will likely want to write our own RxJS pipeline to convert DOM events to gestures. Also need to figure out how/if to map those gestures to underlying DOM elements.

Dynamic spring stiffness

Would be cool to calculate the longest distance in a transition and use that to dynamically set spring stiffness such that the whole thing completes in a reasonable amount of time.

Cancelable transitions

If user interaction occurs before the transition has reached a minimum completeness threshold (e.g. is at least 90% through), rewind the transition. Interaction during the rewind is ignored.

@schlem's idea.

Use variations of the same plan for Tween or Spring performers

This should be fairly easy to prototype using Performer.canHandle. Feature parity and optimal code sharing between tweens and springs might? be a bit harder.

Harder:

  • Would it be possible to seamlessly switch from one to the other, if a plan arrived affecting the same target and property but using the other interpolator (easing vs spring).

Limit activity during a transition

There should probably be a hook to tell the business logic of an application when a transition is happening, so it can minimize any work it's trying to do on the main thread during the transition and save it for when the system is idle.

Transitions and URLs

The Material Motion architecture is designed for transitions between known states. On the Web, links make it easy to go between arbitrary states, and the resulting combinatoric explosion might make it impractical to define Directors between every permutation.

A common refrain in the {React, Ember} communities is "if your UI is nested, your routes should be.". Perhaps we can use this nesting to find the appropriate Director to manage a transition between different branches of the route tree. If there's no director to go from one leaf to another, maybe there's one to transition out of a particular parent view and into another one.

TweenPerformerReact

Build a manually-driven Performer (TweenPerformerReact):

  • Use RxJS to build a time stream that emits timestamps from requestAnimationFrame and subscribe to it with TweenPerformerReact's next function.
    • Scheduler should probably remove the subscription when TweenPerformerWeb signals that it's at-rest.
  • Use bezier-easing for interpolation.
  • Support fulfilling multiple plans concurrently.
  • Interruptability - what and how?

Eventually, I should be able to go to https://material-motion.appspot.com/expressions/, click the Animate button, and have both semicircles move in unison, even though they are powered by separate Performer systems.

Distribution strategy

  • Do we include JS bundles in the GitHub repo?
  • Should probably have 3 targets:
    • Raw source, complete with Flow annotations and experimental Babel syntaxes
    • Closure-friendly build that converts Flow annotations and experimental syntax into JSDoc and ES2015. Let Closure handle any transformations beyond that (including, perhaps, parsing import/export).
    • Single-file bundle, for browsers
  • Release process should generate the above targets, run the appropriate tests on each, and push to the relevant places
  • Do we host the browser-friendly bundle on a CDN?

Loosely-related: #3

Replay isIdle for performers and scheduler

The current implementation of {scheduler,performer}.isIdleStream requires observers to be registered before events are dispatched. It doesn't replay known values.

Need to research the best way to do this in RxJS 5 (there was previously something called shareReplay, but it has been replaced). Could be as simple as writing our own operator, but should see if there's a built-in way first.

Add Challenges to README

Challenges

Quite simply, the Web wasn't designed for layered transitions between states or
for gestural interactivity. Architectural limitations to be aware of are listed
here. Actionable experiments are listed in
Issues.

but with links to the relevant issues for important platform-level concerns.

Inspector

From old notes:


  • Could be effectively Redux actions over {WebRTC, WebSockets, postMessage, Chrome RDB}
  • Every time you want a variable (e.g. a spring config), give it a unique name, and the name of fallback (e.g. “default”). In regular usage, it can inherit the values of the fallback, but when the Inspector is attached, this gives the designer the granularity to change one without changing all.
    • This can all be encapsulated in a component that registers the spring name on instantiation and uses a selector to take the named value if it’s available or the default otherwise.
    • This gives the designer flexibility to share parameters across views, but tweak them individually when that’s helpful.
  • If a team has an app on multiple platforms, what’s the right way to handle platform differences? Do tweaks affect the common styles or a platform specific one?

  • Build this into a split-screen iframe (perhaps 411x360 on the left and 309x360 on the right) that shows a demo on one side and knobs on the other
  • Variables:
    • Spring configs
    • Colors
    • Easing functions
    • Timeline layering (e.g. mapRangeLimited params)

Optimize bundle sizes

  • Make examples depend on dist (rather than bundling it)
  • Figure out why Webpack spits out such big bundles and how to make them tiny.

Loosely related to #3.

Mount functions on a timeline

Interesting idea from Ben Schroeder's project in NYC: bind functions to a particular instant on a timeline. Whenever that frame renders, execute the bound function.

Make TweenPerformerWeb more robust

TweenPerformerWeb doesn't know how to handle multiple simultaneous plans, because most tweens overload transform, so they get overwritten.

  • Absolute plans (from or to) should probably clobber previous ones. Relative ones (by) should probably compound on top of absolute plans.
  • Need to be able to introspect a current transform and dissect it into its constituent parts, maybe with https://github.com/web-animations/web-animations-js/blob/master/src/transform-handler.js ?
  • May need to remember currently-executing Plans to know which order to apply transformations in.
  • WebAnimation Timelines. Can we even?

Best path forward is probably to make TweenPerformerReact (#5) and apply learnings here.

Reuse Performers when tweens affect the same target

Right now, the Scheduler appears to be recreating Performers even though there are existing Performers that should be able to handle that plan. This made #40 hard, because it appeared that the isActiveStream was overly noisey; when in fact, the problem was multiple instances of TweenPerformerWeb being created in parallel for each new set of plans.

Fixing this will probably lead to a memory leak until we decide what the lifecycle is of a Performer (e.g. when it can be disposed): material-motion-archive/starmap#14

Material Elevation

The Material Spec depends on an elevation metaphor, where two sheets of material either lie in the same plane or one is ahead of the other in Z space.

How does that fit in with this architecture?

  • e.g: If a user taps a card that is occluded by the App Bar, the App Bar needs to get out of the way.

How does that solution scale and compose (#12) across Directors? How do you ensure it's handled exactly once (e.g. always considered, but only in one place)? What about if the transition is between two one-sided Directors (#14)?

Environment changes during transition

How do we handle catastrophic environment changes during a transition (e.g. the viewport size changes, or the user enters a new URL)?

The easiest thing to do is probably to abort the transition and jump-cut to the destination. Is that the correct thing?

Nestable timelines, e.g. mapRange

Would be cool to be able to define a sequence of plans on one timeline, and mount that in a greater timeline.

For instance:

innerTimeline: {
  0: planA,
  .5: planB,
  1: planC,
};

mapRange(
  innerTimeline,
  .5,
  .75
) === {
  .5: planA,
  .625: planB,
  .75: planC,
}

Document deviations from Google Style

The next version of the Google Style Guide is way more prescriptive than the current one. I expect most of it we'll want to use wholesale (and we should update eslint-config-google accordingly). However, it may be worth considering if there are places we may want to deviate.

For instance, Google Style is to end protected member names with underscores. Externally, you'd typically begin protected names with underscores. Since they're protected, maybe we should just follow Google Style. However, it's also worth considering if this would make our project feel more alien/less endemic to the OSS community.

Note: .eslintrc is already thoroughly commented. This may be enough, or it may be worth documenting it more explicitly (e.g. in a Markdown file).

Document v1

Some fairly-simple overview of the public API in a .md file would be fine. We don't need to be blocked on super-developer-friendly docs until we're ready to polish and promote.

Composability: how?

We should be able to break transitions into reusable pieces of code (e.g. a radial fade that can be included in many transitions). How would we model that?


If we do have some composable mini director, how does it communicate with elements outside its purview?

Examples:

  • There's a card that's partially occluded by the app bar, and it needs to tell the app bar to get out of the way.
  • A grid of cards is radial fading and some other element should be included in the fade.

This is probably fairly simple (pass the external dependencies as arguments to the mini director), but should be explicitly addressed in any solution.

Animating shadows and corner radii

What's the right way to animate a change in shadows or corner radius?

  • Just change the drop-shadow or box-shadow property and let the browser repaint on every frame.
  • Create a new DOM element for every frame of the animation and treat them like a sprite sheet, moving a new one into view on every frame. (Non-trivial because of #16)
  • Replace the transitioning element with a 9-patch during the transition and translate/scale the patches appropriately.
    • I think I tried this and making the patches and moving them around was more expensive than just letting the element repaint, but I don't think I got it to the point where the patches weren't repainting.

Perhaps the answer changes depending on the number of DOM elements transitioning, or on their complexity. Perhaps it varies based on user hardware.

When do tween Plans compound vs. clobber

From a comment I wrote in TweenPerformerWeb:

I'm being super naïve here because I'm not entirely sure what the difference should be between to and by. from and to are absolute terms, but absolute relative to which coordinate space? Are they relative to wherever the element happens to be, to the viewport, or to the document? Does the answer differ for moves versus fade, scales, or rotations? If it's absolute to the document, and we receive a fadeIn, how do we handle cases where an ancestor has a translucent opacity?

Also relevant, plans may not arrive at the same time, e.g. if a moveTo() is executing and a moveBy() arrives in the middle, the by should probably compound onto the to.

Coordinate spaces for moves

When executing a move, it can be relative to the viewport or to the document. The difference is apparent when scrolling: document-relative moves will scroll with the document; whereas, viewport-relative moves will not.

There are situations where you'd want either/both. For instance, the photos demo should probably be viewport-relative when a photo is expanding, but document-relative when it's collapsing.

Which do we want to do by default? What syntax should we use for specifying this? Perhaps

  • move().to({ x: 200, y: 200 }).in(DOCUMENT), and
  • move().to({ x: 200, y: 200 }).in(VIEWPORT)?

high priority: choose a focus project for the web

This is a high priority task. Let's use this thread to discuss ideas.

Some places in which we can make an impact:

  • Documentation for material motion. Explaining the ideas and providing interactive examples to be played with.

Scroll position

What happens if somebody tries to scroll during a transition? Likely want to let the author specify a behavior (though choose a good default).

How much of scroll position between states should we manage? How much of it should be left to the author's routing framework?

TimeStream

Use RxJS to build a time stream that emits timestamps from requestAnimationFrame.

z-indexing, stacking contexts, and replicas

Stacking contexts and z-indexing are expected to be some of the harder complications of bringing Directors to the Web.

Expected Ramifications

  • Replicas may not be feasible, as correctly interleaving replica elements to match the elements they clone will be quite challenging.
  • Directors will likely need to pick one view to be on top and another to be on bottom. Interleaving views between the two is likely infeasible.

Remove dependency on Immutable

For the stuff we're doing now, we can do this:

return {
  ...oldDict,
  key: newValue,
}

everywhere we are using ImmutableMap now and save 50K on our library size by not-depending on immutable. We also get to use real Flow annotations.

Sadly, we don't get to use Immutable's collections functions (like map), but we should be able to use Rx for a lot of that.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.