【问题标题】:Why setting scrollLeft and scrollTop on mousemove results in max scroll* unexpectedly early?为什么在 mousemove 上设置 scrollLeft 和 scrollTop 会意外提前导致最大滚动*?
【发布时间】:2017-07-01 19:52:21
【问题描述】:

在回答another question on StackOverflow 时,我发布了一个应该是简单的演示,展示了如何通过@987654322 的操作实现子<div> 相对于其父<div> 中光标的移动的移动@ 和element.scrollLeft

我的演示基本上按预期工作,演示了通过event.pageXevent.pageY 收集mousemove 上的光标坐标的基础知识,做一些数学运算来计算应该移动较大的.inner 孩子的比率,并应用scroll*

然而,我注意到当我将光标移动到.outer 父级的底部和/或右侧时,.inner 子级将在我的光标到达之前滚动到最大值边缘。

我为找到解决方案做了什么?
意识到event.pageX 总是至少是1 并且永远不会超过1 小于width 并且event.pageY 永远不会超过1 小于父<div> 的@987654345 @,我添加了一个基本函数来将坐标范围从 0 扩展到完整的 width 和/或 height
尽管该函数完成了它的工作,但它并没有治愈过早的最大值scroll*

编辑:我没有在 SO sn-ps 之外测试过这段代码,根据它是被编辑、正常查看还是扩展,它似乎以不同的方式呈现 HTML;有时该函数是必需的,有时它的存在会导致负值。

scrollTop 不响应负值;相反,它将自己设置回0

如果设置的值大于元素可用的最大值,scrollTop 会将自身设置为最大值。

scrollLeft 也是如此。
所以这种不一致是不相关的;在我添加功能之前问题很明显。

还有什么?
我反复检查了数学,例如

  • 假设我们有一个父母 <div> 测量 100px100px
  • 还有一个孩子<div>300px 测量300px(两个轴上都是父级的3 倍)
  • 如果光标在坐标上。 { x: 90, y: 90 }
  • scrollTop 应设置为 90 * 3 = 270
  • 并且scrollLeft应该设置为90 * 3 = 270
  • 因此,子 <div> 的底部或右侧边缘应该与父级对齐。

考虑到这一点,正如我所说,我检查了又检查了一遍,数学应该工作,但结果出乎意料。

这是代码,有一些额外的位会输出innerHTML 中的一些数字(否则控制台会有点碍事),我的问题将在它下面继续。额外的<output> UI 不会影响结果。

const divs = document.querySelectorAll( "div" ),
      outer_div = divs[ 0 ],
      outer_div_styles = window.getComputedStyle( outer_div ),
      inner_div_styles = window.getComputedStyle( divs[ 1 ] ),
      outer_div_width = parseInt( outer_div_styles.width ),
      outer_div_height = parseInt( outer_div_styles.height ),
      dimention_ratio = {
        x: parseInt( inner_div_styles.width ) / outer_div_width,
        y: parseInt( inner_div_styles.height ) / outer_div_height
      },
      half_odw = outer_div_width / 2,
      half_odh = outer_div_height / 2,
      expandCoords = function( e ) { // sometimes useful, never harmful
        var X = e.pageX,
            Y = e.pageY;
        if ( X < half_odw ) {
          X -= 1;
        } else if ( X > half_odw ) {
          X += 1;
        }
        if ( Y < half_odh ) {
          Y -= 1;
        } else if ( Y > half_odh ) {
          Y += 1;
        }
        return { x: X, y: Y };
      },
      // for demo convenience
      output = document.querySelector( "output" );
outer_div.addEventListener( "mousemove", function( evt ) {
  evt = expandCoords( evt );
  // for demo convenience
  output.innerHTML = "Coords: x:" + evt.x + ", y:" + evt.y + "<br>" +
                     "scrollLeft = " + ( evt.x * dimention_ratio.x ) + "<br>" +
                     "scrollTop = " + ( evt.y * dimention_ratio.y );
  outer_div.scrollLeft = evt.x * dimention_ratio.x;
  outer_div.scrollTop = evt.y * dimention_ratio.y;
}, false );
body {
  overflow: hidden;
  margin: 0;
}
.outer {
  width: 100vw;
  height: 100vh;
  overflow: hidden;
}
.inner {
  width: 1000vw; /* 10 times the width of its parent */
  height: 500vh; /* 5 times the height of its parent */
  box-shadow: inset 0 0 20px 20px green; /* no border edge highlight */
  background: radial-gradient( white, black );
}

