Coder Social home page Coder Social logo

swup / scroll-plugin Goto Github PK

View Code? Open in Web Editor NEW
3.0 3.0 8.0 1.03 MB

A swup plugin for smooth scrolling 🏄‍♂️

Home Page: https://swup.js.org/plugins/scroll-plugin

License: MIT License

TypeScript 100.00%
swup plugin scroll page-transitions animation acceleration

scroll-plugin's Introduction

Swup Scroll plugin

A swup plugin for customizable smooth scrolling.

  • Enables acceleration-based smooth scrolling
  • Animates scroll position between page visits
  • Animates scrolling to anchors
  • Define a custom offset for scroll positions
  • Emulate scroll target selector

Installation

Install the plugin from npm and import it into your bundle.

npm install @swup/scroll-plugin
import SwupScrollPlugin from '@swup/scroll-plugin';

Or include the minified production file from a CDN:

<script src="https://unpkg.com/@swup/scroll-plugin@3"></script>

Usage

To run this plugin, include an instance in the swup options.

const swup = new Swup({
  plugins: [new SwupScrollPlugin()]
});

Behavior

Scroll Plugin works out of the box for sites where the window is the main scroll container, scrolling back up on page visits and restoring the previous position on browser history visits.

Scroll containers

If your site has other scroll containers than the window, like overflowing divs, the plugin will happily restore their scroll positions as long as you let it know about those containers. You can either add the attribute [data-swup-scroll-container] to them or use the scrollContainers option to configure a custom selector.

Reset vs. restore

On each page navigation, the plugin will reset the scroll position to the top just like the browser would. On backword/forward history visits, it will restore the previous scroll position that was saved right before leaving the page.

You can customize when to reset vs. restore while clicking a link using the shouldResetScrollPosition option. A common use case would be a custom back button: clicking it would normally reset the scoll position to the top while users would expect it to restore the previous scroll position on the page the link points towards.

Options

doScrollingRightAway

doScrollingRightAway defines if swup is supposed to wait for the replace of the page to scroll to the top.

animateScroll

animateScroll defines whether the scroll animation is enabled or swup simply sets the scroll without animation instead. Passing true or false will enable or disable all scroll animations. For finer control, you can pass an object:

{
  animateScroll: {
    betweenPages: true,
    samePageWithHash: true,
    samePage: true
  }
}

💡 We encourage you to respect user preferences when setting the animateScroll option:

// Using a simple boolean...
{
  animateScroll: !window.matchMedia('(prefers-reduced-motion: reduce)').matches
}
// ...or this little monster, with full control over everything:
{
  animateScroll: window.matchMedia('(prefers-reduced-motion: reduce)').matches ? false : {
    betweenPages: true,
    samePageWithHash: true,
    samePage: true
  }
}

scrollFriction and scrollAcceleration

The animation behavior of the scroll animation can be adjusted by setting scrollFriction and scrollAcceleration.

getAnchorElement

