1、四个档位
2、可点击加减切换档位
3、可以点击区域切换档位
4、可以滑动切换档位
目的:给大家提供一些实现思路,找了一圈,一些文章基本不能直接用,错漏百出,代码还藏着掖着,希望可以帮到大家
代码ts的写法风格
index.tsx
import { View, ITouchEvent, BaseTouchEvent } from '@tarojs/components' import Taro from '@tarojs/taro' import { useState } from 'react' import styles from './index.module.less' import classNames from 'classnames' import { debounce } from '~/utils/util' enum ANGLES { ANGLES_135 = -135, ANGLES_90 = -90, ANGLES_45 = -45, ANGLES_0 = 0 } enum MODE_VALUE { MODE_1 = 1, MODE_2 = 2, MODE_3 = 3, MODE_4 = 4 } const HalfCircle = () => { const [state, setState] = useState({ originAngle: ANGLES.ANGLES_135, isTouch: false, val: MODE_VALUE.MODE_1, originX: 0, originY: 0 }) /** 半圆的半径 */ const RADIUS = 150 /** 半径的一半 */ const RADIUS_HALF = RADIUS / 2 /** 4/3 圆的直径 */ const RADIUS_THIRD = RADIUS_HALF * 3 /** 直径 */ const RADIUS_DOUBLE = RADIUS * 2 /** 误差 */ const DEVIATION = 25 /** 是否开启点击振动 */ const isVibrateShort = true const getAngle = () => { return { transform: `rotate(${state.originAngle}deg)`, transition: `all ${state.isTouch ? ' 0.2s' : ' 0.55s'}` } } /** * 根据坐标判断是否在半圆轨道上,半圆为RADIUS,误差为DEVIATION * @param pageX * @param pageY */ const isInHalfCircleLine = (pageX: number, pageY: number, deviation?: number) => { const DEVIATION_VALUE = deviation || DEVIATION const squareSum = (pageX - RADIUS) * (pageX - RADIUS) + (pageY - RADIUS) * (pageY - RADIUS) const min = (RADIUS - DEVIATION_VALUE) * (RADIUS - DEVIATION_VALUE) const max = (RADIUS + DEVIATION_VALUE) * (RADIUS + DEVIATION_VALUE) return squareSum >= min && squareSum <= max } /** 根据做标点,获取档位 0 -> 4, -45 -> 3, -90 -> 2, -135 -> 1,从而获取旋转的角度 */ const setGear = (pageX: number, pageY: number) => { let val = state.val let originAngle = state.originAngle if (isInHalfCircleLine(pageX, pageY)) { if (pageX > 0 && pageX <= RADIUS_HALF) { val = MODE_VALUE.MODE_1 originAngle = ANGLES.ANGLES_135 } else if (pageX > RADIUS_HALF && pageX <= RADIUS) { val = MODE_VALUE.MODE_2 originAngle = ANGLES.ANGLES_90 } else if (pageX > RADIUS && pageX <= RADIUS_THIRD) { val = MODE_VALUE.MODE_3 originAngle = ANGLES.ANGLES_45 } else { val = MODE_VALUE.MODE_4 originAngle = ANGLES.ANGLES_0 } } if (state.val === val) return setState((old) => { return { ...old, originAngle, val } }) if (isVibrateShort) { setTimeout(() => { Taro.vibrateShort() }, 200) } } /** * 滑动比较细腻,根据x轴坐标,calcX判断是否前进还是后退 * @param pageX * @param pageY */ const setGearSibler = (pageX: number, pageY: number) => { let val = state.val let originAngle = state.originAngle const calcX = pageX - state.originX /** 把误差值增加,方便滑动 */ if (isInHalfCircleLine(pageX, pageY, 50)) { if (pageX > 0 && pageX <= RADIUS_HALF) { if (calcX > 0) { /** 向前滑动,就前进一个档位 */ val = MODE_VALUE.MODE_2 originAngle = ANGLES.ANGLES_90 } else { /** 向后滑动,就后退一个档位 */ val = MODE_VALUE.MODE_1 originAngle = ANGLES.ANGLES_135 } } else if (pageX > RADIUS_HALF && pageX <= RADIUS) { if (calcX > 0) { val = MODE_VALUE.MODE_2 originAngle = ANGLES.ANGLES_90 } else { val = MODE_VALUE.MODE_1 originAngle = ANGLES.ANGLES_135 } } else if (pageX > RADIUS && pageX <= RADIUS_THIRD) { if (calcX > 0) { val = MODE_VALUE.MODE_3 originAngle = ANGLES.ANGLES_45 } else { val = MODE_VALUE.MODE_2 originAngle = ANGLES.ANGLES_90 } } else { if (calcX > 0) { val = MODE_VALUE.MODE_4 originAngle = ANGLES.ANGLES_0 } else { val = MODE_VALUE.MODE_3 originAngle = ANGLES.ANGLES_45 } } } setState((old) => { return { ...old, originAngle, val } }) } /** * 获取正确的坐标点 * @param pageX * @param pageY * @returns */ const getRealXY = ( pageX: number, pageY: number ): Promise<{ realX: number realY: number }> => { return new Promise((resolve) => { Taro.createSelectorQuery() .select('#sliderBgcId') .boundingClientRect((rect) => { const { left, top } = rect /** 获取真实的做标点 */ const realX = pageX - left const realY = pageY - top resolve({ realX, realY }) }) .exec() }) } const onTouchEnd = (event: BaseTouchEventindex.module.less
@color-brand: #EBC795 ; @borderColor:#706D6D; @sliderWidth:10px; @radius:150px; @long: 150px; @border-radius: @long; .slider { position: relative; padding-bottom: @sliderWidth / 2; background-color: #000; width: 100vw; display: flex; justify-content: center; align-items: center; // 背景色 .sliderBgc { width: @long*2; height: @long; border: @sliderWidth solid; border-radius: @border-radius @border-radius 0 0; border-color: @borderColor; border-bottom: none; } .scaleBgc { width: @long*2 + @sliderWidth *2; height: @long + @sliderWidth; position: absolute; // bottom: 0; // left: 0; border: @sliderWidth solid; border-radius: @border-radius + @sliderWidth @border-radius + @sliderWidth 0 0; border-color: transparent; border-bottom: none; top: -10px; background-clip: padding-box, border-box; background-origin: padding-box, border-box; background-image: linear-gradient(to right, #000, #000), linear-gradient(90deg, #FFD1B2, #E49E6B); } // 激活色 .activeSliderSet { position: absolute; width: (@long) *2; height: @long; // left: 0; // bottom: 0; z-index: 2; overflow: hidden; .activeSlider { bottom: 0; left: 0; width: @long*2; height: @long; border: @sliderWidth solid; border-color: @color-brand; // border-color: transparent !important; border-radius: @border-radius @border-radius 0 0; border-bottom: none; transform: rotate(-100deg); transform-origin: @long @long; // background-clip: padding-box, border-box; // background-origin: padding-box, border-box; // background-image: linear-gradient(to right, #000, #000), linear-gradient(90deg, #FFD1B2, #E49E6B); } } .origin { width: 0; height: 0; position: absolute; background-color: rgba(0, 0, 0, 0.1); bottom: 0; left: 50%; z-index: 11; transform: translateX(50%); .long { width: @long - (@sliderWidth / 2); height: 0; z-index: 9999; position: absolute; top: 0; left: 0; transform-origin: 0 0; .circle { width: 16px; height: 16px; border-radius: 50%; position: absolute; top: 50%; right: 0; transform: translate(50%, -50%); background-color: #000; border: #fff 4px solid; z-index: 999; padding: 5px; } } } } .centerContent { position: absolute; bottom: 0; left: 50%; transform: translateX(-50%); z-index: 99; margin-bottom: 20px; .centerText { text-align: center; color: var(--q-light-color-text-secondary, var(--text-secondary, #8C8C8C)); font-size: 10px; margin-bottom: 25px; } .btn_air_bar { display: flex; align-items: center; .btn_air { width: 30px; height: 30px; border-radius: 50%; background-color: wheat; font-size: 16px; font-weight: 500; display: flex; align-items: center; justify-content: center; } .btn_air_left { background-color: #706D6D; color: white; } .btn_air_right { background-color: white; color: #706D6D; } .val { height: 26px; display: flex; align-items: center; margin: 0 30px; font-size: 26px; font-weight: 700; } } }防抖的工具函数debounce 的详细代码:
import { debounce } from '~/utils/util'
function debounce