Jump to content
Search Community

Add Draggable to Horizontal Scroll

Eugene1984 test
Moderator Tag

Go to solution Solved by Eugene1984,

Recommended Posts

  • 3 weeks later...

Hi

 

Sorry, it's been a while.

 

I have a new issue. 

 

The timeline takes ages to load and when scrolling, near the end, the browser almost freezes. It's very glitchy. I suspect performance issues.

 

I can give you code to review, but unfortunately I can't send you a link as the project is still in development and not yet for public eyes.

 

Also note that I'm using Wordpress. I've built a custom array made up of 2 custom post types that appear on the timeline.

 

// GSAP
import {gsap} from 'gsap';
import {ScrollTrigger} from 'gsap/ScrollTrigger';
import {ScrollSmoother} from 'gsap/ScrollSmoother';
import {Draggable} from 'gsap/Draggable';
import {ScrollToPlugin} from 'gsap/all';

export function storyTimeline() {
    gsap.registerPlugin(
        ScrollTrigger,
        Draggable,
        ScrollSmoother,
        ScrollToPlugin
    );

    const panelsContainer = document.querySelector('.container');
    if (panelsContainer) {
        const sections = gsap.utils.toArray('.panel');
        let maxWidth = 0;

        const getMaxWidth = () => {
            maxWidth = 0;
            sections.forEach((section) => {
                maxWidth += section.offsetWidth;
            });
        };
        getMaxWidth();
        ScrollTrigger.addEventListener('refreshInit', getMaxWidth);

        let scrollTween = gsap.to(sections, {
            x: () => `-${maxWidth - window.innerWidth}`,
            ease: 'none',
        });

        let horizontalScroll = ScrollTrigger.create({
            animation: scrollTween,
            trigger: panelsContainer,
            pin: true,
            scrub: true,
            end: () => `+=${maxWidth}`,
            invalidateOnRefresh: true,
        });

        var dragRatio = maxWidth / (maxWidth - window.innerWidth);

        var drag = Draggable.create('.proxy', {
            trigger: panelsContainer,
            type: 'x',
            allowContextMenu: true,
            onPress() {
                this.startScroll = horizontalScroll.scroll();
            },
            onDrag() {
                horizontalScroll.scroll(
                    this.startScroll - (this.x - this.startX) * dragRatio
                );
            },
        })[0];

        let storyLinks = document.querySelectorAll('.story');
        let years = document.querySelectorAll('.year');

        years.forEach((year) => {
            ScrollTrigger.create({
                trigger: year,
                containerAnimation: scrollTween,
                onEnter: () => year.classList.add('selected'),
                start: 'left 90%',
                ease: 'none'
            });
        });

        storyLinks.forEach((storyLink) => {
            ScrollTrigger.create({
                trigger: storyLink,
                containerAnimation: scrollTween,
                onEnter: () => storyLink.classList.add('selected'),
                start: 'left 80%',
                ease: 'none'
            });
        });

        gsap.to('.navbar .button', {
            opacity: 1,
            pointerEvents: 'visible',
            duration: 1,
            ease: 'none',
            scrollTrigger: {
                trigger: '.panel.story-board',
                containerAnimation: scrollTween,
                start: 'left center',
                toggleActions: 'play none none reset',
            },
        });

        document.querySelectorAll('.button-round').forEach((anchor) => {
            anchor.addEventListener('click', function (e) {
                e.preventDefault();
                let targetElem = document.querySelector(
                        e.target.getAttribute('href')
                    ),
                    y = targetElem;
                if (
                    targetElem &&
                    panelsContainer.isSameNode(targetElem.parentElement)
                ) {
                    let totalScroll =
                            scrollTween.scrollTrigger.end -
                            scrollTween.scrollTrigger.start,
                        totalMovement =
                            (sections.length - 1) * targetElem.offsetWidth;
                    y = Math.round(
                        scrollTween.scrollTrigger.start +
                            (targetElem.offsetLeft / totalMovement) *
                                totalScroll
                    );
                }
                gsap.to(window, {
                    scrollTo: {
                        y: y,
                        autoKill: false,
                    },
                    duration: 1,
                });
            });
        });
    }
};
<?php $timeline = get_timeline(); ?>