Customize how the scroll target is found on the page. Defaults to standard browser behavior (#id first, a[name] second).

{
  // Use a custom data attribute instead of id
  getAnchorElement: (hash) => {
    hash = hash.replace('#', '')
    return document.querySelector(`[data-scroll-target="${hash}"]`)
  }
}

markScrollTarget

Due to certain limitations of the History API, the :target CSS pseudo-class stops working on sites that push their own history entries, which includes any site using swup. Enabling this option provides an alternative way of styling the current target element.

Navigating to the URL /index.html#section2 will make the following element the target element:

<section id="section2">Example</section>

To highlight the current target element, use the data-swup-scroll-target attribute for styling:

[data-swup-scroll-target] {
  outline: 5px auto blue;
}

Offset

Offset to substract from the final scroll position, to account for fixed headers. Can be either a number or a function that returns the offset.

{
  // Number: fixed offset in px
  offset: 30,

  // Function: calculate offset before scrolling
  offset: () => document.querySelector('#header').offsetHeight,

  // The scroll target element is passed into the function
  offset: target => target.offsetHeight * 2,
}

scrollContainers

Customize the selector string used for finding scroll containers other than the window. See the Scroll Containers section for an explanation of how the plugin deals with overflowing containers.

{
  // Always restore the scroll position of overflowing tables and sidebars
  scrollContainers: '.overflowing-table, .overflowing-sidebar'
}

shouldResetScrollPosition

Callback function that allows customizing the behavior when a link is clicked. Instead of scrolling back up on page visits, returning false here will instead restore the previous scroll position recorded for that page. See Reset vs. restore for an explanation and use cases.

{
  // Don't scroll back up for custom back-links, mimicking the browser back button
  shouldResetScrollPosition: (link) => !link.matches('.backlink')
}

Default options

new SwupScrollPlugin({
  doScrollingRightAway: false,
  animateScroll: {
    betweenPages: true,
    samePageWithHash: true,
    samePage: true
  },
  scrollFriction: 0.3,
  scrollAcceleration: 0.04,
  getAnchorElement: null,
  markScrollTarget: false,
  offset: 0,
  scrollContainers: `[data-swup-scroll-container]`,
  shouldResetScrollPosition: (link) => true
});

Methods on the swup instance

Scroll Plugin adds the method scrollTo to the swup instance, which can be used for custom scrolling. The method accepts a scroll position in pixels and a boolean whether the scroll position should be animated:

// will animate the scroll position of the window to 2000px
swup.scrollTo(2000, true);

Hooks

The plugin adds two new hooks scroll:start and scroll:end :

swup.hooks.on('scroll:start', () => console.log('Swup started scrolling'));
swup.hooks.on('scroll:end', () => console.log('Swup finished scrolling'));

Overwriting swup.scrollTo

You can overwrite the scroll function with your own implementation. This way, you can gain full control over how you animate your scroll positions. Here's an example using GSAP's ScrollToPlugin:

import Swup from 'swup';
import SwupScrollPlugin from '@swup/scroll-plugin';

import { gsap } from 'gsap';
import ScrollToPlugin from 'gsap/ScrollToPlugin';
gsap.registerPlugin(ScrollToPlugin);

const swup = new Swup({
	plugins: [new SwupScrollPlugin()]
});

/**
 * Overwrite swup's scrollTo function
 */
swup.scrollTo = (offsetY, animate = true) => {
	if (!animate) {
		swup.hooks.callSync('scroll:start', undefined);
		window.scrollTo(0, offsetY);
		swup.hooks.callSync('scroll:end', undefined);
		return;
	}

	/**
	 * Use GSAP ScrollToPlugin for animated scrolling
	 * @see https://greensock.com/docs/v3/Plugins/ScrollToPlugin
	 */
	gsap.to(window, {
		duration: 0.8,
		scrollTo: offsetY,
		ease: 'power4.inOut',
		autoKill: true,
		onStart: () => {
			swup.hooks.callSync('scroll:start', undefined);
		},
		onComplete: () => {
			swup.hooks.callSync('scroll:end', undefined);
		},
		onAutoKill: () => {
			swup.hooks.callSync('scroll:end', undefined);
		},
	});

};

scroll-plugin's People

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

scroll-plugin's Issues

`swup.scrollTo()` method needs review

While cleaning up I noticed that the docs about swup.scrollTo() are outdated:

https://swup.js.org/api/methods#scrollto

Which lead me to think closer about it. The current implementation in Scroll Plugin allows setting the animated argument, which doesn't really make sense here, since animating the scroll position would be the whole point of using the method in the first place.

We should probably rather discuss if we want to allow targeting a specific element to be scrolled, something like this (Pseudo Code):

swup.scrollTo(2000, {target: window|HtmlElement})

Making this possible would need enhancements of gmrchk/scrl, to add the possibility to target and handle specific elements other than window.

This is somewhat related to #26 .

Add `ignore` option

Describe the problem

One cannot opt out of scrolling for individual links in a reliable way.

For example:

  • Upon first use, shouldResetScrollPosition: (link) => !link.matches('.pls-restore') scrolls to the top if the scroll position before was top
  • Even if it 'restores' scroll position, it still scrolls there - I'd like it to just remain at its position.

Describe the proposed solution

It would be great to have data-swup-no-scroll attribute tell the plugin to stay put and not scroll at all (being equivalent to not being used / vanilla swup behavior).

Alternatives considered

See #3 for an alternative (yet overly complicated) implementation.

How important is this feature to you?

  • Nice to have
  • Would make my life a lot easier
  • I cannot use swup without it

Disable smooth scroll for certain links

Hi,

Loving the new plugin setup, got upgraded from v1 and sorted really quickly. Just one outstanding issue, I am using the smooth scroll plugin on the site, but don't want to use it for some specific links. This is the code I used to achieve this with v1:

// Don't smooth scroll gallery filter links

  var galleryLink = document.querySelectorAll('a.filter-link');

  for (var i = 0, len = galleryLink.length; i < len; i++) {
    galleryLink[i].addEventListener('click', function(){
      swup.options.scroll = false;
    });
  }

document.addEventListener('swup:animationInDone', function() {
    swup.options.scroll = true;
});

What is the best option in Swup v2 for excluding specific links from the scroll behaviour?

Thanks - really enjoying working with Swup :)

