再次考验你的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 是我们永远学不会的语音啊。
如果你有什么问题或建议,请在评论区留言,如果你觉得这篇文章有用,请点赞收藏或分享给你的朋友!