Is it possible to implement a page transition using preact?

I've been experimenting with creating a page transition using preact-router. I attempted to use the preact-transition-group package along with the preact-css-transition-group package, but encountered an error. Despite this, here is my basic setup:

import { h, FunctionComponent } from 'preact';
import Router from 'preact-router';

const App: FunctionComponent = () => {
  return (
    <Router>
      <div path="/1" style="padding: 50px">
        Page 1 <a href="/2">Page 2</a>
      </div>
      <div path="/2" style="padding: 50px">
        Page 2 <a href="/1">Page 1</a>
      </div>
    </Router>
  );
};

A TypeScript and/or preact-router solution would be ideal, though not necessary.

Answer №1

Note: please keep in mind that this method may not work properly with nested routes.

When implementing this technique, you must enclose each route within a div element that has a specific class for animating the route. Make sure to set the key and path attributes to match the route path as shown below:

function wrapRoute(route: h.JSX.Element): h.JSX.Element {
  return ( // the 'in' class handles the animation while the 'page' class ensures smooth transition between old and new routes
    <div path={route.props.path} class="in page" key={route.props.path}>
      {route}
    </div>
  );
}

Next, you need to define two reactive variables like so:

const [previousEl, setPreviousEl] = useState<ComponentChildren | null>(null);
const [outEl, setOutEl] = useState<JSX.Element | null>(null);

Subsequently, you should listen for the onChange event triggered by the preact-router. When detected, set the outEl variable to a div containing previousEl, complete with a class that animates the exit of the current route. Include an onAnimationEnd listener to reset outEl to null once the exit animation finishes. Lastly, update previousEl to e.current.props.children. Your router component handler will resemble the following:

<Router onChange={(e) => {
  if (previousEl) {
    setOutEl(
      <div class="out page" key={e.previous} onAnimationEnd={() => setOutEl(null)}>
        {previousEl}
      </div>
    );
  }
  if (e.current) {
    setPreviousEl(e.current.props.children);
  }
}}
>
{routes} // an array containing all wrapped routes.
</Router>

Lastly, create the necessary animations by referencing the example in app.sass provided below.

Here's the detailed example:

app.tsx

// Code mentioned above included here...

app.sass

.page
  position: absolute
  top: 0
  bottom: 0
  left: 0
  right: 0

.out
  animation: out 200ms forwards

@keyframes out
  0%
    opacity: 1
    transform: translateX(0)
  100%
    opacity: 0
    transform: translateX(100vw)

.in
  animation: in 200ms forwards

@keyframes in
  0%
    opacity: 0
    transform: translateX(-100vw)
  100%
    opacity: 1
    transform: translateX(0)

Answer №2

A great method for creating animations is to incorporate a Higher Order Component (HOC) that envelops your page and serves as the router target. It could look something like this:

import { h, FunctionComponent } from 'preact';
import Router from 'preact-router';

const App: FunctionComponent = () => {
  return (
    <Router>
      <PageAnimation path="/1">
          <div style="padding: 50px">
            page 1 <a href="/2">page 2</a>
          </div>
      </PageAnimation>
      <PageAnimation path="/2">
        <div style="padding: 50px">
          page 2 <a href="/1">page 1</a>
        </div>
      </PageAnimation>
    </Router>
  );
};

If you want details on implementation, check out How to make transition animation.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

How can you effectively filter a JSON array in JavaScript when the keys include special characters?

