CSS位置粘贴奇怪的行为时,规模应用于父,粘性滚动和不同的速度元素



我不敢相信以前没有人注意到这一点,但我似乎找不到任何人注意到这种行为。我应该首先说,这个html是很久以前写的(不是我写的(,现在真的不能修改。

所以问题来了:

我们有像这样的html结构布局

.rptDisplay {
overflow: auto;
position: relative;
top: 0;
left: 0;
z-index: 0;
margin: 0;
padding: 0;
width: 100%;
height: 200px;
max-height: 100vh;
font-size: 11px;
text-align: left;
}
.rptPositioner {
width: 33%;
display: block;
transform-origin: 0px 0px;
transform: scale(3, 3);
}
.rptHeader {
position: sticky !important;
top: 0 !important;
z-index: 1;
background: #eee;
}
body {
margin: 0;
padding: 0;
}
<div class="rptDisplay">
<div class="rptPositioner">
<div class="rptHeader">Header</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
</div>
</div>

问题是有一些非常奇怪的行为,当我滚动标题时,页面向下滚动的速度与页面本身向下滚动的速率不同。考虑到变换比例可以由用户更改以进行缩放,最近添加了粘性位置。

我将问题归结为缩放没有应用于rptDisplay,而是应用于display的子项,但粘性元素的父项。如果我将缩放应用于显示器或标题,问题就会消失,但目前这不是一个选项。

我附上了一个CodePen,演示了我们所看到的问题。

https://codepen.io/steves165/pen/QWOLMwr

一种解决方案可以是根据当前变换比例值自行设置top

你提到过,"变换比例是用户可更改的以进行缩放"这意味着javascript正在被使用。因此,我们可以添加以下小代码来自己设置top

let scale = zoom.value;
let disp = document.querySelector('.rptDisplay');
let psnr = document.querySelector('.rptPositioner');
let header = document.querySelector('.rptHeader');
disp.addEventListener('scroll', setTop);
function setTop() {
let offset = disp.scrollTop - (disp.scrollTop / scale);
header.style.top = `${-offset}px`;
}
// for the demo set custom zoom
function adjust(r) {
scale = r.value;
psnr.style.transform = `scale(${scale},${scale})`;
setTop()
}
.rptDisplay {
overflow: auto;
position: relative;
top: 0;
left: 0;
z-index: 0;
margin: 0;
padding: 0;
width: 100%;
height: 155px;
max-height: 100vh;
font-size: 11px;
text-align: left;
}
.rptPositioner {
width: 33%;
display: block;
transform-origin: 0px 0px;
transform: scale(3, 3);
}
.rptHeader {
position: sticky !important;
/* top: 0 !important;*/
z-index: 1;
background: rgb(215, 253, 199);
}
body {
margin: 0;
padding: 0;
}
<label>Set Zoom(1-4):
<input type="range" id="zoom" min="1" max="4" value="3" onchange="adjust(this)">
</label><br>
<hr>
<div class="rptDisplay">
<div class="rptPositioner">
<div class="rptHeader">Header</div>
<div class="row">Row first</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row Last</div>
</div>
</div>

我将添加一个解决此问题的示例。此示例使用以下自定义CSS变量(您可以自己设置(:

  • --scale-设置列表的缩放级别(而不是transform: scale()(。通过更改这个CSS变量(直接、在CSS规则中或通过JS(,规模也会发生变化
  • --width--height-值,我们使用JS为列表中的每个元素设置这些值,如果项目随后更改其大小,则使用ResizeObserver的变量将被更新

示例的逻辑是基于这样一个事实,即我们不增加整个列表,而是单独增加每个元素。为此,margin-bottom等于"0";(比例-1(*height";添加到每个元素中,从而出现一个空白,该空白将用transform: scale()填充。与CCD_ 9和CCD_。

以下示例:

// tracking changes in the size of elements
const resizeObserver = new ResizeObserver((entries) => {
entries.forEach(({ target }) => {
target.style.setProperty("--height", target.offsetHeight);
target.style.setProperty("--width", target.offsetWidth);
});
});
// initialization of basic parameters and observers
document.querySelectorAll(".rptPositioner > div").forEach((el) => {
el.style.setProperty("--height", el.offsetHeight);
el.style.setProperty("--width", el.offsetWidth);
resizeObserver.observe(el);
});
// control of the zoom control
document.querySelector("#scale-control").onchange = function () {
document
.querySelector(".rptDisplay")
.style.setProperty("--scale", this.value);
};
.rptDisplay {
overflow: auto;
position: relative;
top: 0;
left: 0;
z-index: 0;
margin: 0;
padding: 0;
width: 100%;
height: 170px;
max-height: 100vh;
font-size: 11px;
text-align: left;
}
.rptPositioner {
width: calc(100% / var(--scale, 1));
}
.rptPositioner>div {
transform-origin: 0px 0px;
transform: scale(var(--scale, 1));
margin-bottom: calc(1px * var(--height) * (var(--scale, 1) - 1));
margin-right: calc(1px * var(--width) * (var(--scale, 1) - 1));
min-width: 100%;
}
.rptHeader {
position: sticky !important;
top: 0px !important;
z-index: 999;
background: #eee;
}
body {
margin: 0;
padding: 0;
}
<label>
Scale: 
<input type="number" id="scale-control" min="0.5" step="0.5" value="3">
</label>
<div class="rptDisplay" style="--scale: 3;">
<div class="rptPositioner">
<div class="rptHeader">Header1 Header1 Header1 Header1 Header1 Header1</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="rptHeader">Header3 Header3 Header3 Header3 Header3 Header3</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
</div>
</div>

为了清晰起见,如果在X轴和Y轴上需要不同的比例,则可以简化示例,然后可以创建两个单独的变量,例如--scale-x--scale-y,并分别使用它们。

需要注意的是,列表元素本身没有缩放(在本例中为.rptPositioner(,但是首先,--scale变量可用于此元素,因此您可以直接缩放CSS属性(例如:border-width: calc((var(--scale) - 1) * 1px),其次,也可以通过ResizeObserver监控列表元素以获得此参数(宽度、高度等(。

.rptDisplay {
overflow: auto;
position: relative;
top: 0;
left: 0;
z-index: 0;
margin: 0;
padding: 0;
width: 100%;
height: 200px;
max-height: 100vh;
font-size: 11px;
text-align: left;
}
.rptPositioner {
width: 33%;
display: block;
transform-origin: 0px 0px;
transform: scale(3, 3);
}
.rptHeader {
position: sticky !important;
top: 0 !important;
z-index: 1;
background: #eee;
width: 33%;
transform-origin: 0px 0px;
transform: scale(3, 3);/*same style or zoom*/
}
body {
margin: 0;
padding: 0;
}
<div class="rptDisplay">
<div class="rptHeader">Header</div><!-- took out of positioner -->
<div class="rptPositioner">
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
</div>
</div>

想法

  • 简单rptPositioner中取出rptHeader,并将其作为兄弟而非子
  • 由于我们现在已经取出,我们对rptHeader应用了相同的缩放

p.S

看起来非常复杂的问题可能有这样的解决方案简单html

我会创建一个新的

transform-origin可能会将滚动速率提高到x3
所以请尝试缩放,使用缩放

它是非标准的,最初仅在InternetExplorer中实现。尽管其他一些浏览器现在支持缩放,但不建议将其用于生产网站。

* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.rptDisplay {
padding-top: 50px;/* <--- to show the stick effect*/
position: relative;
zoom: 3; /*but this has less browser suppoert*/
}
.rptHeader {
position: sticky;
top: 0;
background: #eee;
}
<div class="rptDisplay">
<div class="rptHeader">Header</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
</div>

如果您感兴趣的是将scale与粘性标头一起应用,则可以像下面的一样将其直接应用于rptDisplay

.rptDisplay {
height: 120px;
width: fit-content;
margin: 10px;
padding-right: 50px;
overflow: auto;
position: relative;
transform-origin: 0 0;
transform: scale(1.5);
}
.rptHeader {
position: sticky;
top: 0;
background: white;
}
<div class="rptDisplay">
<div class="rptPositioner">
<div class="rptHeader">header 1</div>
<div class="row">Row First - Header 1</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row Last - Header 1</div>
<div class="rptHeader">header 2</div>
<div class="row">Row First - Header 2</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row</div>
<div class="row">Row Last - Header 2</div>
</div>
</div>

最新更新