rcbyr / keen-slider

The HTML touch slider carousel with the most native feeling you will get.
https://keen-slider.io/
MIT License
4.57k stars 210 forks source link

Mobile drag issues #396

Open tutods opened 6 months ago

tutods commented 6 months ago

Hi. I'm trying to do a slide with some cards, but in mobile isn't working.

https://github.com/rcbyr/keen-slider/assets/18479474/2deffb58-38a3-4847-b4bf-ba8ab54293b3

I have other sliders, but using images and it's ok, only this one I can't put it working.

My code:

'use client';

import { useMemo } from 'react';

import type { KeenSliderOptions } from 'keen-slider/react';

import { ProjectCard } from '@components/cards';
import { CarouselDot } from '@components/carousels/partials/Dot';
import { useCarousel } from '@hooks/useCarousel';
import type { HighlightedProjectsType } from '@shared/types';
import { cn } from '@utils';

type Props = {
  projects: HighlightedProjectsType;
  className?: string;
};

const SLIDER_OPTIONS = (totalLength: number): Partial<KeenSliderOptions> => ({
  breakpoints: {
    '(max-width: 768px)': {
      loop: true,
      mode: 'free-snap',
      slides: {
        number: totalLength,
        perView: 1,
        spacing: 10,
      },
    },
    '(min-width: 769px)': {
      loop: false,
      slides: {
        number: totalLength / 3,
        perView: 3,
        spacing: 10,
      },
    },
  },
  initial: 0,
  mode: 'snap',
  renderMode: 'performance',
  slides: {
    origin: 'auto',
  },
});

export const ProjectCarousel = ({ className = '', projects }: Props) => {
  const { currentSlide, instanceRef, loaded, sliderRef } = useCarousel(
    SLIDER_OPTIONS(projects.length),
  );

  const slidesLength = useMemo(
    () => instanceRef?.current?.track.details?.slides?.length ?? projects.length,
    [projects, instanceRef],
  );

  return (
    <section className={cn('flex flex-col gap-2 max-w-full', className)}>
      <div
        className="keen-slider flex cursor-grab justify-end gap-2.5"
        data-aos="fade-left"
        ref={sliderRef}
      >
        {projects.map(project => (
          <ProjectCard key={project._id} className="keen-slider__slide" {...project} />
        ))}
      </div>

      {loaded && !!instanceRef.current && (
        <nav className="flex items-center justify-end gap-2" data-aos="fade-left">
          {[...Array(slidesLength).keys()].map(idx => (
            <CarouselDot
              key={idx}
              id={idx}
              isCurrentSlide={currentSlide === idx}
              variant="light"
              onClick={() => {
                instanceRef.current?.moveToIdx(idx);
              }}
            />
          ))}
        </nav>
      )}
    </section>
  );
};
tutods commented 6 months ago

The ProjectCard component is very simple:

import Link from 'next/link';

import { Button } from '@components/buttons';
import { OptimizedImage } from '@components/images/OptimizedImage';
import { SpriteCategories } from '@enums';
import type { BasePageWithImageSelectionType } from '@shared/types/selections/Common';
import { cn } from '@utils/cn';

type Props = BasePageWithImageSelectionType & {
  className?: string;
};

export const ProjectCard = ({ className = '', headline, image, slug, title, ...props }: Props) => (
  <Link
    passHref
    className={cn('group', className)}
    data-aos="fade-up"
    href={`/projects/${slug}`}
    {...props}
  >
    <article className="relative isolate h-full min-h-[400px] w-full overflow-hidden rounded-lg shadow-sm lg:h-80">
      <OptimizedImage
        fill
        alt={title}
        className="-z-10 object-cover object-center transition-all duration-500 ease-in-out group-hover:scale-125"
        image={image}
      />

      <div className="absolute inset-0 -z-10 bg-gradient-to-b from-transparent to-zinc-900" />

      <div className="mt-auto flex h-full flex-col justify-end px-4 py-6 text-zinc-200">
        <h3 className="mb-1 text-lg font-semibold text-zinc-50">{title}</h3>
        {!!headline && <p className="line-clamp-3 text-sm">{headline}</p>}

        <Button
          className="mt-4 max-w-fit"
          variant="text"
          icon={{
            category: SpriteCategories.ARROWS,
            name: 'arrow-narrow-right',
            position: 'suffix',
          }}
        >
          Ver mais
        </Button>
      </div>
    </article>
  </Link>
);
tutods commented 6 months ago

The problem is solved, basically the problem is the display: flex. But now I have other problem.

'(min-width: 769px)': {
        loop: false,
        slides: {
          number: Math.ceil(projects.length / 2),
          perView: 2,
          spacing: 10,
        },
      },

If I use this number on slides to help me with navigation bullets (for example have 3 slides, should show 2 bullets - so length / 2), the slides appear this way: image