I received a JSON response that has the following structure: [ { "@level": "info", "@message": "Text" }, { "@level": "error", "@message": &quo ...

How to Use Radio Buttons in Material-UI to Disable React Components

Just starting out with ReactJS and Material-UI, I've been experimenting with them for about 3 weeks. Currently, I'm facing a challenge where: - There are 2 radio buttons in a group and 2 components (a text field and a select field); - The goal is ...

Wildcard whitelisting in middleware systems

Currently, my app has a global middleware that is functioning properly. However, I am now looking to whitelist certain URLs from this middleware. The URLs that I need to whitelist have the format api/invitation/RANDOMSTRING. To achieve this, I considered ...

I'm confused why this particular method within a class is not being inherited by the next class. Rather than seeing the expected extension, I am presented with - [Function (

Working fine with the Person class, the register() function displays the correct return statement when logged in the console. However, upon extending it to the Employee class, instead of the expected return statement, the console logs show [Function (anon ...

Utilize Vue.js to send bound data to a click event

I am currently working with a v-for loop that iterates over data fetched from an API, setting the key as the ID of each object. My goal is to create a click event that captures the v-bind:key value of the clicked element. This will allow me to locate all t ...

Once the AJAX callback is complete, the onblur event will revert back to the original field or the updated field

I am currently working with an AJAX callback: Here is the HTML snippet: <a onCLick="loadXMLDoc(id1,id2)">(Add)</a> This function triggers an AJAX method that replaces the "(Add)" text with a basic HTML input field. Subsequently, when there ...

Attempting to open and display the contents of a text file (.txt) within a modal dialog box upon clicking a button

I am attempting to develop a modal that will appear when the user clicks on a specific button. The goal is for this modal to showcase text retrieved from a separate file stored on the server. My aim is to show this text within a dialog box modal. So far, ...

Tips for attaching an event listener to a div element that is accessed by reference in a React and TypeScript environment

I am attempting to attach an event listener to the div element using a ref. In my code, I have added a ref called div_ref to the Wrapper div and accessed that div_ref in the enableDragEventListeners method to add event listeners to it on component mount. ...

Using Typescript to retrieve a property by its name using a string as a generic type

I messed up the title, not sure the exact term for what I am trying to achieve. Type constraints are a bit confusing to me right now as I'm learning them in both F# and Typescript at the same time. I have a variable called interface state that contai ...

Tips for utilizing ng class within a loop

Having some trouble with my template that loops through a JSON file using json server. The issue I'm facing is related to correctly applying ng class when clicking on icons. Currently, when I click on an icon, it adds a SCSS class but applies it to al ...

Ways to display a jQuery alert when navigating on a webpage

I am looking to enhance my page navigation by implementing a jQuery confirmation message with 'Yes' and 'No' buttons. This way, when I navigate from one page to another, I can decide whether to proceed or stay on the current page. Inst ...

When you hover over the button, it seamlessly transitions to a

Previously, my button component was styled like this and it functioned properly: <Button component={Link} to={link} style={{ background: '#6c74cc', borderRadius: 3, border: 0, color: 'white', height: 48, padding: '0 ...

Managing "unprocessed information" in a Node.js environment and transferring the information through a Node Express endpoint

Currently, I am in the process of making an API call to retrieve a file using axios: async function fetchData() { const configuration = {}; // { responseType: 'stream'}; const { response } = await axios.get(URL, configuration); c ...

Guide on efficiently mapping an array containing arrays and simply retrieving the result

I am working with an array of arrays and I need to extract the values from each array. However, when I try to map over the arrays, I end up with just a single array and I'm not sure how to access the individual values. const arr = [ [1, 2, 3], ...

Utilizing a Search feature with Express and Node within the MVC framework

On the customers page of my website, I have implemented a search bar that sends the admin's input as a GET request. The goal is to find all data in the MySQL database that includes the entered string within the fullname field. The website is built us ...

Leveraging User Location in Angular 6 using API with OpenLayers

I am currently developing a mapping application using Angular (6) and OpenLayers (4.6.5) with the initial aim of retrieving user geolocation. To achieve this, I am utilizing a French API provided by the French Government for querying and obtaining GeoJSON ...

Tips on effectively centering a wide div

After much experimentation, this is what I came up with: (function(d, s, id) { var js, fjs = d.getElementsByTagName(s)[0]; if (d.getElementById(id)) return; js = d.createElement(s); js.id = id; js.src = "//connect.facebook.net/en ...

The functionality of ngModel seems to be malfunctioning when used within select options that are generated inside

I'm currently working on dynamically adding options to an HTML select element within a for loop. I am using [(ngModel)] to get the selected option, but initially no option is pre-selected. Here's a snippet of the code: <table align="center"& ...

Utilizing an Angular controller to fetch and parse JSON data

Trying to fetch JSON data and present it on a Jade template using an Angular controller. Controller : processJson.js var getJson = angular.module('getJson', []).controller('controller1', function ($scope, $http) { var url = '/ ...

Verify the position of the scrollbar without triggering any reflow

Here is a function I have: is_bottom: function() { if (settings.scrollBottomOffset === -1) { return false; } else { var scroll_height, scroll_top, height; scroll_height = ...