为什么在 mousemove 上设置 scrollLeft 和 scrollTop 会导致 max scroll* 意外提前?
Why setting scrollLeft and scrollTop on mousemove results in max scroll* unexpectedly early?
在回答another question on Whosebug, I posted what was supposed to be a simple demo showing how to achieve movement of a child <div>
relative to the movement of the cursor within its parent <div>
, by the manipulation of element.scrollTop
and element.scrollLeft
.
我的演示基本上按预期工作,演示了在 mousemove
via event.pageX
and event.pageY
上收集光标坐标的基础知识,做一些数学运算来计算较大的 .inner
child 应该的比率移动,并应用 scroll*
.
但是,我注意到当我将光标移动到 .outer
parent 右下方 and/or 附近时,.inner
child 会在我的光标到达边缘之前滚动到最大值。
我做了什么找到解决方案?
意识到 event.pageX
总是至少 1
并且绝不会比 width
少 1
并且 event.pageY
永远不会超过 1
少了parent<div>
的height
,我加了一个基本的函数,把坐标范围从0
扩展到全width
and/or height
.
尽管该功能发挥了作用,但它并没有治愈过早的最大值 scroll*
.
EDIT:我没有在 SO 片段之外测试这段代码,这些片段似乎以不同方式呈现 HTML,具体取决于它是被编辑、正常查看还是展开;有时该函数是 必需的 ,有时它的存在会导致负值。
scrollTop
doesn't respond to negative values; instead, it sets itself back to 0
.
If set to a value greater than the maximum available for the element, scrollTop
settles itself to the maximum value.
scrollLeft
也是如此。
所以这种不一致是无关紧要的;在我添加函数之前问题就很明显了。
还有什么?
我反复检查了数学,例如
- 假设我们有一个 parent
<div>
通过 100px
测量 100px
- 和 child
<div>
通过 300px
测量 300px
(两个轴上 parent 的 3 倍)
- 如果光标在坐标上。
{ x: 90, y: 90 }
scrollTop
应该设置为90 * 3
=270
- 而
scrollLeft
应该设置为90 * 3
= 270
- 所以 child
<div>
的底部或右边缘应该 而不是 与 parent.[=122] 对齐=]
考虑到这一点,正如我所说,我已经检查并再次检查,数学应该有效,但结果出乎意料。
这是代码,一些额外的位输出 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;
}
<div class="outer"><div class="inner"></div><output></output></div>
如您所见,child <div>
的右侧和底部边缘在光标到达右侧 and/or 底部边缘之前很久就进入视野parent.
这是为什么,如何在动态应用程序中修复?
“动态应用程序” 我的意思是不对解决方案进行逐案硬编码。
注意:虽然(我知道)这段代码可以在很多方面优化,但它纯粹是为了演示,因此,优化不影响对特定问题的修复不感兴趣。
我想通了
scrollTop
和 scrollLeft
是在各自的轴上滚动了多少像素的量度。
这相当于滚动了多少超出视图,所以总有剩余量无法滚动。
此金额等于 parent.
的相应度量
- 如果 child
<div>
的大小是 parent 的两倍,那么当在任一轴上滚动到最大值时,只会滚动一半。
- 如果 child 是大小的三倍,在最大滚动时,将滚动三分之二。
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 );
}
<div class="outer"><div class="inner"></div></div>
在回答another question on Whosebug, I posted what was supposed to be a simple demo showing how to achieve movement of a child <div>
relative to the movement of the cursor within its parent <div>
, by the manipulation of element.scrollTop
and element.scrollLeft
.
我的演示基本上按预期工作,演示了在 mousemove
via event.pageX
and event.pageY
上收集光标坐标的基础知识,做一些数学运算来计算较大的 .inner
child 应该的比率移动,并应用 scroll*
.
但是,我注意到当我将光标移动到 .outer
parent 右下方 and/or 附近时,.inner
child 会在我的光标到达边缘之前滚动到最大值。
我做了什么找到解决方案?
意识到 event.pageX
总是至少 1
并且绝不会比 width
少 1
并且 event.pageY
永远不会超过 1
少了parent<div>
的height
,我加了一个基本的函数,把坐标范围从0
扩展到全width
and/or height
.
尽管该功能发挥了作用,但它并没有治愈过早的最大值 scroll*
.
EDIT:我没有在 SO 片段之外测试这段代码,这些片段似乎以不同方式呈现 HTML,具体取决于它是被编辑、正常查看还是展开;有时该函数是 必需的 ,有时它的存在会导致负值。
scrollTop
doesn't respond to negative values; instead, it sets itself back to0
.If set to a value greater than the maximum available for the element,
scrollTop
settles itself to the maximum value.
scrollLeft
也是如此。
所以这种不一致是无关紧要的;在我添加函数之前问题就很明显了。
还有什么?
我反复检查了数学,例如
- 假设我们有一个 parent
<div>
通过100px
测量 - 和 child
<div>
通过300px
测量300px
(两个轴上 parent 的 3 倍) - 如果光标在坐标上。
{ x: 90, y: 90 }
scrollTop
应该设置为90 * 3
=270
- 而
scrollLeft
应该设置为90 * 3
=270
- 所以 child
<div>
的底部或右边缘应该 而不是 与 parent.[=122] 对齐=]
100px
考虑到这一点,正如我所说,我已经检查并再次检查,数学应该有效,但结果出乎意料。
这是代码,一些额外的位输出 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;
}
<div class="outer"><div class="inner"></div><output></output></div>
如您所见,child <div>
的右侧和底部边缘在光标到达右侧 and/or 底部边缘之前很久就进入视野parent.
这是为什么,如何在动态应用程序中修复?
“动态应用程序” 我的意思是不对解决方案进行逐案硬编码。
注意:虽然(我知道)这段代码可以在很多方面优化,但它纯粹是为了演示,因此,优化不影响对特定问题的修复不感兴趣。
我想通了
scrollTop
和 scrollLeft
是在各自的轴上滚动了多少像素的量度。
这相当于滚动了多少超出视图,所以总有剩余量无法滚动。
此金额等于 parent.
- 如果 child
<div>
的大小是 parent 的两倍,那么当在任一轴上滚动到最大值时,只会滚动一半。 - 如果 child 是大小的三倍,在最大滚动时,将滚动三分之二。
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 );
}
<div class="outer"><div class="inner"></div></div>