为什么第一个和最后一个元素之间的距离减小?

Why is the distance between first and last element decreasing?

我正在尝试制作 image slider。但是正如您所看到的,第一个元素和最后一个元素之间的距离并不一致。如果继续向左拖动,距离会减小;如果继续向右拖动,距离会增加。看起来代码在不同的缩放级别上表现不同(有时?)因此每个元素之间的距离有时会发生变化。

//project refers to placeholder rectangular divs

projectContainer = document.querySelector(".project-container")
projects = document.querySelectorAll(".project")

elementAOffset = projects[0].offsetLeft;
elementBOffset = projects[1].offsetLeft;
elementAWidth = parseInt(getComputedStyle(projects[0]).width)
margin = (elementBOffset - (elementAOffset + elementAWidth))

LeftSideBoundary = -(elementAWidth)
RightSideBoundary = (elementAWidth * (projects.length)) + (margin * (projects.length))
RightSidePosition = RightSideBoundary - elementAWidth;

initialPosition = 0; //referring to mouse 
mouseIsDown = false

projectContainer.addEventListener("mousedown", e => {
    mouseIsDown = true
    initialPosition = e.clientX;
})

projectContainer.addEventListener("mouseup", e => {
    mouseExit(e)
})

projectContainer.addEventListener("mouseleave", e => {
    mouseExit(e);
})

function mouseExit(e) {
    mouseIsDown = false

    //updates translateX value of transform
    projects.forEach(project => {
        var style = window.getComputedStyle(project)
        project.currentTranslationX = (new WebKitCSSMatrix(style.webkitTransform)).m41
        project.style.transform = 'translateX(' + (project.currentTranslationX) + 'px)'
    })
}

projectContainer.addEventListener("mousemove", e => {
    if (!mouseIsDown) { return };

    // adds mousemovement to translateX
    projects.forEach(project => {
        project.style.transform = 'translateX(' + ((project.currentTranslationX ?? 0) + (e.clientX - initialPosition)) + 'px)'
        shiftPosition(e, project)
    })
})

//teleports div if it hits left or right boundary to make an infinite loop
function shiftPosition(e, project) {
    projectStyle = window.getComputedStyle(project)
    projectTranslateX = (new WebKitCSSMatrix(projectStyle.webkitTransform)).m41
    //projectVisualPosition is relative to the left border of container div
    projectVisualPosition = project.offsetLeft + projectTranslateX

    if (projectVisualPosition <= LeftSideBoundary) {
        project.style.transform = "translateX(" + ((RightSidePosition - project.offsetLeft)) + "px)"
        updateTranslateX(e);
    }
    if (projectVisualPosition >= RightSidePosition) {
        newPosition = -1 * (project.offsetLeft + elementAWidth)
        project.style.transform = "translateX(" + newPosition + "px)"
        updateTranslateX(e);
    }
}

function updateTranslateX(e) {
    projects.forEach(project => {
        style = window.getComputedStyle(project)
        project.currentTranslationX = (new WebKitCSSMatrix(style.webkitTransform)).m41

        project.style.transform = 'translateX(' + (project.currentTranslationX) + 'px)'
        initialPosition = e.clientX
    })
}
 *, *::before, *::after{
        margin:0px;
        padding:0px;
        box-sizing: border-box;
        font-size:0px;
        user-select: none;
    }
    
    .project-container{
        font-size: 0px;
        position: relative;
        width:1500px;
        height:400px;
        background-color: rgb(15, 207, 224);
        margin:auto;
        margin-top:60px;
        white-space: nowrap;
        overflow: hidden;
        padding-left:40px;
        padding-right:40px;
    }
    
    .project{
        font-size:100px;
        margin:40px;
        display: inline-block;
        height:300px;
        width:350px;
        background-color:red;
        border: black 3px solid;
        user-select: none;
    }
        <div class="project-container">
            <div class="project">1</div>
            <div class="project">2</div>
            <div class="project">3</div>
            <div class="project">4</div>
            <div class="project">5</div>
            <div class="project">6</div>
            <div class="project">7</div>
            <div class="project">8</div>
        </div>

我不确定您将如何修复实施。我玩了一会儿,发现了一些东西;拖得越快,位移越严重,位移似乎主要发生在元素被传送到容器两端的时候。

我猜这主要是因为您要遍历所有元素并将它们单独隔开。鼠标移动事件通常相隔不到 20 毫秒,并且您依赖于所有 DOM 元素在注册下一个移动之前使用其新的变换位置重新绘制。

我确实想出了一种使用绝对放置元素和 IntersectionObserver API 的不同方法,现在所有现代浏览器都支持这种方法。这里的想法基本上是,当每个元素与容器的边缘相交时,它会触发数组查找以查看序列中的下一个元素是否在正确的一端,如果不是,则将其移动到那里。元素仅由静态变量隔开,而滑动它们的工作将传递给新的父包装器 .project-slider.

window.addEventListener('DOMContentLoaded', () => {
  // Style variables
  const styles = {
    width: 350,
    margin: 40
  };
  const space = styles.margin*2 + styles.width;

  // Document variables
  const projectContainer = document.querySelector(".project-container");
  const projectSlider = document.querySelector(".project-slider");
  const projects = Array.from(document.querySelectorAll(".project"));

  // Mouse interactions
  let dragActive = false;
  let prevPos = 0;

  projectContainer.addEventListener('mousedown', e => {
    dragActive = true;
    prevPos = e.clientX;
  });

  projectContainer.addEventListener('mouseup', () => dragActive = false);

  projectContainer.addEventListener('mouseleave', () => dragActive = false);

  projectContainer.addEventListener('mousemove', e => {
    if (!dragActive) return;

    const newTrans = projectSlider.currentTransX + e.clientX - prevPos;

    projectSlider.style.transform = `translateX(${newTrans}px)`;
    projectSlider.currentTransX = newTrans;
    prevPos = e.clientX;
  });
  
  // Generate initial layout
  function init() {
    let workingLeft = styles.margin;

    projects.forEach((project, i) => {
      if (i === projects.length - 1) {
        project.style.left = `-${space - styles.margin}px`;
      } else {
        i !== 0 && (workingLeft += space);
        project.style.left = `${workingLeft}px`;
      };
    });

    projectSlider.currentTransX = 0;
  };

  // Intersection observer
  function observe() {

    const callback = (entries, observer) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {

          // Find intersecting edge
          const { left } = entry.boundingClientRect;
          const isLeftEdge = left < projectContainer.clientWidth - left;

          // Test and reposition next element
          const targetIdx = projects.findIndex(project => project === entry.target);
          let nextIdx = null;
          const nextEl = () => projects[nextIdx];

          const targetLeft = parseInt(entry.target.style.left);
          const nextLeft = () => parseInt(nextEl().style.left);

          if (isLeftEdge) {
            nextIdx = targetIdx === 0 ? projects.length-1 : targetIdx - 1;
            nextLeft() > targetLeft && (nextEl().style.left = `${targetLeft - space}px`);
          } else {
            nextIdx = targetIdx === projects.length-1 ? 0 : targetIdx + 1;
            nextLeft() < targetLeft && (nextEl().style.left = `${targetLeft + space}px`);
          };
        };
      });
    };

    const observer = new IntersectionObserver(callback, {root: projectContainer});

    projects.forEach(project => observer.observe(project));
  };

  init();
  observe();
});
*, *::before, *::after{
    margin:0px;
    padding:0px;
    box-sizing: border-box;
    font-size:0px;
    user-select: none;
}
    
.project-container {
    font-size: 0px;
    width: 100%;
    height: 400px;
    background-color: rgb(15, 207, 224);
    margin:auto;
    margin-top:60px;
    white-space: nowrap;
    overflow: hidden;
}

.project-slider {
  position: relative;
}
    
.project {
    font-size:100px;
    display: block;
    position: absolute;
    top: 40px;
    height:300px;
    width:350px;
    background-color:red;
    border: black 3px solid;
    user-select: none;
}
<div class="project-container">
  <div class="project-slider">
    <div class="project">1</div>
    <div class="project">2</div>
    <div class="project">3</div>
    <div class="project">4</div>
    <div class="project">5</div>
    <div class="project">6</div>
    <div class="project">7</div>
    <div class="project">8</div>
  </div>
</div>

这里还有一个问题,就是如何为较小的屏幕调整元素的大小,以及如何调整浏览器的大小。您必须为 window 调整大小添加另一个事件侦听器,它会在某些断点处重置位置和样式,并在页面首次加载时以编程方式确定样式变量。我相信这仍然是原始实现的部分问题,因此您必须在某个时候以任何一种方式解决它。