在偏移内联元素时定位粘滞

Position Sticky While Offsetting Inline Elements

我想做的是在对内联元素使用粘性位置的同时逐渐偏移内联元素,以便在用户滚动时将元素锁定到位,形成连贯的结构。例如,我有一个parent元素,div,它包含一个pchild,它又包含多个spanchildren。

<div>
  <p>
    <span>w</span>
    <span>o</span>
    <span>r</span>
    <span>d</span>
  </p>
</div>

我所做的是使用 translateY 逐渐偏移每个字母(因为 sticky 显然使用 top 等来确定症结点),因此第一个字母是例如,偏移 100%,第二个字母偏移 200%,依此类推。这个想法是,当用户滚动时,每个字母都会锁定到位,最终在用户完成滚动时形成一个单词。

我知道问题是一旦我启用 stickyspan 元素就会被定位到 relative 到 parent,这意味着元素是始终相对于 parent 进行偏移。我只是想不出一个纯粹的 CSS 解决方案。

我在写这篇文章时突然想到:也许诀窍是用top来抵消,而bottom来坚持?

Here是下面嵌入代码的codepen。

html,
body { width: 100%; height: 100%; margin: 0; }

div  { border: solid red 2px; box-sizing: border-box; }

p    { font-family: arial; font-size: 104px; border: solid blue 2px; box-sizing: border-box;
  overflow-y: scroll;
  margin: 0;
}

span { border: solid black 2px; box-sizing: border-box;
  /* position: sticky; */
  top: 0;
  display: inline-block;
}

span:nth-child(1) { transform: translateY(100%); }
span:nth-child(2) { transform: translateY(200%); }
span:nth-child(3) { transform: translateY(300%); }
span:nth-child(4) { transform: translateY(400%); }
<div>
  <p>
    <span>w</span>
    <span>o</span>
    <span>r</span>
    <span>d</span>
  </p>
</div>

您可以在每个字母上使用负百分比来抵消转换:

html,
body {
  width: 100%;
  height: 100%;
  margin: 0;
}

div {
  border: solid red 2px;
  box-sizing: border-box;
}

p {
  font-family: arial;
  font-size: 104px;
  border: solid blue 2px;
  box-sizing: border-box;
  overflow-y: scroll;
  margin: 0;
}

span {
  border: solid black 2px;
  box-sizing: border-box;
  position: sticky;
  display: inline-block;
}

span:nth-child(1) {
  transform: translateY(100%);
  top: -100%;
}

span:nth-child(2) {
  transform: translateY(200%);
  top: -200%;
}

span:nth-child(3) {
  transform: translateY(300%);
  top: -300%;
}

span:nth-child(4) {
  transform: translateY(400%);
  top: -400%;
}
<div>
  <p>
    <span>w</span>
    <span>o</span>
    <span>r</span>
    <span>d</span>
  </p>
</div>

这是另一个更适合等宽字体和通用的解决方案(您不需要使用 nth-child 设置样式)

div {
  border: solid red 2px;
  box-sizing: border-box;
}

p {
  font-family: monospace;
  font-size:90px;
  border: solid blue 2px;
  box-sizing: border-box;
  overflow-y: scroll;
  margin: 0;
  line-height: 1.2em;
  height: 1.2em; /* equal to line-height */
}

span {
  position: sticky;
  top: 0;
  display: block;
}

span:first-child {
  margin-top: 1.2em; /* equal to line-height */
}

span:not(:first-child)::before {
  content: "";
  float: left;
  width: 1ch;
  height: 1.3em; /* a bit bigger than the line-height */
}
<div>
  <p>
    <span>H</span>
    <span>e</span>
    <span>l</span>
    <span>l</span>
    <span>o</span>
    <span> </span>
    <span>w</span>
    <span>o</span>
    <span>r</span>
    <span>d</span>
  </p>
</div>