Learus / react-material-ui-carousel

A Generic carousel UI component for React using Material UI.
MIT License
552 stars 220 forks source link

Incorrect Slide direction with two items #192

Open darynl opened 2 years ago

darynl commented 2 years ago

If there are only two items, the carousel slides the wrong direction.

alamit commented 2 years ago

I am facing the same issue. From a little research, it comes from this part of the code in CarouselItem.tsx:

  // Handle animation directions and opacity given based on active, prevActive and this item's index
  const { active, next: isNext, prevActive } = state;
  let animate = 'center';
  if (index === active) {
    animate = 'center';
  } else if (index === prevActive) {
    animate = isNext ? 'leftwardExit' : 'rightwardExit';
    if (active === maxIndex && index === 0) animate = 'rightwardExit';
    if (active === 0 && index === maxIndex) animate = 'leftwardExit';
  } else {
    animate = index < active ? 'leftOut' : 'rightOut';
    if (active === maxIndex && index === 0) animate = 'rightOut';
    if (active === 0 && index === maxIndex) animate = 'leftOut';
  }

I have been trying to debug without success as I am not very experienced with framer-motion, from what I understand the issue comes from the fact that when only 2 items are provided, the previous and the next element becomes the same which is not handled by the bool logic above, only allowing an item to be a prev or a next.

I stumbled across this example that is using some pagination, but my knowledge with those libs is too limited to create a PR.

Interested to collaborate on the matter though.

alamit commented 2 years ago

I actually came up with a quick hack to handle this, using a local version of the package. But I don't think it is a viable solution.

In Carousel.tsx:

[...]
  let sanitizedProps = sanitizeProps(props);
  let doubled = false;
  if (Array.isArray(sanitizedProps.children) && sanitizedProps.children.length === 2) {
    sanitizedProps = { ...sanitizedProps, children: [...sanitizedProps.children, ...sanitizedProps.children] };
    doubled = true;
  }
[...]
      {indicators ? (
        <Indicators
          length={Array.isArray(children) ? children.length / (doubled ? 2 : 1) : 0}
          active={doubled ? state.active % 2 : state.active}
          press={setNext}
          indicatorContainerProps={indicatorContainerProps}
          indicatorIconButtonProps={indicatorIconButtonProps}
          activeIndicatorIconButtonProps={activeIndicatorIconButtonProps}
          IndicatorIcon={IndicatorIcon}
        />
      ) : null}
AchillesTurtle commented 1 year ago

When I attempted to npm install the fork of ted537 #194 it couldn't find the module. I removed /dist from .gitignore and ran npm run build on my fork so it should be able to used directly by doing npm install https://github.com/AchillesTurtle/react-material-ui-carousel

I also changed the code a bit since the last solution makes the prevActive disappear instantly on click which is a little weird visually.

// Handle animation directions and opacity given based on active, prevActive and this item's index
const { active, next: isNext, prevActive } = state;
let animate = 'center';
if (index === active)
    animate = 'center';
else if (index === prevActive)
{
    animate = isNext ? 'leftwardExit' : 'rightwardExit';
    if (maxIndex > 1) {
        if (active === maxIndex && index === 0) animate = 'rightwardExit';
        if (active === 0 && index === maxIndex) animate = 'leftwardExit'
    }
}
else
{
    animate = index < active ? 'leftOut' : 'rightOut';
    if (maxIndex > 1) {
        if (active === maxIndex && index === 0) animate = 'rightOut';
        if (active === 0 && index === maxIndex) animate = 'leftOut';
    }
}

This handles slides as I wanted it to, but when setting cycleNavigation={true} it kind of shuffles instead which is not ideal.