/* for demo convenience */
output {
  position: absolute;
  top: 10vh;
  left: 10vw;
  font-family: Consolas, monospace;
  background: white;
  padding: .2em .4em .3em;
  cursor: default;
}
&lt;div class="outer"&gt;&lt;div class="inner"&gt;&lt;/div&gt;&lt;output&gt;&lt;/output&gt;&lt;/div&gt;

如您所见,子 &lt;div&gt; 的右边缘和下边缘早在光标到达父对象的右边缘和/或下边缘之前就已经出现了。

为什么会这样,如何在动态应用程序中修复它?

“动态应用程序”我的意思是不根据具体情况对解决方案进行硬编码。

注意:虽然(我知道)这段代码可以在很多方面进行优化,但它纯粹是为了演示,因此不会影响修复的优化具体问题不感兴趣。

【问题讨论】:

    标签: javascript html math coordinates mousemove


    【解决方案1】:

    我想通了

    scrollTopscrollLeft 是衡量在它们各自的轴上滚动多少像素的量度。
    这相当于滚动到视野之外的量,因此总有剩余的量无法滚动。
    此金额等于父级的相应度量。

    • 如果子 &lt;div&gt; 的大小是父级的两倍,那么当在任一轴上滚动到最大值时,只会滚动一半。
    • 如果子项是大小的三倍,则在最大滚动时,将滚动三分之二。
    • 10 倍大小:9/10ths 滚动

    在这个特定的应用程序中,光标坐标应乘以尺寸比,计算如下:

    ( width of the child minus the width of the parent ) divided by the width of the parent

    ( height of the child minus the height of the parent ) divided by the height of the parent

    所以修正后的代码,处理任意比例:

    const divs = document.querySelectorAll( "div" ),
          outer_div = divs[ 0 ],
          outer_div_styles = window.getComputedStyle( outer_div ),
          inner_div_styles = window.getComputedStyle( divs[ 1 ] ),
          outer_div_width = parseInt( outer_div_styles.width ),
          outer_div_height = parseInt( outer_div_styles.height ),
          dimention_ratio = {
            x: ( parseInt( inner_div_styles.width ) - outer_div_width ) / outer_div_width, // fixed
            y: ( parseInt( inner_div_styles.height ) - outer_div_height ) / outer_div_height // fixed
          },
          half_odw = outer_div_width / 2,
          half_odh = outer_div_height / 2,
          expandCoords = function( e ) {
            var X = e.pageX,
                Y = e.pageY;
            if ( X < half_odw ) {
              X -= 1;
            } else if ( X > half_odw ) {
              X += 1;
            }
            if ( Y < half_odh ) {
              Y -= 1;
            } else if ( Y > half_odh ) {
              Y += 1;
            }
            return { x: X, y: Y };
          };
    outer_div.addEventListener( "mousemove", function( evt ) {
      evt = expandCoords( evt );
      outer_div.scrollLeft = evt.x * dimention_ratio.x;
      outer_div.scrollTop = evt.y * dimention_ratio.y;
    }, false );
    body {
      overflow: hidden;
      margin: 0;
    }
    .outer {
      width: 100vw;
      height: 100vh;
      overflow: hidden;
    }
    .inner {
      width: 1234vw; /* 12.34 times the width of its parent */
      height: 567vh; /* 5.67 times the height of its parent */
      box-shadow: inset 0 0 20px 20px green; /* no border edge highlight */
      background: radial-gradient( white, black );
    }
    &lt;div class="outer"&gt;&lt;div class="inner"&gt;&lt;/div&gt;&lt;/div&gt;

    【讨论】:

      猜你喜欢
      • 2011-05-17
      • 2012-12-17
      • 1970-01-01
      • 2013-04-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多