效果:
功能點(diǎn):
1、四個檔位
2、可點(diǎn)擊加減切換檔位
3、可以點(diǎn)擊區(qū)域切換檔位
4、可以滑動切換檔位
目的:
給大家提供一些實(shí)現(xiàn)思路,找了一圈,一些文章基本不能直接用,錯漏百出,代碼還藏著掖著,希望可以幫到大家
代碼
ts的寫法風(fēng)格
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
/** 是否開啟點(diǎn)擊振動 */
const isVibrateShort = true
const getAngle = () => {
return {
transform: `rotate(${state.originAngle}deg)`,
transition: `all ${state.isTouch ? ' 0.2s' : ' 0.55s'}`
}
}
/**
* 根據(jù)坐標(biāo)判斷是否在半圓軌道上,半圓為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
}
/** 根據(jù)做標(biāo)點(diǎn),獲取檔位 0 -> 4, -45 -> 3, -90 -> 2, -135 -> 1,從而獲取旋轉(zhuǎn)的角度 */
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ì)膩,根據(jù)x軸坐標(biāo),calcX判斷是否前進(jìn)還是后退
* @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) {
/** 向前滑動,就前進(jìn)一個檔位 */
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
}
})
}
/**
* 獲取正確的坐標(biāo)點(diǎn)
* @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
/** 獲取真實(shí)的做標(biāo)點(diǎn) */
const realX = pageX - left
const realY = pageY - top
resolve({
realX,
realY
})
})
.exec()
})
}
const onTouchEnd = (event: BaseTouchEvent<any>) => {
setState((old) => {
return {
...old,
isTouch: false
}
})
}
const onTouchMove = debounce(async (event: BaseTouchEvent<any>) => {
const { pageX, pageY } = event.changedTouches[0]
const { realX, realY } = await getRealXY(pageX, pageY)
if (isInHalfCircleLine(realX, realY)) {
setGearSibler(realX, realY)
}
}, 100)
const onTouchStart = async (event: BaseTouchEvent<any>) => {
const { pageX, pageY } = event.changedTouches[0]
const { realX, realY } = await getRealXY(pageX, pageY)
setState((old) => {
return {
...old,
originX: realX,
originY: realY,
isTouch: true
}
})
}
/** 點(diǎn)擊設(shè)置檔位 */
const onHandleFirstTouch = async (event: BaseTouchEvent<any>) => {
const { pageX, pageY } = event.changedTouches[0]
const { realX, realY } = await getRealXY(pageX, pageY)
if (isInHalfCircleLine(realX, realY)) {
setGear(realX, realY)
}
}
const lose = () => {
if (state.isTouch) return
if (state.val === 1) return Taro.showToast({
title: '最低只能1擋',
icon: 'error',
duration: 2000
})
setState((old) => {
return {
...old,
originAngle: state.originAngle - 45,
val: state.val - 1
}
})
if (isVibrateShort) {
Taro.vibrateShort()
}
}
const add = () => {
if (state.isTouch) return
if (state.val === 4) return Taro.showToast({
title: '最高只能4擋',
icon: 'error',
duration: 2000
})
setState((old) => {
return {
...old,
originAngle: state.originAngle + 45,
val: state.val + 1
}
})
if (isVibrateShort) {
Taro.vibrateShort()
}
}
return (
<View
className={styles.slider}
// onTouchEnd={(event) => onTouchEnd(event)}
// onTouchMove={(event) => onTouchMove(event)}
// onTouchStart={(event) => onTouchStart(event)}
onClick={onHandleFirstTouch}
>
<View className={styles.activeSliderSet}>
<View className={styles.activeSlider} style={getAngle()} />
</View>
<View className={styles.origin} id="origin">
<View className={styles.long} style={getAngle()}>
<View
className={styles.circle}
onTouchMove={(event) => onTouchMove(event as BaseTouchEvent<any>)}
onTouchStart={(event) => onTouchStart(event as BaseTouchEvent<any>)}
onTouchEnd={(event) => onTouchEnd(event as BaseTouchEvent<any>)}
/>
</View>
</View>
{/* 背景 */}
<View className={styles.sliderBgc} id="sliderBgcId" />
{/* 刻度 */}
{/* <View className={styles.scaleBgc} /> */}
<View className={styles.centerContent}>
<View className={styles.centerText}>能量檔位</View>
<View className={styles.btn_air_bar}>
<View className={classNames(styles.btn_air, styles.btn_air_left)} onClick={lose}>
-
</View>
<View className={styles.val}>
<View className="val_text">{state.val}</View>
</View>
<View className={classNames(styles.btn_air, styles.btn_air_right)} onClick={add}>
+
</View>
</View>
</View>
</View>
)
}
export default HalfCircle
index.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;
}
}
}
防抖的工具函數(shù)debounce 的詳細(xì)代碼:文章來源:http://www.zghlxwxcb.cn/news/detail-738645.html
import { debounce } from '~/utils/util'文章來源地址http://www.zghlxwxcb.cn/news/detail-738645.html
function debounce<T extends Function>(func: T, delay: number): T {
let timeout
return function (this: any, ...args: any[]): void {
const context = this;
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(context, args);
}, delay);
} as any;
}
到了這里,關(guān)于基于Taro + React 實(shí)現(xiàn)微信小程序半圓滑塊組件、半圓進(jìn)度條、弧形進(jìn)度條、半圓滑行軌道(附源碼)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!