Возврат в липкий элемент CSS с JavaScript? - PullRequest
2 голосов
/ 14 февраля 2020

У меня есть страница, где все разделы заполняют весь экран и располагаются с CSS position: sticky; для достижения эффекта слоистого . См. Здесь:

https://codesandbox.io/s/ecstatic-khayyam-cgql1?fontsize=14&hidenavigation=1&theme=dark

Все это прекрасно работает и, как и ожидалось.

Проблема, однако, заключается в перемещении между этими областями с помощью JavaScript. Если вы попробуете использовать меню, вы увидите, что ссылки будут работать в разделах, к которым мы еще не полностью добрались (и, следовательно, не «залипали»), но не позволяют вам «go создать резервную копию» страницы.

Я полагаю, что это проблема с el.getBoundingClientRect(), когда элемент стал "липким", его верхнее значение становится практически всегда нулевым.

Здесь я использую небольшую библиотеку под названием Jump.js прыгать вокруг, но даже в ванили JS эта проблема все равно будет той же, поскольку проблема является результатом вычисления, когда элемент становится липким.

Есть ли способ найти исходное положение для каждого раздела, прежде чем он стал липким? По крайней мере, таким образом я мог бы перемещаться по пользователю, устанавливая положение прокрутки вручную.

Я использую Vue. js, но это не влияет на проблему под рукой, которая является CSS и JS относящиеся.

Приложение. vue

<template>
  <main id="app">
    <ul class="menu">
      <li @click="goTo('A')">Section A</li>
      <li @click="goTo('B')">Section B</li>
      <li @click="goTo('C')">Section C</li>
      <li @click="goTo('D')">Section D</li>
      <li @click="goTo('E')">Section E</li>
    </ul>
    <SectionItem id="A" color="red"/>
    <SectionItem id="B" color="green"/>
    <SectionItem id="C" color="blue"/>
    <SectionItem id="D" color="orange"/>
    <SectionItem id="E" color="purple"/>
  </main>
</template>

<script>
import jump from "jump.js";
import SectionItem from "./components/SectionItem.vue";

export default {
  name: "App",
  components: {
    SectionItem
  },
  methods: {
    goTo(id) {
      jump(`#${id}`, {
        duration: 300
      });
    }
  }
};
</script>

SectionItem. vue

<template>
  <div :id="id" class="SectionItem" :style="styles">
    <p>I am section item: {{ id }}</p>
  </div>
</template>

<script>
export default {
  name: "SectionItem",
  props: {
    id: {
      type: String,
      required: true
    },
    color: {
      type: String,
      required: true
    }
  },
  computed: {
    styles() {
      return {
        backgroundColor: this.color
      };
    }
  }
};
</script>

<style>
.SectionItem {
  position: sticky;
  top: 0;
  width: 100%;
  min-height: 100vh;
  padding: 20px;
  color: white;
  border: 10px solid white;
}
</style>

I ' Я ищу любые разумные решения, которые заставят автоматическую прокрутку работать в обоих направлениях. Большое спасибо за ваше время.

Ответы [ 2 ]

2 голосов
/ 15 февраля 2020

Прыжок JS имеет опцию для передачи числа вместо селектора, поэтому одним из способов является захват начальных вершин секции в mounted() и использование их в gotoId().

В вызове jump(number) ожидается число относительно location () , которое равно window.scrollY || window.pageYOffset, поэтому мы должны изменить его на абсолютное значение, передав отрицательный параметр offset.

export default {
  name: "App",
  components: {
    SectionItem
  },
  data() {
    return {
      tops: {}
    }
  },
  mounted() {
    this.tops = this.$children.reduce((acc, child) => {
      acc[child.id] = child.$el.getBoundingClientRect().top;
      return acc;
    }, {})
  },
  methods: {
    goTo(id) {
      const offset = -(window.scrollY || window.pageYOffset); // make jump absolute
      jump(this.tops[id], { duration: 300, offset });
    }
  }
};
1 голос
/ 15 февраля 2020

Вот как должен выглядеть ваш goTo метод:

goTo(id) {
  const el = document.querySelector(`#${id}`);
  el.style.position = "static";
  requestAnimationFrame(() => {
    jump(`#${id}`, {
      duration: 300
    });
    el.style.removeProperty('position');
  });
}

Посмотрите его здесь .

Код должен быть очевиден: установите позицию элемента в static используя встроенный стиль, чтобы вычисление было выполнено правильно и удалите атрибут позиции встроенного стиля в следующем кадре анимации, чтобы элемент отображался так, как установлено остальной частью приложения (в данном случае, CSS). Поскольку это происходит только для одного кадра, он не воспринимается человеческим глазом.

Примечание: я просто написал кое-что быстро, чтобы продемонстрировать исправление - его, вероятно, можно написать более "vue like" (рассмотрите возможность использования refs).

Еще одно замечание: если вам неудобно переключать позицию элемента для кадра и вы хотите сделать это менее агрессивно (по отношению к визуализированным элементам), другой подход заключается в клонировании всего домена. (без событий) и делать то же самое, что я предлагаю, но на клоне. Получив значение, вы можете удалить клон и передать его на реальную страницу. Очевидно, клон должен иметь position: absolute; width 100%; visibility: hidden; top: 0 и быть прямым предком <body>. В целом, я считаю, что это будет тяжелее (с точки зрения использования памяти), но в отношении реальной страницы она будет менее агрессивной (это позволит избежать каких-либо побочных эффектов) - весь макет, рисование и рендеринг будут выполнены правильно (как будто вы прокрутка). Под побочными эффектами я имею в виду крайние случаи, такие как триггеры для любых потенциальных слушателей на пересечение области просмотра, плагины аффиксов, слушатели прокрутки и т. Д. c ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...