<section class="panel story-board" id="storyBoard">

    <ul class="years">
        <?php foreach ($timeline as $item) : ?>
            <?php if (!empty($item['lodge_ids']) || !empty($item['story_ids'])) : ?>
                <li class="year">
                    <?= (!empty($item['year'])) ? '<h3>' . $item['year'] . '</h3>' : ''; ?>
                    <?php $lodgeClass = (!empty($item['lodge_ids'])) ? 'has-lodge' : ''; ?>
                    <ul class="stories <?= $lodgeClass; ?>">
                        <?php if (!empty($item['lodge_ids'])) : ?>
                            <?php foreach ($item['lodge_ids'] as $lodgeId) : ?>
                                <li class="story lodge">
                                    <div class="inner">
                                        <div class="story-head">
                                            <div class="location">
                                                <h5><?= get_the_title($lodgeId); ?></h5>
                                                <p>
                                                    <?php
                                                    $terms = get_the_terms($lodgeId, 'location');

                                                    if ($terms && !is_wp_error($terms)) {
                                                        $single_term = reset($terms);
                                                        $term_name  = $single_term->name;

                                                        echo str_replace(' - ', ', ', $term_name);
                                                    }
                                                    ?>
                                                </p>
                                            </div>
                                            <div class="dated">
                                                <span class="opened">Opened</span>
                                                <span class="date">
                                                    <span><?= get_the_date('M', $lodgeId); ?></span>
                                                    <span>
                                                        <?php
                                                        $year = get_the_date('Y', $lodgeId);
                                                        echo substr($year, 0, 2) . '<br>' . substr($year, 2);
                                                        ?>
                                                    </span>
                                                </span>
                                            </div>
                                        </div>

                                        <?php if (has_post_thumbnail($lodgeId)) : ?>
                                            <div class="story-img">
                                                <?php $thumb_url =  get_the_post_thumbnail_url($lodgeId); ?>
                                                <img src="<?= $thumb_url; ?>" alt="Featured" class="lazyload visi" />
                                            </div>
                                        <?php endif; ?>

                                        <?php
                                        $post_content = get_post($lodgeId);
                                        $content = $post_content->post_content;
                                        if ($content) :
                                        ?>
                                            <div class="excerpt">
                                                <?= wpautop($content); ?>
                                            </div>
                                        <?php endif; ?>
                                    </div>
                                </li>
                            <?php endforeach; ?>
                        <?php endif; ?>

                        <?php
                        if (!empty($item['story_ids'])) :
                            foreach ($item['story_ids'] as $storyId) :
                                $storyDate = get_post_meta($storyId, 'story-date', true);
                                $storyTitle = get_the_title($storyId);
                                $storyGuest = get_post_meta($storyId, 'your-name', true);
                                $storyLodges = get_post_meta($storyId, 'lodge-visited', true);
                                $storyWords = get_post_meta($storyId, 'story-words', true);
                        ?>
                                <li class="story">
                                    <a href="#" class="story-link" data-story-id="<?= $storyId; ?>">
                                        <span class="story-date">
                                            <?php
                                            if ($storyDate) {
                                                $date = DateTime::createFromFormat('M Y', $storyDate);
                                                $formatted_date = $date->format('M Y');

                                                echo $formatted_date;
                                            }
                                            ?>
                                        </span>
                                        <?php
                                        $attachments = get_attached_media('', $storyId);
                                        if (!empty($attachments)) :
                                            $single_attachment = reset($attachments);
                                            $attachment_url = wp_get_attachment_url($single_attachment->ID);
                                            $file_ext = wp_check_filetype($attachment_url);
                                        ?>
                                            <div class="story-img">
                                                <?php
                                                if (($file_ext['ext'] === 'jpg' || $file_ext['ext'] === 'jpeg' || $file_ext['ext'] === 'png')) : ?>
                                                    <img src="<?= $attachment_url; ?>" alt="<?= $single_attachment->post_title; ?>" style="display: block; width: 100%;">
                                                <?php endif; ?>

                                                <?php if ($file_ext['ext'] === 'mp4' || $file_ext['ext'] === 'mov') : ?>
                                                    <video>
                                                        <source src="<?= $attachment_url; ?>" type="video/mp4">
                                                        Your browser does not support HTML video.
                                                    </video>
                                                <?php endif; ?>
                                            </div>
                                        <?php endif; ?>
                                        <div class="story-auth">
                                            <?php
                                            if ($storyGuest) {
                                                echo '<span>' . $storyGuest . '</span>';
                                            }
                                            if ($storyLodges) {
                                                echo '<span>' . str_replace(',', ', ', $storyLodges) . '</span>';
                                            }
                                            ?>
                                        </div>

                                        <div class="story-excerpt">
                                            <?= ($storyWords) ? wp_trim_words($storyWords, 20, '...') : ''; ?>
                                        </div>
                                    </a>
                                </li>
                            <?php endforeach; ?>
                        <?php endif; ?>
                    </ul>
                </li>
            <?php endif; ?>
        <?php endforeach; ?>
    </ul>

    <div class="story-tail">
        <h2>Pioneering spirit, enduring purpose</h2>
        <p>
            As we look to the next 30 years of our
            conservation journey, Lorem ipsum donor sit
            amet, consectetur adipiscing elit, sed do
            eiusmod tempor incididunt ut labore et dolore
            magna aliqua.
        </p>
    </div>
