基于Taro + React 实现微信小程序半圆滑块组件、半圆进度条、弧形进度条、半圆滑行轨道(附源码)

这篇具有很好参考价值的文章主要介绍了基于Taro + React 实现微信小程序半圆滑块组件、半圆进度条、弧形进度条、半圆滑行轨道(附源码)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

效果:

基于Taro + React 实现微信小程序半圆滑块组件、半圆进度条、弧形进度条、半圆滑行轨道(附源码),Taro,一些例子,taro,react.js,前端,半圆滑轨,半圆进度条,弧形进度条,半圆滑块组件

基于Taro + React 实现微信小程序半圆滑块组件、半圆进度条、弧形进度条、半圆滑行轨道(附源码),Taro,一些例子,taro,react.js,前端,半圆滑轨,半圆进度条,弧形进度条,半圆滑块组件

功能点:

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: 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
      }
    })
  }

  /** 点击设置档位 */
  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;
    }
  }
} 

防抖的工具函数debounce 的详细代码:

import { debounce } from '~/utils/util'文章来源地址https://www.toymoban.com/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;
}

到了这里,关于基于Taro + React 实现微信小程序半圆滑块组件、半圆进度条、弧形进度条、半圆滑行轨道(附源码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于 Taro 框架的微信小程序 canvas 绘图海报组件

    项目需要保存收款码,效果如图: (此文仅代表个人日常工作记录,能力有限描述并不全面) 1.安装 npm i taro-plugin-canvas -S --production(taro-plugin-canvas 是基于 Taro 框架的微信小程序 canvas 绘图组件,封装了常用的操作,通过配置的方式生成分享图片)  2.引入:import { TaroCanvas

    2024年01月21日
    浏览(47)
  • Taro微信小程序 实现下拉、触底刷新

    下拉刷新, 需要在页面的config文件中配置 enablePullDownRefresh: true, onReachBottomDistance: 50, backgroundTextStyle: \\\'dark\\\', 在代码中使用usePullDownRefresh, 当我们刷新页面时候会触发usePullDownRefresh方法,这里我们获取数据。

    2024年02月11日
    浏览(55)
  • 微信小程序自定义slider组件实现双向滑块

    实现效果: zy-slider标签是封装的自定义组件 我放到了资源这里地址:https://download.csdn.net/download/Axiaoheng_/86399342?spm=1001.2014.3001.5503 wxml引入 参数 说明 min Number/String slider 最小值 max Number/String slider 最大值 minValue Number/String slider 左边滑块初始位置 maxValue Number/String slider 右边滑块

    2024年02月10日
    浏览(30)
  • Taro:微信小程序通过获取手机号实现一键登录

            本文介绍如果通过微信小程序的getPhoneNumber方法获取用户微信绑定的手机号并自动注册登录。 1、按钮类型 openType 指定 \\\"getPhoneNumber\\\"   2、@getphonenumber必须全部小写 code:手机号获取凭证:动态令牌。可通过动态令牌换取用户手机号。         后台根据前端传入

    2024年02月16日
    浏览(56)
  • 使用taro+canvas实现微信小程序的图片分享功能

    二轮充电业务中,用户充电完成后在订单详情页展示订单相关信息,用户点击分享按钮唤起微信小程序分享菜单,将生成的图片海报分享给微信好友或者下载到本地,好友可通过扫描海报中的二维码加群领取优惠。 使用场景及功能:微信小程序 生成海报图片 分享好友 下载图

    2024年02月05日
    浏览(120)
  • 使用taro或react框架的,针对微信小程序隐私协议的组件封装,网上找了很久都没有相关模版,只有自己写了一个,现分享给有困难的同伴

    注意:我是使用hooks的,其他可以参考封装思路大同小异的 首先写一个PrivacyAgreement.js文件 样式文件index.scss如下 在需要用到隐私协议的组件或者api的页面里面引用

    2024年02月05日
    浏览(48)
  • Taro + React + TS + Taro-UI + ECharts + Markdown 开发微信小程序

    1、安装 Taro 脚手架工具 npm install -g @tarojs/cli yarn global add @tarojs/cli 2、taro 初始化项目 taro init taro-app 安装 taro-ui cd taro-app yarn add taro-ui 全局引入taro-ui样式 3、项目路由路径 page 对应项目路径 lazyCodeLoading 组件按需注入 Taro.navigateTo({url: xxx}) 小程序内部跳转 4、全局添加分享功能

    2024年02月04日
    浏览(36)
  • 微信小程序+Taro 混编,Taro 使用微信原生 behaviors

    最近有一个小程序项目,因为一些原因项目架构选择了微信小程序原生+Taro 混编的方式进行开发,在开发的过程中发现 Taro 不支持使用原生的 behaviors 特性,因为混编的原因项目当中已有原生页面在使用 behaviors,所以需要一个方案在不影响其他页面的基础上使 Taro 也能使用这

    2024年02月11日
    浏览(41)
  • 【Taro】微信小程序隐私协议改造

     微信要求小程序开发者在2023.9.15日前将小程序中调用获取用户隐私api的接口时,都必须要先让用户授权,如果用户拒绝授权,那么小程序的对应接口或组件将直接禁用。 那么首先,请将微信小程序开发者工具-详情-本地设置-基础调试库 切换至2.33.0以上。低于该调试库会报错

    2024年02月09日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包