-
Notifications
You must be signed in to change notification settings - Fork 10.3k
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
Add a gatsby-page-transition component #5213
Comments
How about supporting INDIVIDUAL TRANSITION-STYLES PER PAGE? For example having the home-page "zoom", while having the about-us page sliding in from left-to-right? It would be really cool to have an easy solution for this |
Yeah, that's the idea with the presets — you'd able to pick a preset for each page component. |
@thebarty That should be possible with the above API. It might look something like this: export default ({ children, location }) => {
const transition = location.pathname === '/' ? 'zoom' : 'slide-left';
return (
<PageTransition effect={transition}>
{children}
</PageTransition>
);
}; |
@jlengstorf I tried your answer for individual pages for my |
@LekoArts Give that a shot and let me know if you have any trouble. If you're still having issues, please pass along a link to see your code so we can figure out what's up. Good luck! |
Then it should work 🤔 Because I want to use gatsby-node.js exports.createPages = ({ graphql, boundActionCreators }) => {
const { createPage } = boundActionCreators;
return new Promise((resolve, reject) => {
const docPage = path.resolve('src/templates/doc.jsx');
resolve(
graphql(`
{
// Content
}
`).then(result => {
if (result.errors) {
console.log(result.errors);
reject(result.errors);
}
const docsList = result.data.docs.edges;
docsList.forEach(project => {
createPage({
path: project.node.fields.slug,
component: docPage,
context: {
slug: project.node.fields.slug,
},
});
});
})
);
});
}; doc.jsx import React, { Component } from 'react';
import PropTypes from 'prop-types';
import styled from 'react-emotion';
import { css } from 'emotion';
import { TransitionGroup, CSSTransition } from 'react-transition-group';
import SEO from '../components/SEO';
import Wrapper from '../components/Wrapper';
import Content from '../components/Content';
import SideNav from '../components/SideNav';
const enter = css`
opacity: 0.01;
`;
const enterActive = css`
opacity: 1;
transition: opacity 250ms ease-in;
`;
const exit = css`
opacity: 1;
`;
const exitActive = css`
opacity: 0.01;
transition: opacity 250ms ease-out;
`;
const OuterHelper = styled.div`
position: relative;
`;
class TransitionHandler extends Component {
shouldComponentUpdate() {
return this.props.location.pathname === window.location.pathname;
}
render() {
return this.props.children;
}
}
const Doc = ({ pathContext, data, location }) => {
const docContent = data.markdownRemark;
return (
<React.Fragment>
<SEO docPath={pathContext.slug} docNode={docContent} docSEO />
<Wrapper>
<SideNav categories={data.allMarkdownRemark.edges} pathCont={pathContext} />
<TransitionGroup component={OuterHelper}>
<CSSTransition
classNames={{ enter, enterActive, exit, exitActive }}
timeout={{ enter: 250, exit: 250 }}
key={location.pathname}
>
<TransitionHandler location={location}>
<Content dangerouslySetInnerHTML={{ __html: docContent.html }} />
</TransitionHandler>
</CSSTransition>
</TransitionGroup>
</Wrapper>
</React.Fragment>
);
};
export default Doc; |
@LekoArts What version of Gatsby are you using? I think this may have changed in V1 -> V2, now that I'm thinking about it. @pieh @m-allanson @KyleAMathews can you confirm if If it's not available in V1, you'll need to pass it down from your layout component. |
|
@jlengstorf @pieh I was able to reproduce it with the gatsby starter, you can clone it here: I'm doing the same there: I'm expecting when clicking the links at the top of the template page, that the content below fades in/out. |
@LekoArts I think the problem might be a missing wrapper div. I had a similar issue and was able to fix it with an extra div: That component works, so if it's not the wrapper, you can potentially just pull that component out as-is and implement it as shown in my layout here: |
@jlengstorf I'm already wrapping it with an additional div: I also copied your component, no change either. Please make sure to test it in an template, not layout/page file :) |
@jlengstorf I tried to fiddle around with it a bit more, but without luck :( It only works for me if I wrap children in the layout file. |
@LekoArts I'm looking at this, and for some reason the React component lifecycle methods just... aren't firing from the page templates. I don't know why, but I was able to get it working by moving the transitions to the layout component. (This kind of mystery is one of the reasons we wanted to remove layouts in V2.) I opened a PR with transitions working on your error repo: https://github.com/LeKoArts/gatsby-transition-error-example/pull/1 |
@jlengstorf Yeah, I know that. But for my usecase it's no option to transition the whole page. I have a fixed sidebar navigation and when I switch pages via this Nav I only want the text of content box (which is on the right side) to fade. It would just look too weird if the static/fixed sidebar would transition.
Then that's the thing which has to be inspected I guess. |
Yeah. This goes away in V2, so if you have a specialized use case it may be worth just upgrading. The migration guide is ready; if you wanted to give that a shot and open an issue with any feedback, it would be a huge help to us (making the migration guide better) and would hopefully help you clear up this transitions issue. |
I'll upgrade the example repo and see if it works as I want it to behave. |
@jlengstorf Upgraded the repo to v2 and now it's working as intended :) |
@jlengstorf Ok, discovered a new issue: After the start of the dev server the transition doesn't work when you visit the page for the first page. You can reproduce that with the example repo above: Start with gatsby develop, click on one of the blog items and switch between pages with the three links above the blog heading. When you visit e.g. "Hello World", the transition doesn't work. If you switch back and forth they'll work. |
@LekoArts Does this happen after |
Yeah, you were right. After |
@LekoArts that's something we want to fix — it shouldn't be doing that. From a conversation with @pieh and @KyleAMathews, it sounds like we're waiting for React Suspense to patch it, but it will be patched in the future. |
Hi @jlengstorf |
I like where this is going but I think the proposed API needs some work. export default ({ children, location }) => {
const transition = location.pathname === '/' ? 'zoom' : 'slide-left';
return (
<PageTransition effect={transition}>
{children}
</PageTransition>
);
}; I can imagine a massive switch/if else already and it's not pretty. The other problem is that with the proposed api, we can't roll with our own animation library if we don't want to use the built in gatsby animation library. I'd like be be able to switch it out as needed if a project calls for something else. Because of those two issues, being able to put the animation logic inside the page component or the template component seems key to me. That way each page can be a self contained component including it's own exit and entry animations. I did quite a bit of messing around trying to figure out how to do this recently and I discovered a couple things. The animation component seems to need to be wrapped around the route component and can't be a child of the route component. If the animation is inside the route it will suddenly unmount when the route switches and the exit animation will be cut off. Maybe there's a way around that but I couldn't figure it out. To me that means the transition has to be in the layout component. In my page component:import TransitionElement from './transitions';
const PageName = () => (
<div><h1>This is my page</h1></div>
);
PageName.defaultProps = {
transitionComponent: TransitionElement
};
export default PageName; In my layout component:export const WrapPageComponent = ({ element, props }) => {
const { key } = props.location;
const { transitionComponent: TransitionComponent } = element.props;
return (
<PoseGroup>
<TransitionComponent key={key} {...props}>
{element}
</TransitionComponent>
</PoseGroup>
);
}; Here's an example site and example repo As you can see, this works great! Using default props to do this seems super hacky though. A built in api for passing information from the page to the layout would be really great. Besides that the only other thing I can see that's missing is the ability for a page to tell the next page how long to delay before starting it's animation but that seems like it could be done easily with redux. Or maybe the context api? Is there a better way that already exists for me to have my page component tell my layout component which transition to wrap my page in? I'm thinking an api similar to how page graphql queries work would be awesome. If a page or template could just export a transition component that it should be wrapped in there wouldn't be need for much else since we already have a lot of great react animation libraries we can use. Actually if a page could export an object containing a transition component and how long to delay the next route and the layout component could use that, that would be pretty flexible. If not I can just keep using default props for this but it feels dirty. |
@riencoertjens I just sent over a PR with changes to your repo that will get transitions running. The short answer is that V2 changed the way page components were mounted and unmounted, so you need to use the @TylerBarnes I think you make a good point about the API, but I don't have a good solution off the top of my head. Maybe @pieh knows of a better way to pass elements up the tree to the page wrapper? |
Not sure if that's still a problem, but I remember having an issue where on each page transition router would jump to the top of the screen before transitioning the current page ( see this for more details: #7921 ). I think when adding a page transition it would be also necessary to prevent the scroll to top using |
@janczizikow I still have the same issue with the page jumping to the top prior to the transition finishing. This is particularly apparent when using a fixed nav bar. @jlengstorf or @pieh is the example code from @stefanprobst on #7921 still the best option to handle this via the const transitionDelay = 250
exports.shouldUpdateScroll = ({ pathname }) => {
// // We use a temporary hack here, see #7758
if (window.__navigatingToLink) {
window.setTimeout(() => window.scrollTo(0, 0), transitionDelay)
} else {
const savedPosition = JSON.parse(
window.sessionStorage.getItem(`@@scroll|${pathname}`)
)
window.setTimeout(() => window.scrollTo(...savedPosition), transitionDelay)
}
return false
} |
@ryanwiemer We can do a little better because now the saved scroll position and location.action are passed to |
@stefanprobst gotcha. So what would the improved snippet look like? Is there an example documented anywhere? |
The issue with page transition animations is that they only happen after the route has changed. This is why exit animations are harder than enter animations, and they work only because So some manual work is necessary, but should be quite straightforward with
Please report back if the docs should be clearer on this. const transitionDelay = 250
exports.shouldUpdateScroll = ({
routerProps: { location },
getSavedScrollPosition,
}) => {
if (location.action === 'PUSH') {
window.setTimeout(() => window.scrollTo(0, 0), transitionDelay)
} else {
const savedPosition = getSavedScrollPosition(location)
window.setTimeout(
() => window.scrollTo(...(savedPosition || [0, 0])),
transitionDelay
)
}
return false
} |
@stefanprobst Thank you very much for your detailed explanation and example. I'm sure this will be helpful for others as well. |
@jlengstorf I just revisited this to see what else I could come up with and I found gatsby-transformer-javascript-frontmatter which theoretically is exactly what I want. The idea is I could pass time to delay unmounting the current route and time to delay mounting the next component as frontmatter and have that available as page context and then the animation could be handled within the page easily without the layout needing to contain any transitions. For example, I'm trying to get the following to start the next pages animation after 300ms and have the current page delay unmounting for 600ms, allowing for an overlapping fade. export const frontmatter = {
transition: {
delayMountingNext: 300,
delayUnmountingCurrent: 600
}
}; I'm a little stumped as to how to actually add this javascript frontmatter to my pages though. |
@TylerBarnes I just played with this a bit, and the instructions mention, but don't explain, that you need to have I've opened #9077 to track this. Please post any follow-up questions there to keep this issue focused on a single thread. Thanks! |
I am suuuuuper duper interested in this whole thing, as I am struggling mightily to get Gatsby to do what I want it to do for a baseline SPAPWA experience. +infinity that y'all awesome peeps are working this out :-) |
Over the weekend and the last couple nights I created a working page transitions plugin and I'm excited to share it! I had the idea that it actually makes sense for the Link component to take props describing what the transition should look like, since the link is already in charge of transitioning (although usually quite abruptly). So I came up with the idea of This idea allows for a lot of control and flexibility, and many different animations when needed. There's an example site with smooth / seamless transitions using gsap. @jlengstorf this is the code I mentioned which uses the context api for page transitions. Anyone interested can find a messy readme at the plugin repo with more details. It's really just a working proof of concept and I think the internals and the api need work. I'm new to react/gatsby so if anyone finds this interesting, please send ideas or PR's! |
@TylerBarnes this is awesome work. Would you be up for writing a blog post about this and showing off how to implement it? I think it would go over really well with our community! |
@jlengstorf I would absolutely love to! I'll clean up the plugin and example site and write up a post! Do you guys have any guidelines for blog posts or should I just write it and submit a PR? |
@TylerBarnes Great news! We've got guidelines for contributing to the blog written up here: https://www.gatsbyjs.org/docs/how-to-contribute/#contributing-to-the-blog Thanks so much! |
Old issues will be closed after 30 days of inactivity. This issue has been quiet for 20 days and is being marked as stale. Reply here or add the label "not stale" to keep this issue open! |
Hey again! It’s been 30 days since anything happened on this issue, so our friendly neighborhood robot (that’s me!) is going to close it. Please keep in mind that I’m only a robot, so if I’ve closed this issue in error, I’m Thanks again for being part of the Gatsby community! |
One of the benefits of removing layouts (see gatsbyjs/rfcs#2) is that we have a much simpler path to adding page transitions, which were a bit tricky and/or hacky in V1 (see discussion in #1854). This could also help solve #4919 and/or #3674.
For a new site I'm working on using Gatsby v2, I was able to get page transitions in place with the following code (thanks for the help @m-allanson and @KyleAMathews!):
The result is a fade transition between pages:
Default use case: no config, transition everything
The transitions are entirely contained within this component, which means we can probably package it up and create
gatsby-page-transitions
with an API that starts out with a sane default:Only add transitions for part of the page
This could be dropped into the middle of a layout to only transition the content that changes (this is what the page I'm building does):
Simplified customization: presets
If we make the component extendable, it would be possible for contributors to create presets for the component to allow simple-but-coarse customization of page transitions:
Advanced customization: expose all underlying APIs
And it could be configured with all the animation options for power users:
Effectively we could expose the entire API of the underlying tech if someone were inclined to customize all of it, but by default we make it a drop-in solution (similar to
gatsby-image
andgatsby-link
).This isn't a blocking issue for anything, but it would be nice to have, and could be another flashy effect to attract more attention to Gatsby the way Using Gatsby Image does.
The text was updated successfully, but these errors were encountered: