【问题标题】:Why is the distance between first and last element decreasing?为什么第一个元素和最后一个元素之间的距离会减小?
【发布时间】:2020-12-05 15:33:43
【问题描述】:

我正在尝试创建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>

【问题讨论】:

    标签: javascript html css transform translate


    【解决方案1】:

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

    我猜这主要的原因是你循环遍历所有元素并单独分隔它们。鼠标移动事件通常在 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>

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

    【讨论】:

    • 伙计,我还没有完全阅读代码和你的答案,但是这件事确实让我很开心。看完全文我会回来的。非常感谢。对我来说意义重大
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-02-27
    • 2014-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-25
    • 1970-01-01
    相关资源
    最近更新 更多