绘制到“位置:固定”叠加层,与滚动的内联内容对齐

Drawing into a "position: fixed" overlay, aligned with scrolling inline content

提问人:Don McCurdy 提问时间:10/23/2023 最后编辑:Don McCurdy 更新时间:10/26/2023 访问量:47

问:

我有一个很长的网页,其中包含一些需要 2D 或 3D HTMLCanvasElement 才能显示的内联元素。出于此处不在范围内的性能原因,将 HTMLCanvasElement 放入每个 中是不切实际的,而是我使用单个 HTMLCanvasElement 作为覆盖层。<figure/><figure/>position: fixed

在每一帧(使用)上,我用来查找每个图形相对于窗口的位置和大小,然后绘制到画布叠加层中与其后面重叠的区域。为简单起见,每个数字都只是一个圆圈。requestAnimationFramefigure.getBoundingClientRect()<figure/>

当页面是静态的时,这工作正常,但是我在滚动时在不同的浏览器中看到的结果非常不一致。在Firefox中,绘制到画布上的内容与其后面的内联页面完美对齐。在 Chrome 和(有时?Safari 中,绘制到画布上的内容略微滞后于内联页面 - 请参阅演示中,其中圆圈在滚动时不会停留在边框内。

目标:

  1. 一 (1) 个 HTMLCanvasElement
  2. 滚动时,叠加内容相对于内嵌图形边框是稳定的
  3. 在滚动较大的图形时,叠加内容相对于窗口是稳定的(即看不到任何移动)position: sticky

问题:为什么这在 Chrome 或 Safari 中不起作用,可以使用不同的 CSS 或 JavaScript 实现来修复吗?

滚动过程中的屏幕截图:

misaligned figure

const figures = Array.from(document.querySelectorAll('figure'));
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');

animate();

function animate() {
  requestAnimationFrame(animate);

  if (canvas.width !== innerWidth || canvas.height !== innerHeight) {
    canvas.width = innerWidth;
    canvas.height = innerHeight;
  }

  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.fillStyle = "crimson";

  for (const figure of figures) {
    drawFigure(figure);
  }
}

function drawFigure(figure) {
  const {
    left,
    top,
    width,
    height
  } = figure.getBoundingClientRect();

  if (top > innerHeight) return;

  const size = Math.min(width, height) / 2 - 5;
  const x = left + width / 2;
  const y = top + height / 2;

  ctx.beginPath();
  ctx.arc(x, y, size, 0, 2 * Math.PI);
  ctx.fill();
}
html,
body {
  margin: 0;
  padding: 0;
  background: #eee;
  font-family: Courier;
  line-height: 1.4em;
}

main {
  margin: 2em auto;
  max-width: 1024px;
  background: #fff;
  border-radius: 8px;
  padding: 2em;
}

p {
  text-indent: 2.5em;
}

figure {
  background: mintcream;
  border: 5px dashed aquamarine;
  margin: 2em;
  box-sizing: border-box;
}

figure.inline {
  height: 200px;
  width: 200px;
  margin: 0.5em 2em;
}

figure.inline:nth-of-type(odd) {
  float: right;
  margin-right: 0;
}

figure.inline:nth-of-type(even) {
  float: left;
  margin-left: 0;
}

.full {
  height: 500vh;
  width: 100%;
  margin: 0;
  position: relative;
}

.full>figure.sticky {
  position: sticky;
  top: 0;
  left: 0;
  box-sizing: border-box;
  width: 100%;
  height: 100vh;
  border: 5px dashed crimson;
  margin: 0;
}

footer {
  font-style: italic;
}

canvas {
  position: fixed;
  top: 0;
  left: 0;
  pointer-events: none;
  opacity: 0.5;
}
<main>
  <h1>Scrolling Inline Content + Fixed Overlay</h1>
  <p>
    Tart sweet roll jujubes wafer halvah chocolate bar muffin. Pie sweet roll brownie chocolate liquorice oat cake gingerbread. Powder topping shortbread jujubes oat cake shortbread jelly. Macaroon chupa chups sweet roll danish powder carrot cake tart bonbon.
    Sweet gummies chocolate bar sugar plum chocolate lollipop sweet. Caramels marshmallow sugar plum apple pie tart soufflé biscuit halvah tart. Topping danish toffee chocolate pastry. Shortbread donut oat cake chocolate bar biscuit marshmallow macaroon
    wafer. Danish chocolate bar cupcake marzipan jelly beans wafer. Bear claw jelly toffee topping cotton candy pudding oat cake bonbon. Caramels powder brownie donut carrot cake cake ice cream. Jelly chocolate bar dragée gingerbread cotton candy fruitcake
    shortbread. Soufflé cake danish fruitcake fruitcake cake marzipan.
  </p>
  <p>
    Cheesecake sweet sweet cookie soufflé jelly beans. Caramels chupa chups chocolate cake shortbread cupcake donut muffin macaroon croissant. Brownie sweet chupa chups sweet roll toffee. Tiramisu marzipan liquorice candy powder pudding toffee wafer sesame
    snaps. Powder lollipop cake toffee chupa chups. Halvah dragée shortbread cookie ice cream. Lemon drops bear claw muffin powder gummies tootsie roll powder gummi bears. Toffee sweet cake gingerbread tootsie roll marzipan lollipop jelly-o pudding. Cake
    cake sugar plum pie tiramisu chocolate cake jelly beans candy canes oat cake. Cupcake chocolate wafer powder powder. Tiramisu topping sweet ice cream sugar plum candy canes danish pudding. Fruitcake candy canes chocolate cake oat cake croissant cookie.
    Cake pie lemon drops ice cream apple pie cookie marzipan tiramisu caramels.
  </p>
  <figure class="inline"></figure>
  <p>
    Fruitcake topping cookie soufflé toffee candy bonbon. Powder sweet gummi bears ice cream pudding halvah tootsie roll tart dragée. Danish tootsie roll jelly beans bear claw chocolate cake cheesecake gingerbread tiramisu oat cake. Chocolate bar candy canes
    shortbread donut jujubes bear claw. Tootsie roll toffee soufflé liquorice marshmallow. Powder cotton candy marzipan soufflé macaroon dragée lollipop chocolate bar gingerbread. Oat cake chocolate cake jelly beans cotton candy pudding. Tiramisu candy
    canes jelly-o croissant brownie. Chocolate bar icing fruitcake sesame snaps powder.
  </p>
  <p>
    Halvah cookie cheesecake donut lollipop pudding candy tart. Gummies biscuit dragée cotton candy chocolate cake toffee lemon drops. Chocolate bar oat cake gummi bears tart toffee gingerbread sugar plum donut jelly-o. Apple pie pastry sugar plum jujubes
    lollipop candy sesame snaps lemon drops caramels. Carrot cake pie oat cake powder dragée chocolate cake sweet roll marzipan. Lollipop lollipop tiramisu bonbon pudding tootsie roll. Dessert caramels sweet sugar plum cheesecake cake. Dragée cookie cake
    pastry candy.
  </p>
  <figure class="inline"></figure>
  <p>
    Oat cake chocolate bar donut cotton candy soufflé halvah. Biscuit jujubes ice cream brownie cheesecake brownie dragée lollipop dragée. Dragée sweet powder shortbread croissant croissant sweet roll cheesecake. Wafer marshmallow cheesecake jujubes tootsie
    roll bonbon croissant carrot cake. Chocolate shortbread shortbread cheesecake pie chocolate sweet. Sweet roll chupa chups candy muffin macaroon topping cake. Candy icing donut shortbread macaroon fruitcake tart muffin. Caramels candy canes sweet pie
    cheesecake. Topping powder soufflé chocolate cake liquorice powder danish.
  </p>
  <p>
    Macaroon dessert gummies cake tiramisu candy chocolate. Cookie powder donut jelly beans ice cream dragée pie. Marshmallow fruitcake macaroon icing jujubes croissant. Candy canes topping dessert candy canes apple pie pie cake. Tiramisu caramels tiramisu
    brownie croissant. Chocolate cake marshmallow apple pie candy danish shortbread jujubes chupa chups caramels. Wafer chupa chups candy canes fruitcake biscuit fruitcake gingerbread pudding.
  </p>
  <p>
    Jelly beans danish danish chupa chups cookie cheesecake powder. Danish lemon drops gingerbread chupa chups cheesecake macaroon danish tart sweet roll. Biscuit jelly beans gummi bears sesame snaps liquorice halvah wafer. Gingerbread halvah cotton candy
    apple pie carrot cake sweet roll gingerbread pastry. Jelly beans sesame snaps tootsie roll cupcake chocolate bar chocolate cake sesame snaps. Carrot cake powder cake ice cream ice cream chocolate cake. Carrot cake gummies biscuit powder cake shortbread.
    Tiramisu liquorice bonbon jelly-o danish tootsie roll lollipop brownie brownie. Carrot cake jelly-o danish bear claw tootsie roll sesame snaps caramels pie gummies.
  </p>
  <div class="full">
    <figure class="sticky"></figure>
  </div>
  <p>
    Chocolate bar shortbread jelly-o cake pastry cake chocolate cake. Pudding pastry caramels sweet bear claw marshmallow bear claw. Sesame snaps gummies topping brownie macaroon pastry. Dragée macaroon danish apple pie chocolate bar shortbread muffin. Dessert
    jelly apple pie biscuit dessert. Tart chocolate sugar plum cheesecake topping. Pastry toffee donut candy canes marshmallow cake lemon drops. Cotton candy cake tart carrot cake topping tart cake gummi bears cake. Brownie sweet croissant jujubes sweet
    roll jelly beans jelly fruitcake cupcake. Tart danish danish jujubes pastry tiramisu tart dragée dessert.
  </p>
  <figure class="inline"></figure>

  <p>
    Croissant powder danish muffin sugar plum gingerbread cake chocolate cake. Chocolate cake biscuit wafer tootsie roll marshmallow sesame snaps soufflé pudding. Bonbon pie oat cake shortbread cheesecake ice cream. Chupa chups oat cake caramels cupcake chocolate.
    Carrot cake candy canes wafer soufflé chocolate bar cake jelly beans. Caramels lollipop jelly beans marzipan gingerbread brownie marshmallow. Candy canes ice cream gummi bears candy ice cream chocolate bar ice cream. Sugar plum danish fruitcake biscuit
    cheesecake chocolate bar.
  </p>
  <p>
    Carrot cake powder brownie gummi bears marzipan carrot cake. Tootsie roll cake gummies dragée chupa chups oat cake tart. Dragée lemon drops cake icing jelly beans cookie chocolate cake. Biscuit sweet roll sesame snaps tart pudding jelly ice cream. Fruitcake
    danish tootsie roll marshmallow fruitcake halvah. Cake lemon drops carrot cake chocolate cake bonbon sweet carrot cake caramels.
  </p>
  <p>
    Biscuit pudding macaroon cookie muffin jelly. Candy canes lollipop dragée sweet carrot cake candy cotton candy. Sweet roll dragée dragée cheesecake sweet gingerbread oat cake danish. Shortbread jelly-o cake bear claw gummi bears sesame snaps. Shortbread
    pastry liquorice tiramisu bonbon. Lollipop gingerbread gummi bears sweet liquorice gummies shortbread biscuit sweet. Fruitcake donut chocolate cake wafer danish. Chocolate fruitcake powder bonbon tiramisu lollipop.
  </p>
  <p>
    Chocolate cake chocolate candy liquorice liquorice jelly beans carrot cake candy canes cheesecake. Lollipop chocolate bar gummi bears candy canes chocolate cake oat cake halvah bonbon. Cupcake biscuit biscuit halvah brownie candy oat cake lollipop. Gummi
    bears sweet pastry marshmallow jujubes donut lollipop candy dragée. Oat cake carrot cake donut halvah dragée biscuit brownie gummies. Liquorice chupa chups sweet candy canes chocolate cake. Croissant cupcake bonbon gingerbread sweet lemon drops tootsie
    roll. Macaroon cake chupa chups sweet jelly beans sesame snaps bonbon cotton candy ice cream. Dessert marzipan lemon drops muffin chocolate liquorice. Chupa chups biscuit bear claw bear claw liquorice sugar plum fruitcake. Gummi bears donut chocolate
    shortbread chocolate bar halvah gingerbread. Cotton candy cheesecake croissant donut jelly jelly. Chocolate sweet roll fruitcake ice cream muffin chupa chups croissant. Sugar plum jujubes cupcake cotton candy marshmallow sesame snaps dragée candy.
  </p>
  <hr>
  <footer>
    Filler text by <a href="https://cupcakeipsum.com/">Cupcake Ipsum</a>.
  </footer>
</main>

<canvas></canvas>

JSFiddle:https://jsfiddle.net/donmccurdy/9sg1fr3p/

javascript css 滚动 css-position

评论

1赞 Rory McCrossan 10/23/2023
您问题中的演示在 Chrome 和 Firefox 中对我来说都很好 - 尽管动画结束得如此之快,我无法同时测试滚动和动画
0赞 Don McCurdy 10/24/2023
感谢您的回复。没有这样的动画,页面应该看起来好像它只包含内联图像。但是在滚动时,圆圈会以图像不会的方式移动到边界之外。这可能是特定于平台的——我正在 macOS 14 上进行测试,并在 Chrome 和 Safari 中看到了这个问题。
0赞 Rory McCrossan 10/24/2023
明白了 - 这可能是一个与 Mac 相关的问题,因为我在 Windows 上进行测试。
0赞 Eliezer Berlin 10/25/2023
我几乎无法在我的 Windows 计算机上看到它,但我认为这有点故意。如果它做了你想要的,它需要每秒重新计算位置大约4-5次,这对滚动时的性能来说是很糟糕的。如果您停止使用,它可能会更频繁地刷新,但代价是性能会下降。requestAnimationFrame
0赞 Eliezer Berlin 10/25/2023
我想你可以试着弄乱.也许会给你更好的结果,或者其他什么。scroll-behaviorscroll-behavior: smooth;

答:

0赞 Don McCurdy 10/26/2023 #1

注意:我正在发布我自己的问题的解决方案,但会暂时保持开放和“未回答”,希望其他人能想出更好的解决方案,或者解释为什么会发生这种情况!谢谢。

我已经解决了这个问题,将其分为两种情况并使用 JavaScript 在它们之间切换。

  1. 没有人物占据全屏高度。使用 ,更新每一帧,使画布相对于文档正文及其内联内容保持固定。现在,在滚动过程中,视口会稍微远离画布,因此我们将画布扩展 100px 作为缓冲区。position: absolutetop: XXpx
  2. 至少有一个图形占据整个屏幕高度。用于防止图形在滚动过程中相对于视口移动。position: sticky; top: 0;

就我而言,这两种情况是相互排斥的。当我们在两者之间过渡时,画布上仍然有一个小的转变,但它比原来的问题要明显得多。

对画布的更新工作如下:

if (figures.some(isFullWindow)) {
    canvas.style.position = 'fixed';
    canvas.style.top = (-PAD) + 'px';
    canvas.style.left = (-PAD) + 'px';
} else {
    canvas.style.position = 'absolute';
    canvas.style.top = (window.scrollY - PAD) + 'px';
    canvas.style.left = (-PAD) + 'px';
}

function isFullWindow(figure) {
  const {top, bottom} = figure.getBoundingClientRect();
  const _isFullView = top <= 0 && bottom >= innerHeight;
  return _isFullView;
}

画布周围需要一个额外的容器来支持模式。position: sticky