Bootstrap

什么?CSS 能实现鼠标滚轮的横向滚动?

再次考验你的css功底,这样的横向平滑滚动效果,只用css就可以实现,想不想来挑战一下?

看到这个效果同学们脑子里第一个想到的是什么?监听鼠标的滚轮事件吗?其实也可以实现但是非常的麻烦,因为要做到平滑滚动的话,还要去算这个事件的触发频率以及滚动的距离。

我是渡一子辰老师,今天给大家介绍的是纯CSS 实现横向滚动的原理和步骤,通过具体的代码示例和深入的技术分析,让你的技术更上一层楼!

思考

要实现这个效果其实很简单,只需要换个角度看问题就行了。

我们都知道鼠标滚轮可以控制的是竖向的滚动条,那如果我们把显示器旋转 90 度呢?比如下图这样:

这样竖的滚动条是不是就变成横的了,而且鼠标依然可以控制它。怎么样?是不是很有趣。

虽然旋转显示器是在搞笑,但是这个思维的转换其实就是这个效果实现的切入点,我们可以用 css 来旋转元素,达到同样的效果。

我们今天将这个效果做成一个 vue 的通用组件,组件的名字就叫做 XScroll 吧。先看一下 XScroll 组件的嵌套结构,为什么这样嵌套看接下来的图例你就明白了。

<template>
  <div class="container">
    <div class="scroll">
      <div class="content">
        <slot></slot>
      </div>
    </div>
  </div>
</template>

如上图所示,通过之前的思考我们知道换个角度看就可以实现效果,那么我们用代码把想法实现出来就好了。

通过图 我们知道,scroll 其实是竖着的,这样 scroll 的滚动条我们才可以通过鼠标滚轮控制,然后我们通过 css 将 scroll 旋转成图 的样子,这样 scroll 的竖向滚动条就变成了横向的,我们依然可以通过鼠标滚轮来控制。

因为 container 是我们的最外围的容器,而 scroll 与 container 是一样大的,但是我们要知道 scroll 最开始是竖着的。也就说,scroll 的宽等于 container 的高,scroll 的高等于 container 的宽。

好了,思路到这里我们已经清楚了,接下来就是用代码把想法实现出来。

实现

首先我们知道 scroll 的大小是根据 container 的大小来的,所以我们必须知道 container 的大小,这里我们通过之前写过的一个 Vue 指令来获取 container 的大小,并且指令可以在 dom 元素的大小改变时获取最新的尺寸,指令的代码放在最后。

<template>
  <!-- v-size-ob 指令,可以在 dom 元素改变大小时获取 dom 元素的尺寸,并且返回尺寸 -->
  <div v-size-ob="handleChange" class="container">
    <div class="scroll">
      <div class="content">
        <slot></slot>
      </div>
    </div>
  </div>
</template>

<script setup>
import { reactive } from 'vue';
const s = reactive({ // 声明一个响应式数据存储一下
  w: 0,
  h: 0,
});
function handleChange(size) { // 通过 v-size-ob 指令的返回值获取 container 的大小
  s.w = size.width;
  s.h = size.height;
}
</script>

<style scoped>
/* 为每一个盒子加上边框方便查看效果 */
.container {
  outline: 5px solid #ec7270;
  width: 100%;
  height: 100%;
}
.scroll {
  outline: 5px solid #7985ec;
  /* 通过 vue3 的 v-bind 在 style 中获取值 */
  /* 因为宽高是有单位的,所以利用 calc 乘以 1px 给宽高加上单位 */
  /* scroll 宽等于 container 的高  */
  width: calc(v-bind("s.h") * 1px);
  /* scroll 高等于 container 的宽  */
  height: calc(v-bind("s.w") * 1px);
}
.content {
  outline: 5px solid #f1ac6a;
  height: calc(v-bind("s.h") * 1px);
}
</style>

通过以上代码我们可以的到上图中的效果,那么下一步我们就要旋转一下 content,使其放置于 scroll 中,并且 scroll 要出现滚动条。

.scroll {
  outline: 5px solid #7985ec;
  width: calc(v-bind("s.h") * 1px);
  height: calc(v-bind("s.w") * 1px);
  position: relative;
  overflow: auto;
}
.content {
  outline: 5px solid #f1ac6a;
  height: calc(v-bind("s.h") * 1px);
  position: absolute;
  left: 100%;
  transform-origin: 0 0;
  transform: rotate(90deg);
}

现在我们得到了这样的效果。

可以说现在我们已经实现了,就差最后一步了,我们将 content 旋转到 container 里就大功告成了。

按照惯例请看图。

.scroll {
  outline: 5px solid #7985ec;
  width: calc(v-bind("s.h") * 1px);
  height: calc(v-bind("s.w") * 1px);
  position: relative;
  overflow: auto;
  transform-origin: 0 0;
  transform: translateY(calc(v-bind("s.h") * 1px)) rotate(-90deg);
}

最后我们隐藏滚动条去除边框就得到了我们所需要的效果啦!

自定义指令代码

const map = new WeakMap();
const ob = new ResizeObserver((entries) => {
  for (const entry of entries) {
    const handler = map.get(entry.target);
    if (handler) {
      const box = entry.borderBoxSize[0];
      handler({
        width: box.inlineSize,
        height: box.blockSize,
      });
    }
  }
});

export default {
  mounted(el, binding) {
    ob.observe(el);
    map.set(el, binding.value);
  },
  unmounted(el) {
    ob.unobserve(el);
  },
};

总结

整个实现过程非常巧妙地使用了 css,让我们感受到了 css 的强大和灵活。

当然这只是一个非常小的知识点,因为 CSS 是我们永远学不会的语音啊。

如果你有什么问题或建议,请在评论区留言,如果你觉得这篇文章有用,请点赞收藏或分享给你的朋友!

;