</section>

 

Link to comment
Share on other sites

It's pretty tough to troubleshoot without a minimal demo - the issue could be caused by CSS, markup, a third party library, your browser, an external script that's totally unrelated to GSAP, etc. Would you please provide a very simple CodePen or CodeSandbox that demonstrates the issue? 

 

Please don't include your whole project. Just some colored <div> elements and the GSAP code is best (avoid frameworks if possible). See if you can recreate the issue with as few dependancies as possible. If not, incrementally add code bit by bit until it breaks. Usually people solve their own issues during this process! If not, then at least we have a reduced test case which greatly increases your chances of getting a relevant answer.

 

Here's a starter CodePen that loads all the plugins. Just click "fork" at the bottom right and make your minimal demo

See the Pen aYYOdN by GreenSock (@GreenSock) on CodePen

 

If you're using something like React/Next/Vue/Nuxt or some other framework, you may find StackBlitz easier to use. We have a series of collections with different templates for you to get started on these different frameworks: React/Next/Vue/Nuxt.

 

Once we see an isolated demo, we'll do our best to jump in and help with your GSAP-specific questions. 

Link to comment
Share on other sites

Hi there -

 

Yeah, nothing looks like a red flag to me (aside from the fact that you're doing all the animation in CSS, which seems a little bit of an odd choice, using CSS and GSAP is a good way to get into a muddle and end up with conflicts between tweens and transition times)

 

This seems to be working great though

https://cdpn.io/pen/debug/eYQgEKd?authentication_hash=GnAnbWnXXZLA

 

If it's working in codepen then maybe the issue is elsewhere? Large images, other JS running at the same time and slowing stuff down? Other animations? 

Link to comment
Share on other sites

Okay, so I've removed all other JS and images, but it's still very slow. On some devices the page crashes. 

 

I've also removed the CSS animations and rather using GSAP for this.

 

I'm thinking of maybe using AJAX to load one item at a time on scroll. I've tried this, but it seems like the scrolltrigger needs to be reinitialised after the AJAX call. I've added the function in the success property so that the scrolltrigger gets initialised again, but this also doesn't work. Maybe someone can point me in the right direction for this? 

Link to comment
Share on other sites

Hi,

 

 

What you could try is use ScrollTrigger.refresh() after the Ajax call and once you are completely sure that the data returned from the Ajax call is reflected in the DOM, you can call the refresh method:

 

https://greensock.com/docs/v3/Plugins/ScrollTrigger/static.refresh()

 

Here is an example that uses an approach that emulates the approach of getting data from an API call:

See the Pen YzOzjbL by GreenSock (@GreenSock) on CodePen

 

Hopefully this helps.

Happy Tweening!

  • Like 1
Link to comment
Share on other sites

Hi @Rodrigo

 

Thanks for the reply. I've tried the refresh() method, but did not work for me. The killAll() method however did. I would really like to know why :D

 

I'm adding this here for reference and so that it can help others with a similar problem.

 

I hade to add the ScrollTrigger.killAll() method after the successful AJAX call. Then I've added a little timeout on the ScrollTrigger functions.

 

$.ajax({
  url: main_ajax_scripts.ajax_url,
  data: {
    action: 'get_more_years',
    yearNumber: yearNumber++,
    last_year: lastYear,
  },
  success: function (data) {
    $('#timelineContainer').append(data);
    ScrollTrigger.killAll();
    setTimeout(() => {
      storyTimeline();
    }, 500);
  },
  error: function (errorThrown) {
    console.log(errorThrown);
  },
});

 

Link to comment
Share on other sites

Hi,

 

The approach with the refresh method only works for the ScrollTrigger instances that were created before the ajax call, there is actually no need to re-create ScrollTrigger instances that existed before you make the API request, those have to be refreshed, that's why in the example you see this:

// Create a new batch just for the new content
createBatch(newContent);
ScrollTrigger.refresh();

You create the ScrollTrigger instances for the new content that is added after the API response and then you just run the refresh method.

 

Killing all existing ScrollTrigger instances and then create everything from scratch might not be the best option performance-wise.

 

Hopefully this helps.

Happy Tweening!

Link to comment
Share on other sites

  • Solution

Hi @Rodrigo

 

Thanks for your help, but I've managed to find the problem for the very slow loading time. So no need for AJAX anymore.

 

The first thing I spotted was that it only loading extremely slow in Chrome and not Safari or Firefox. I then did a couple of Lighthouse page speed analysis, and removed unused stylesheets etc...

 

It was the damn "wp-block-library" stylesheet. I've dequeued this and problem solved!

  • Thanks 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...