为什么在mousemove上设置scrollLeft和scrollTop导致意外地提前最大滚动?

时间:2017-07-01 19:52:21

标签: javascript html math coordinates mousemove

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

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

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

我做了什么来寻找解决方案?
认识到.inner始终至少event.pageX且永远不会超过1 1width永远不会超过event.pageY }} {}}} {}} {} {}} 。
尽管该功能起到了作用,但它并没有治愈过早的最大值1

编辑:我没有在SO片段之外测试此代码,这些代码似乎以不同的方式显示HTML,具体取决于是否正在编辑,正常查看或扩展;有时函数是必需,有时它的存在会产生负值。

  

<div>没有回应负值;相反,它将自己设置回height

     

如果设置为大于元素可用最大值的值,0将自己设置为最大值。

width也是如此 所以这种不一致是不相关的;在添加函数之前,问题很明显。

还有什么?
我一再检查数学,例如

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

考虑到这一点,正如我所说,我已经检查并再次检查,数学应该有效,但结果是出乎意料的。

这是代码,有一些额外的位输出270中的一些数字(否则控制台会有所不同),我的问题将继续下去。额外的scrollLeft用户界面不会影响结果。

90 * 3
270
<div>

正如您所看到的,在光标到达父级的右边和/或底边之前很久,孩子innerHTML的右边和右边就会进入视图。

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

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

注意:虽然(我知道)此代码可以通过多种方式优化,但它纯粹用于演示,因此,优化不会影响修复具体问题没有意义。

1 个答案:

答案 0 :(得分:0)

我想通了

scrollTopscrollLeft衡量在各自轴上滚动的像素数量。
这相当于滚动超出视野的数量,因此总会有剩余的数量无法滚动。
此金额等于父母的相应度量。

  • 如果子<div>的大小是父级的两倍,那么当在任一轴上滚动到最大值时,只会滚动一半。
  • 如果孩子的大小是三倍,则在最大滚动时,将滚动三分之二。
  • 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

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

&#13;
&#13;
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 );
&#13;
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 );
}
&#13;
<div class="outer"><div class="inner"></div></div>
&#13;
&#13;
&#13;