1. 场景
通过鼠标上下滚动控制横向滚动,先看下面效果:
2. 具体实现
接下来看一下具体实现过程:
2.1 页面布局
定义一个基本的页面布局,用于包裹滚动内容。这个布局包括一个外部的 scroll-container
和内部的 v-scroll
(实际进行横向滚动的区域,通过 CSS 变换将其旋转和位移,以模拟纵向滚动控制横向滚动的效果) 以及实际的内容容器 content
。使用slot
方便外部内容传入。
js
<div v-size="size" class="scroll-container">
<div class="v-scroll">
<div class=" content">
<slot></slot>
</div>
</div>
</div>
为了实时获取内容的宽高,写一个自定义指令进行获取。
2.2 获取元素的宽高自定义指令
使用了自定义指令 v-size
来动态获取 scroll-container
的宽高,返回宽高信息。
ResizeObserverStore
类:用于管理 ResizeObserver 实例,确保在组件卸载时能够正确移除监听。useResizeObserver
和removeResizeObserver
函数:分别用于添加和移除 ResizeObserver 实例。
js
class ResizeObserverStore {
observer = new Map()
get(target){
return this.observer.get(target) || []
}
set(target,observer){
const observers = this.observer.get(target)
if(observers){
observers.push(observer)
}else{
this.observer.set(target,[observer])
}
}
remove(target){
const observers = this.observer.get(target)
if(observers){
observers.forEach(observer => {
observer.disconnect()
});
this.observer.delete(target)
}
}
}
const resizeObserverStore = new ResizeObserverStore()
export const useResizeObserver = (target,callback)=>{
const observer = new ResizeObserver(callback)
observer.observe(target)
resizeObserverStore.set(target,observer)
return observer
}
export const removeResizeObserver = (target)=>{
resizeObserverStore.remove(target)
}
指令代码:
js
import { useResizeObserver, removeResizeObserver } from './resize'
export const vSize = {
mounted(el, binding) {
useResizeObserver(el, (entries) => {
const { width, height } = entries[0].contentRect
binding.value.width = width
binding.value.height = height
})
},
beforeUnmount(el) {
removeResizeObserver(el)
}
}
全局引入:
js
import { vSize } from './directives'
app.directive('size', vSize)
局部引入,组件内引入即可:
js
import { vSize } from './directives'
2.3 CSS 样式与变换
- 设置
scroll-container
容器宽高为百分之百 - 获取
content
中内容的宽高,外部容器设置固定宽高,此时展示为 - 将内容根据v-scroll定位和transfer旋转展示
针对css进行说明:
scss
.v-scroll {
--w: calc(v-bind(size.width) * 1px);
--h: calc(v-bind(size.height) * 1px);
width: var(--w);
height: var(--h);
position: relative;
overflow: auto;
}
将元素内容设置滚动和宽高,效果如下:
进行左上角旋转,此时会看不见元素,需要向下平移高度
scss
transform-origin: left top ;
transform: translateY(var(--h)) rotate(-90deg);
变成:
此时将内容的进行旋转展示,去除滚动条
scss
.v-scroll::-webkit-scrollbar {
display: none;
}
.content {
position: absolute;
width: var(--w);
height: var(--h);
top: 0;
left: var(--h);
transform-origin: left top;
transform: rotate(90deg);
}
完整css:
scss
.scroll-container {
width: 100%;
height: 100%;
}
.v-scroll {
--w: calc(v-bind(size.width) * 1px);
--h: calc(v-bind(size.height) * 1px);
width: var(--w);
height: var(--h);
position: relative;
overflow: auto;
transform-origin: left top ;
transform: translateY(var(--h)) rotate(-90deg);
}
.v-scroll::-webkit-scrollbar {
display: none;
}
.content {
position: absolute;
width: var(--w);
height: var(--h);
top: 0;
left: var(--h);
transform-origin: left top;
transform: rotate(90deg);
}
最后使用:
ini
<XScroll>
<div style="display: flex;">
<div v-for="item in imgList">
<img :src="item" alt="" srcset=""/>
</div>
</div>
</XScroll>
const imgList = ref([])
实现效果的关键点:
.v-scroll
:通过transform
属性将其旋转-90deg
并向下位移其高度,这样原本的垂直滚动就变成了水平滚动。.content
:通过transform
属性将其旋转90deg
,恢复内容的正常显示方向。同时,将其位置调整至.v-scroll
的右侧(通过left: var(--h)
),匹配旋转后的布局。
完整XScroll组件代码:
vue
<template>
<div v-size="size" class="scroll-container">
<div class="v-scroll">
<div class=" content">
<slot></slot>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const size = ref({
width: 0,
height: 0
})
</script>
<style scoped lang="scss">
.scroll-container {
width: 100%;
height: 100%;
}
.v-scroll {
--w: calc(v-bind(size.width) * 1px);
--h: calc(v-bind(size.height) * 1px);
width: var(--w);
height: var(--h);
position: relative;
overflow: auto;
transform-origin: left top ;
transform: translateY(var(--h)) rotate(-90deg);
}
.v-scroll::-webkit-scrollbar {
display: none;
}
.content {
position: absolute;
width: var(--w);
height: var(--h);
top: 0;
left: var(--h);
transform-origin: left top;
transform: rotate(90deg);
}
</style>
3. 总结
最后总结一下: 实现一个通过鼠标上下滚动控制横向滚动的功能,关键点在于:
- 动态获取尺寸 :使用自定义指令
v-size
和 ResizeObserver,动态获取并绑定容器的宽高。 - CSS 变换 :通过 CSS 的
transform
属性,实现了将垂直滚动转换为水平滚动的效果,并保持了内容的正常显示方向。
如有错误,请指正O^O!