Redbattle
踩坑
环境
项目
配置
知识点
踩坑
环境
项目
配置
知识点
  • 视频方案
  • DNS
  • 主题切换
  • 图片性能优化
  • 富文本预览
  • 区域滚动方法
  • 底部固定区域
  • CSS实现背景透明文字不透明
  • 文字展开收起
  • 二次开发
  • 两个区域关联滚动
  • scheme
2026-01-27

两个区域关联滚动

npm i vue-scrollto
<template>
  <div class="scroll-link">
    <div class="scroll-link-container" id="scroll_left" @mouseenter="mouseActive = 'left'">
      <div
        class="scroll-link-item"
        ref="item_left"
        v-for="(item, key) in tableData"
        :key="key"
        :id="`item_left_${key}`"
      >
        {{ key }}
        {{ item.left }}
      </div>
    </div>
    <div class="scroll-link-container" id="scroll_right" @mouseenter="mouseActive = 'right'">
      <div
        class="scroll-link-item"
        ref="item_right"
        v-for="(item, key) in tableData"
        :key="key"
        :id="`item_right_${key}`"
      >
        {{ key }}
        {{ item.right }}
      </div>
    </div>
  </div>
</template>

<script>
import { scroller } from 'vue-scrollto/src/scrollTo';

// debounce 方法见 /pages/77ac4f/
import { debounce } from 'utils.js'

const scrollDefault = {
  duration: 300,
  offset: -20,
};

export default {
  props: {},
  watch: {},
  data() {
    return {
      mouseActive: '',
      scrollTo: null,
      scroll_left: null,
      scroll_right: null,
      tableData: [
        {
          left: '左侧列表',
          right: '右侧列表',
        },
        {
          left: '左侧列表',
          right: '右侧列表',
        },
        {
          left: '左侧列表',
          right: '右侧列表',
        },
        {
          left: '左侧列表',
          right: '右侧列表',
        },
        {
          left: '左侧列表',
          right: '右侧列表',
        },
      ],
    };
  },
  created() {
    this.mouseActive = '';
  },
  mounted() {
    this.initScroll();
  },
  methods: {
    initScroll() {
      this.$nextTick(() => {
        this.destroyScroll();
        this.scrollTo = scroller();
        this.scroll_left = document.querySelector('#scroll_left');
        this.scroll_right = document.querySelector('#scroll_right');
        this.scroll_left.addEventListener('scroll', this.handleScrollLeft);
        this.scroll_right.addEventListener('scroll', this.handleScrollRight);
      });
    },
    handleScroll(handleTarget, scrollTarget) {
      if (this.mouseActive !== handleTarget) {
        return;
      }
      this.$nextTick(() => {
        const itemRefs = this.$refs[`item_${handleTarget}`];
        if (itemRefs && itemRefs.length > 0) {
          const scrollKeyPart = [];
          const scrollKeyFull = [];
          const parent_position = this[`scroll_${handleTarget}`].getBoundingClientRect() || {};
          itemRefs.forEach((item, key) => {
            const item_position = item.getBoundingClientRect() || {};
            if (item_position.bottom >= parent_position.top && item_position.top <= parent_position.bottom) {
              scrollKeyPart.push(key);
              if (item_position.top >= parent_position.top) {
                scrollKeyFull.push(key);
              }
            }
          });
          debounce(() => {
            let scrollKey = null;
            if (scrollKeyFull.length > 0) {
              scrollKey = scrollKeyFull[0];
            } else if (scrollKeyPart.length > 0) {
              scrollKey = scrollKeyPart[0];
            }
            console.log('scrollKey', scrollKey);
            if (scrollKey === null) {
              return;
            }
            this.scrollTo(`#item_${scrollTarget}_${scrollKey}`, {
              ...scrollDefault,
              container: `#scroll_${scrollTarget}`,
            });
          }, 300);
        }
      });
    },
    handleScrollLeft() {
      this.handleScroll('left', 'right');
    },
    handleScrollRight() {
      this.handleScroll('right', 'left');
    },
    destroyScroll() {
      if (this.scroll_left) {
        this.scroll_left.removeEventListener('scroll', this.handleScrollLeft);
        this.scroll_left = null;
      }
      if (this.scroll_right) {
        this.scroll_right.removeEventListener('scroll', this.handleScrollRight);
        this.scroll_right = null;
      }
      if (this.scrollTo) {
        this.scrollTo.destroy();
        this.scrollTo = null;
      }
    },
  },
  beforeDestroy() {
    this.destroyScroll();
  },
};
</script>

<style lang="less" scoped>
.scroll-link {
  display: flex;
  &-container {
    flex: 1;
    border: 1px solid #ccc;
    padding: 12px;
    height: 400px;
    overflow-y: auto;
  }
  &-item {
    height: 200px;
    border: 1px solid #eee;
  }
}
</style>
最近更新
01
烧虾球
05-13
02
二次开发
12-20
03
文字展开收起
10-17
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式