Make `shouldResetScrollPosition` work for non-link requests

  • Currently, shouldResetScrollPosition passes in a link element
  • We should make sure this still works for form requests or API requests
  • The API of ignoreVisit probably makes sense to emulate here
options = {
  shouldResetScrollPosition: (url: string, { el, event }: { el?: Element; event?: Event }) => boolean
}

How important is this feature to you?

  • Nice to have
  • Would make my life a lot easier
  • I cannot use swup without it

I'm not even sure if this is a issue, but I need help

If I click on a link to load new page, and once the page is loaded, it will keep scroll position of old page.
Meaning, if my button is somewhere in middle or bottom of the 1st page, and I click it, the new page will be scrolled down already...
Is this by default?

PS.
I'm able to fix this with window.scrollTo(0, 0); when refreshing JS with swup events...but I want to understand if this is by purpose or Im doing something wrong, cause I cant see why would it keep scroll position

A11y: allow disabling animated scroll from visit object

Describe the feature

  • Add a visit.scroll.animate key that gets set right on visit:start by this plugin
  • Allow other plugins (mainly a11y-plugin) to unset the flag and disable animations

Alternatives considered

  • Use the existing suggested solution with matchMedia
  • As a user, I'd prefer having an option on the a11y-plugin to group such behavior in one place

How important is this feature to you?

  • Nice to have
  • Would make my life a lot easier
  • I cannot use swup without it

Scroll options not applied correctly

Description of the issue

With the switch to scroll:top and scroll:anchor, we've lost the ability to distinguish top/anchor scrolling on the same page vs. top/anchor scrolling after a page load. Inside the scroll:* handler, there is no way of knowing if the scrolling is part of a visit or a same-page hash-scroll. This means that the animateScroll option is applied incorrectly.

How to reproduce the issue

Set animateScroll to non-equal values:

animateScroll: {
  betweenPages: false,
  samePageWithHash: true,
  samePage: true
}

Feature Idea: Restore overflowing div's scroll position on popstate

Some of the sites I build make heavy use of overflowing divs. A good example where I used my own home-grown swup-like library: https://junge-akademie.adk.de/en/

Underneath the large teaser on the front page, two large overflowing divs scroll into view. Now if you scroll one or both of these divs down a bit, then navigate to one of the links and go back to the front page through the browsers back button, the div's previous scroll positions get restored automatically.

I would love to have this kind of feature available in swup, as well. Maybe the scroll plugin would be a good place to implement this? If you agree, I would try to implement it.

In the above example, I added a custom attribute [data-scrollable-id] to the overflowing divs, cached their scroll position and restored it on popstate. For swup, I could imagine an attribute like [data-swup-restore-scroll] and detecting popstate navigation on contentReplaced, which has an argument popstate for detecting navigation through the history API.

Before creating this issue, did you think of...:

  • Have you checked closed issues for similar/related problems.
  • Have you provided all helpful information available?
  • Have you considered creating a demo so we can help you better?

Emulate `:target` to allow styling anchor element

Describe the problem

When clicking a hash link on a page, swup scrolls to the matching target element. But the :target selector doesn't have any effect. This example will paint the element #my-element red without swup enabled, but won't do anything with swup.

<a href="#element">Scroll to element</a>

<div id="element">Element</div>

<style>
  :target {
    background: red;
  }
</style>

Describe the proposed solution

From the discussion of the original issue on the core repo:

Seems like fitting aria-current values could be location or simply true. I worry that using that attribute could interfere with consumer-scripts, though (they may be using the aria-current attribute already to mark an active tab or the like).

Maybe a simple swup-scroll-target or swup-current-target or swup-hash-target attribute would be less intrusive? In that case, it should be part of the Scroll Plugin, as you first suggested.

Research

A few links with prior discussions or solutions:

How important is this feature to you?

  • Nice to have
  • Would make my life a lot easier
  • I cannot use swup without it

Scrolls to incorrect position on Firefox when using anchor link

