akiran / react-slick

React carousel component
http://react-slick.neostack.com/
MIT License
11.62k stars 2.09k forks source link

CenterMode with Infinite and Variable Width not centering properly #2347

Open joshuatuscan opened 4 months ago

joshuatuscan commented 4 months ago

The slider is sitting in a container that is 100% the width of the viewport and width is determined by each image element child which is ultimately governed by the height of the parent container. These are native images in an img tag so they proportionally scale their aspect ratio according to the height.

The centering is off from the center by a small amount that is variable on each item. I'm not sure what is wrong with the centering calculation, but it is wrong and producing off-center results. See several screenshots below.

Screenshot 2024-02-16 at 3 47 27 PM Screenshot 2024-03-06 at 8 47 55 PM Screenshot 2024-03-06 at 8 48 29 PM

PixelSnap 2024-03-06 at 20 55 58@2x

``

 const slide_settings = {
    infinite: true,
    slidesToScroll: 1,
    slidesToShow: 1,
    arrows: false,
    dots: false,
    speed: 400,
    swipe: true,
    swipeToSlide: true,
    easing: "ease-out",
    className: "projectslider",
    variableWidth: true,
    centerMode: true,
    centerPadding: "0px",
    afterChange: (index) => {
      setCurrentMedia(index);
    },
  };
.projectslider {
  height: 100%;
  padding: 0;

  .slick-list {
    height: 100%;

    .slick-track {
      height: 100%;

      .slick-slide {
        height: 100%;

        > div {
          height: 100%;
          width: auto;

          figure {
            outline: none;
            height: 100%;
            width: auto !important;
            margin: 0;
            padding: 0 25px;
            position: relative;
            z-index: 1;

            img,
            video {
              cursor: pointer;
              height: 100%;
              outline: none;
              width: auto;
            }
          }
       }
     }
   } 
  }
}
<div className={styles.inner}>
        <Slider className={"projectslider"} ref={sliderRef} {...slide_settings}>
          {mediaData.map((media, index) =>
            media.type === "videotype" ? (
              <figure onClick={() => goToSlide(index)} key={index}>
                <video autoPlay loop playsInline muted>
                  <source src={media.videoFile} type="video/mp4" />
                </video>
              </figure>
            ) : (
              <figure key={index} onClick={() => goToSlide(index)}>
                <img
                  src={media.largeImgURL + "?auto=format"}
                  alt={media.caption}
                />
              </figure>
            )
          )}
        </Slider>
      </div>

You can see a live version here.

fzn0x commented 2 months ago

This is 2024 and I don't want anyone to wrap their head around this issue.

The trick is using afterChange.

    const settings = {
        className: "center slider variable-width slick-gradient",
        centerMode: true,
        infinite: true,
        slidesToShow: 1,
        speed: 100,
        rows: 1,
        slidesPerRow: 1,
        arrows: false,
        centerPadding: "100px",
        beforeChange: (_oldIndex, newIndex) => {
            // ...
        },
        afterChange: (currentIndex) => {
            // Select the element with the class 'slick-track'
            var element = document.querySelector('.slick-track');

            // Get the current transform value
            var currentTransform = getComputedStyle(element).transform;

            // Extract the x translation value. The match function attempts to find the numbers in the matrix transform, which are separated by commas.
            // The matrix format is matrix(a, b, c, d, tx, ty) where tx is our x translation.
            // If the transform is in the translate3d format, it should still match correctly.
            var match = currentTransform.match(/matrix\(([^,]+),([^,]+),([^,]+),([^,]+),([^,]+)/);

            if (match) {
                var currentX = parseFloat(match[5]); // Get the x translation value

                // Calculate the new x value by adding 45
                var newX = currentX + 45;

                // Set the new transform value
                element.style.transform = 'translate3d(' + newX + 'px, 0px, 0px)';
            } else {
                console.log("Could not parse the current transform value.");
            }
        },
        variableWidth: true,
    };

Consider to follow me if this solves your issue. :D

fzn0x commented 2 months ago

I've been noticed that the transform was substracted by 45

so I added it back again after any slides and it solves the issue.

joshuatuscan commented 2 months ago

This is 2024 and I don't want anyone to wrap their head around this issue.

The trick is using afterChange.

    const settings = {
        className: "center slider variable-width slick-gradient",
        centerMode: true,
        infinite: true,
        slidesToShow: 1,
        speed: 100,
        rows: 1,
        slidesPerRow: 1,
        arrows: false,
        centerPadding: "100px",
        beforeChange: (_oldIndex, newIndex) => {
            // ...
        },
        afterChange: (currentIndex) => {
            // Select the element with the class 'slick-track'
            var element = document.querySelector('.slick-track');

            // Get the current transform value
            var currentTransform = getComputedStyle(element).transform;

            // Extract the x translation value. The match function attempts to find the numbers in the matrix transform, which are separated by commas.
            // The matrix format is matrix(a, b, c, d, tx, ty) where tx is our x translation.
            // If the transform is in the translate3d format, it should still match correctly.
            var match = currentTransform.match(/matrix\(([^,]+),([^,]+),([^,]+),([^,]+),([^,]+)/);

            if (match) {
                var currentX = parseFloat(match[5]); // Get the x translation value

                // Calculate the new x value by adding 45
                var newX = currentX + 45;

                // Set the new transform value
                element.style.transform = 'translate3d(' + newX + 'px, 0px, 0px)';
            } else {
                console.log("Could not parse the current transform value.");
            }
        },
        variableWidth: true,
    };

Consider to follow me if this solves your issue. :D

This actually didn't solve the problem at all, made it worse by shifting everything further and further to the right of center as you moved through the slides. Seems to have compounding result.

fzn0x commented 2 months ago

This is 2024 and I don't want anyone to wrap their head around this issue. The trick is using afterChange.

    const settings = {
        className: "center slider variable-width slick-gradient",
        centerMode: true,
        infinite: true,
        slidesToShow: 1,
        speed: 100,
        rows: 1,
        slidesPerRow: 1,
        arrows: false,
        centerPadding: "100px",
        beforeChange: (_oldIndex, newIndex) => {
            // ...
        },
        afterChange: (currentIndex) => {
            // Select the element with the class 'slick-track'
            var element = document.querySelector('.slick-track');

            // Get the current transform value
            var currentTransform = getComputedStyle(element).transform;

            // Extract the x translation value. The match function attempts to find the numbers in the matrix transform, which are separated by commas.
            // The matrix format is matrix(a, b, c, d, tx, ty) where tx is our x translation.
            // If the transform is in the translate3d format, it should still match correctly.
            var match = currentTransform.match(/matrix\(([^,]+),([^,]+),([^,]+),([^,]+),([^,]+)/);

            if (match) {
                var currentX = parseFloat(match[5]); // Get the x translation value

                // Calculate the new x value by adding 45
                var newX = currentX + 45;

                // Set the new transform value
                element.style.transform = 'translate3d(' + newX + 'px, 0px, 0px)';
            } else {
                console.log("Could not parse the current transform value.");
            }
        },
        variableWidth: true,
    };

Consider to follow me if this solves your issue. :D

This actually didn't solve the problem at all, made it worse by shifting everything further and further to the right of center as you moved through the slides. Seems to have compounding result.

Adds code for mobile and go to previous slide as well, see the result

// Calculate the new x value by adding 45
var newX = currentX + (device === "mobile" ? (currentSliderIndex > currentIndex ? -25 : 25) : (currentSliderIndex > currentIndex ? -45 : 45));
fzn0x commented 2 months ago

You need to check your own tranform TX value changes.