Files
2025-08-18 09:57:10 +08:00

158 lines
4.2 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="container" :style="containerStyle">
<section :class="indicatorClass?`selectBox ${indicatorClass}`:'selectBox'" :style="indicatorStyle?indicatorStyle:{height: `${config.rowHeight}${unit}`}">
</section>
<section class="columns" :style="columnsStyle" @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend">
<section
:class="index===currentTarget?'row select-row':'row'"
v-for="(item,index) in list" :key="index" :style="selectRowStyle?{...selectRowStyle,...rowStyle}:rowStyle">
<slot :item="item">
<span>{{item}}</span>
</slot>
</section>
</section>
</view>
</template>
<script setup lang="ts">
import {computed, ref, watch} from "vue";
const props=withDefaults(defineProps<{
list: Array<any>,
value?: number,
indicatorStyle?: object,
indicatorClass?: string,
selectRowStyle?: object,
height?: number,
rowHeight?: number,
}>(),{
value: 0,
indicatorClass: "",
height: 40,
rowHeight: 6
})
const emits=defineEmits<{
(e: 'change',value: number):void,
}>()
// 单位
const unit="vh";
// 配置项
const config=ref({
rowHeight: props.rowHeight,
containerHeight: props.height,
columnsPx: 0,
})
const containerStyle=computed(()=>({
height: `${config.value.containerHeight}${unit}`
}))
const columnsStyle=computed(()=>({
top: `${config.value.columnsPx}${unit}`
}))
const rowStyle=computed(()=>({
height: `${config.value.rowHeight}${unit}`
}))
// 列居中位置(容器高度/2
const pYCenter=computed(()=>config.value.containerHeight/2)
// 行居中位置(行高度/2
const rowPYCenter=computed(()=>config.value.rowHeight/2)
// 最大下滑位置
const maxPosition=computed(()=>config.value.rowHeight*props.list.length-1);
// 当前选中的列下标
const currentTarget=ref(props.value);
const computedCurrentTarget=()=>{
// 选中行的位置为:当前列下标*行高+列居中位置-行居中位置
config.value.columnsPx=-(currentTarget.value*config.value.rowHeight)+pYCenter.value-rowPYCenter.value;
}
watch(()=>currentTarget.value,value=>{
// 计算当前选中的下标
computedCurrentTarget();
emits('change',value);
},{immediate: true})
// 监听滑动事件
let moveDistance=0;
let originPosition=0;
const moveSpeed=6;//滑动速度倍速
const touchstart=(e:TouchEvent)=>{
const touch = e.touches[0];
moveDistance=touch.clientY/moveSpeed;
originPosition=config.value.columnsPx;
}
const touchmove=(e:TouchEvent)=>{
const touch = e.touches[0];
config.value.columnsPx=originPosition+(touch.clientY/moveSpeed)-moveDistance;
}
const touchend=(e:TouchEvent)=>{
originPosition=moveDistance=0;
const current=-(Math.round((config.value.columnsPx-pYCenter.value)/config.value.rowHeight)+1)
// 判断是否超过最大下滑位置或最大上滑位置
if(current<0){
currentTarget.value=0;
}else if(current>props.list.length-1){
currentTarget.value=props.list.length-1;
}else{
// 根据位置重新计算当前行下标
// 当前下标=四舍五入(当前位置/行高度)
currentTarget.value=current;
}
computedCurrentTarget();
}
</script>
<style scoped lang="scss">
.container{
position: relative;
box-sizing: border-box;
width: 100%;
height: 200px;
//padding: 10px ;
overflow: hidden;
&:after{
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(rgba(255,255,255,0.9) 0,transparent 50%,rgba(255,255,255,0.9) 100%);
pointer-events: none;
z-index: 99;
}
.selectBox{
box-sizing: border-box;
position: absolute;
top: 50%;
transform: translateY(-50%);
left: 0;
width: 100%;
background: rgba(0,0,0,0.03)
}
.columns{
position: absolute;
top: 0;
width: 100%;
height: 100%;
//background: red;
z-index: 10;
transition: 100ms;
.row{
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
width: 100%;
height: 40px;
text-align: center;
font-size: 18px;
transition: 200ms;
//border: #000000 solid 1px;
//color: $font-color-secondary
}
.select-row{
color: #6DFFDFFF;
font-size: 28px;
}
}
}
</style>