Hi Georgy, congrats on the refactor of Swup — it appears really well thought out! I really like the plugin architecture especially.

I've just built a new site using this version, but in testing have noticed that when using an anchor link from another page the Scroll Plugin isn't hitting the correct spot in Firefox. It scrolls to the correct position in Safari and Edge.

I'm using the following options (the issue occurs with doScrollingRightAway set to false as well):

    new SwupScrollPlugin({
      doScrollingRightAway: true, // default false
      scrollFriction: .35, // default 0.3
      scrollAcceleration: 0.04, // default 0.04
    })

Could this be a bug, or is there some sort of user error on my behalf I'm missing? Happy to provide a link to my staging site if that's helpful.

Thanks for your help.

Back button scroll restoration not working

I believe that in a previous version of this plugin the scroll position was always set to the top of the screen when navigating with the back button.

I have tried to set shouldResetScrollPosition: true on a new site I'm working on, but when navigating with the back button the scroll position is always remembered.

I have a scroll based animation on the homepage of a site that I always need to start at the top, so ideally I would like to completely disable the scroll restoration when navigating with the back button.

Current config

const swup = new Swup({
animateHistoryBrowsing: true,
plugins: [
new SwupJsPlugin(options),
new SwupBodyClassPlugin(),
new SwupScrollPlugin({
animateScroll: false,
doScrollingRightAway: false,
shouldResetScrollPosition: true,
}),
],
});

Is this possible to do? Thanks in advance.

animateScroll doesn't seem to work

Hi Georgy,

First of all. Great stuff on this plugin, it is really easy to use and works awesome!

I am trying to set animateScroll to false to disable the scrolling animation when navigating to another page.

However, it doesn't seem to work for me, no matter the value I use. The pages are always scrolling when I navigate to one and another as soon as I have a CTA that is positioned lower on a page.

My configuration is quite simple:

const swupOptions = {
    containers: ['#container'],
    plugins: [
        new SwupDebugPlugin(),
        new SwupBodyClassPlugin(),
        new SwupPreloadPlugin(),
        new SwupScrollPlugin({
            doScrollingRightAway: true,
            animateScroll: false,
            scrollFriction: 0.3,
            scrollAcceleration: 0.04
        }),
        new SwupJsPlugin(swupTransitions)
    ]
};

const swup = new Swup(swupOptions);

Thank you again for the plugin and any help!

Feature Idea: more fine-grained options for animateScroll

It would be nice if we had more fine-grained control over the behavior of animateScroll. For example, animating the scroll on samePage and samePageWithHash might be desired, but animating it on contentReplaced could not be wanted.

I have been able to hack this behavior together (smooth scrolling for samePage and samePageWithHash, no smooth scrolling for regular transitions) like this:

// safe the scrollPlugin instance, so we can access it later in the livecycle:
const scrollPlugin = new SwupScrollPlugin({
  animateScroll: false
});

// instantiate swup with the scrollPlugin instance:
const swup = new Swup({
  // ...
  plugins: [
    scrollPlugin,
  ]
});

// Disable the handlers for scrollPlugin by default:
swup.off('samePage', scrollPlugin.onSamePage);
swup.off('samePageWithHash', scrollPlugin.onSamePageWithHash);

// opt-in to smooth scrolling for `samePage` and `samePageWithHash`:
swup.on('samePage', e => {
  scrollPlugin.options.animateScroll = true
  scrollPlugin.onSamePage()
});
swup.on('samePageWithHash', e => {
  scrollPlugin.options.animateScroll = true
  scrollPlugin.onSamePageWithHash(e)
});

// reset animateScroll each time after a scroll
swup.on('scrollDone', e => {
  scrollPlugin.options.animateScroll = false
});

I could imagine an API like this:

const swup = new Swup({
  // ...
  plugins: [
    new SwupScrollPlugin({
      animateScroll: false,
      animateScrollSamePage: true,
      animateScrollSamePageWithHash: true,
    }),
  ]
});

Or, using an optional object for animateScroll:

const swup = new Swup({
  // ...
  plugins: [
    new SwupScrollPlugin({
      animateScroll: {
        default: false,
        samePage: true,
        samePageWithHash: true
      },
    }),
  ]
});

What would you prefer? I would try to put together a PR if you think this makes sense.

Before creating this issue, did you think of...:

  • Have you checked closed issues for similar/related problems.
  • Have you provided all helpful information available?
  • Have you considered creating a demo so we can help you better?

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.