实现圆弧形拖动进度条步骤详解

这次给大家带来实现圆弧形拖动进度条步骤详解,实现圆弧形拖动进度条的注意事项有哪些,下面就是实战案例,一起来看一下。

先上效果图

实现圆弧形拖动进度条步骤详解

因为需求需要实现这个效果图 非原生实现,

难点1:绘制 使用svg

难点2:点击事件的处理

难点3:封装

由于绘制需要是使用svg

此处自行百度 按照svg以及api 教学

视图代码块

 render() { return (    //实际圆环  {this._renderCircleSvg()}  // 计算中心距离     // 暴露给外部渲染圆环中心的接口   {this.props.renderCenterView(this.state.temp)}     ); _renderCircleSvg() { //中心点 const cx = this.props.width / 2; const cy = this.props.height / 2; //计算是否有偏差角 对应图就是下面缺了一块的 const prad = this.props.angle / 2 * (Math.PI / 180); //三角计算起点 const startX = -(Math.sin(prad) * this.props.r) + cx; const startY = cy + Math.cos(prad) * this.props.r;  //终点 const endX = Math.sin(prad) * this.props.r + cx; const endY = cy + Math.cos(prad) * this.props.r; // 计算进度点 const progress = parseInt(  this._circlerate() * (360 - this.props.angle) / 100,  10 ); // 根据象限做处理 苦苦苦 高中数学全忘了,参考辅助线 const t = progress + this.props.angle / 2; const progressX = cx - Math.sin(t * (Math.PI / 180)) * this.props.r; const progressY = cy + Math.cos(t * (Math.PI / 180)) * this.props.r;// SVG的描述 这里百度下就知道什么意思 const descriptions = [  'M',  startX,  startY,  'A',  this.props.r,  this.props.r,  0,  1,  1,  endX,  endY, ].join(' '); const progressdescription = [  'M',  startX,  startY,  'A',  this.props.r,  this.props.r,  0,  //根据角度是否是0,1 看下效果就知道了  t >= 180 + this.props.angle / 2 ? 1 : 0,  1,  progressX,  progressY, ].join(' '); return (           ); }}

登录后复制

事件处理代码块

// 参考react native 官网对手势的讲解 iniPanResponder() { this.parseToDeg = this.parseToDeg.bind(this); this._panResponder = PanResponder.create({  // 要求成为响应者:  onStartShouldSetPanResponder: () => true,  onStartShouldSetPanResponderCapture: () => true,  onMoveShouldSetPanResponder: () => true,  onMoveShouldSetPanResponderCapture: () => true,  onPanResponderGrant: evt => {  // 开始手势操作。给用户一些视觉反馈,让他们知道发生了什么事情!  if (this.props.enTouch) {   this.lastTemper = this.state.temp;   const x = evt.nativeEvent.locationX;   const y = evt.nativeEvent.locationY;   this.parseToDeg(x, y);  }  },  onPanResponderMove: (evt, gestureState) => {  if (this.props.enTouch) {   let x = evt.nativeEvent.locationX;   let y = evt.nativeEvent.locationY;   if (Platform.OS === 'android') {   x = evt.nativeEvent.locationX + gestureState.dx;   y = evt.nativeEvent.locationY + gestureState.dy;   }   this.parseToDeg(x, y);  }  },  onPanResponderTerminationRequest: () => true,  onPanResponderRelease: () => {  if (this.props.enTouch) this.props.complete(this.state.temp);  },  // 另一个组件已经成为了新的响应者,所以当前手势将被取消。  onPanResponderTerminate: () => {},  // 返回一个布尔值,决定当前组件是否应该阻止原生组件成为JS响应者  // 默认返回true。目前暂时只支持android。  onShouldBlockNativeResponder: () => true, }); }//画象限看看就知道了 就是和中线点计算角度parseToDeg(x, y) { const cx = this.props.width / 2; const cy = this.props.height / 2; let deg; let temp; if (x >= cx && y = cx && y >= cy) {  deg = Math.atan((cy - y) / (cx - x)) * 180 / Math.PI;  temp =  (270 + deg - this.props.angle / 2) /   (360 - this.props.angle) *   (this.props.max - this.props.min) +  this.props.min; } else if (x <= cx && y <= cy) {  deg = Math.atan((x - cx) / (y - cy)) * 180 / Math.PI;  temp =  (180 - this.props.angle / 2 - deg) /   (360 - this.props.angle) *   (this.props.max - this.props.min) +  this.props.min; } else if (x = cy) {  deg = Math.atan((cx - x) / (y - cy)) * 180 / Math.PI;  if (deg < this.props.angle / 2) {  deg = this.props.angle / 2;  }  temp =  (deg - this.props.angle / 2) /   (360 - this.props.angle) *   (this.props.max - this.props.min) +  this.props.min; } if (temp = this.props.max) {  temp = this.props.max; } //因为提供步长,所欲需要做接近步长的数 temp = this.getTemps(temp); this.setState({  temp, }); this.props.valueChange(this.state.temp); } getTemps(tmps) { const k = parseInt((tmps - this.props.min) / this.props.step, 10); const k1 = this.props.min + this.props.step * k; const k2 = this.props.min + this.props.step * (k + 1); if (Math.abs(k1 - tmps) > Math.abs(k2 - tmps)) return k2; return k1; }

登录后复制

完整代码块

import React, { Component } from 'react';import { View, StyleSheet, PanResponder, Platform, Text } from 'react-native';import Svg, { Circle, Path } from 'react-native-svg';export default class CircleView extends Component { static propTypes = { height: React.PropTypes.number, width: React.PropTypes.number, r: React.PropTypes.number, angle: React.PropTypes.number, outArcColor: React.PropTypes.object, progressvalue: React.PropTypes.object, tabColor: React.PropTypes.object, tabStrokeColor: React.PropTypes.object, strokeWidth: React.PropTypes.number, value: React.PropTypes.number, min: React.PropTypes.number, max: React.PropTypes.number, tabR: React.PropTypes.number, step: React.PropTypes.number, tabStrokeWidth: React.PropTypes.number, valueChange: React.PropTypes.func, renderCenterView: React.PropTypes.func, complete: React.PropTypes.func, enTouch: React.PropTypes.boolean, }; static defaultProps = { width: 300, height: 300, r: 100, angle: 60, outArcColor: 'white', strokeWidth: 10, value: 20, min: 10, max: 70, progressvalue: '#ED8D1B', tabR: 15, tabColor: '#EFE526', tabStrokeWidth: 5, tabStrokeColor: '#86BA38', valueChange: () => {}, complete: () => {}, renderCenterView: () => {}, step: 1, enTouch: true, }; constructor(props) { super(props); this.state = {  temp: this.props.value, }; this.iniPanResponder(); } iniPanResponder() { this.parseToDeg = this.parseToDeg.bind(this); this._panResponder = PanResponder.create({  // 要求成为响应者:  onStartShouldSetPanResponder: () => true,  onStartShouldSetPanResponderCapture: () => true,  onMoveShouldSetPanResponder: () => true,  onMoveShouldSetPanResponderCapture: () => true,  onPanResponderGrant: evt => {  // 开始手势操作。给用户一些视觉反馈,让他们知道发生了什么事情!  if (this.props.enTouch) {   this.lastTemper = this.state.temp;   const x = evt.nativeEvent.locationX;   const y = evt.nativeEvent.locationY;   this.parseToDeg(x, y);  }  },  onPanResponderMove: (evt, gestureState) => {  if (this.props.enTouch) {   let x = evt.nativeEvent.locationX;   let y = evt.nativeEvent.locationY;   if (Platform.OS === 'android') {   x = evt.nativeEvent.locationX + gestureState.dx;   y = evt.nativeEvent.locationY + gestureState.dy;   }   this.parseToDeg(x, y);  }  },  onPanResponderTerminationRequest: () => true,  onPanResponderRelease: () => {  if (this.props.enTouch) this.props.complete(this.state.temp);  },  // 另一个组件已经成为了新的响应者,所以当前手势将被取消。  onPanResponderTerminate: () => {},  // 返回一个布尔值,决定当前组件是否应该阻止原生组件成为JS响应者  // 默认返回true。目前暂时只支持android。  onShouldBlockNativeResponder: () => true, }); } componentWillReceiveProps(nextProps) { if (nextProps.value != this.state.temp) {  this.state = {  temp: nextProps.value,  }; } } parseToDeg(x, y) { const cx = this.props.width / 2; const cy = this.props.height / 2; let deg; let temp; if (x >= cx && y = cx && y >= cy) {  deg = Math.atan((cy - y) / (cx - x)) * 180 / Math.PI;  temp =  (270 + deg - this.props.angle / 2) /   (360 - this.props.angle) *   (this.props.max - this.props.min) +  this.props.min; } else if (x <= cx && y <= cy) {  deg = Math.atan((x - cx) / (y - cy)) * 180 / Math.PI;  temp =  (180 - this.props.angle / 2 - deg) /   (360 - this.props.angle) *   (this.props.max - this.props.min) +  this.props.min; } else if (x = cy) {  deg = Math.atan((cx - x) / (y - cy)) * 180 / Math.PI;  if (deg < this.props.angle / 2) {  deg = this.props.angle / 2;  }  temp =  (deg - this.props.angle / 2) /   (360 - this.props.angle) *   (this.props.max - this.props.min) +  this.props.min; } if (temp = this.props.max) {  temp = this.props.max; } temp = this.getTemps(temp); this.setState({  temp, }); this.props.valueChange(this.state.temp); } getTemps(tmps) { const k = parseInt((tmps - this.props.min) / this.props.step, 10); const k1 = this.props.min + this.props.step * k; const k2 = this.props.min + this.props.step * (k + 1); if (Math.abs(k1 - tmps) > Math.abs(k2 - tmps)) return k2; return k1; } render() { return (    {this._renderCircleSvg()}     {this.props.renderCenterView(this.state.temp)}     ); } _circlerate() { let rate = parseInt(  (this.state.temp - this.props.min) *  100 /  (this.props.max - this.props.min),  10 ); if (rate  100) {  rate = 100; } return rate; } _renderCircleSvg() { const cx = this.props.width / 2; const cy = this.props.height / 2; const prad = this.props.angle / 2 * (Math.PI / 180); const startX = -(Math.sin(prad) * this.props.r) + cx; const startY = cy + Math.cos(prad) * this.props.r; // // 最外层的圆弧配置 const endX = Math.sin(prad) * this.props.r + cx; const endY = cy + Math.cos(prad) * this.props.r; // 计算进度点 const progress = parseInt(  this._circlerate() * (360 - this.props.angle) / 100,  10 ); // 根据象限做处理 苦苦苦 高中数学全忘了,参考辅助线 const t = progress + this.props.angle / 2; const progressX = cx - Math.sin(t * (Math.PI / 180)) * this.props.r; const progressY = cy + Math.cos(t * (Math.PI / 180)) * this.props.r; const descriptions = [  'M',  startX,  startY,  'A',  this.props.r,  this.props.r,  0,  1,  1,  endX,  endY, ].join(' '); const progressdescription = [  'M',  startX,  startY,  'A',  this.props.r,  this.props.r,  0,  t >= 180 + this.props.angle / 2 ? 1 : 0,  1,  progressX,  progressY, ].join(' '); return (           ); }}const styles = StyleSheet.create({ svg: {},});

登录后复制

外部调用

   {   }}   valueChange={temp => {}}   renderCenterView={temp => (         )}   enTouch={true} />  

登录后复制

相信看了本文案例你已经掌握了方法,更多精彩请关注【创想鸟】其它相关文章!

推荐阅读:

JS实现文本字体打印界面

vue组件写法规范

以上就是实现圆弧形拖动进度条步骤详解的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2766304.html

(0)
上一篇 2025年3月8日 11:13:09
下一篇 2025年2月25日 14:57:38

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • vue2.0实现注册登录步骤详解

    这次给大家带来vue2.0实现注册登录步骤详解,vue2.0实现注册登录的注意事项有哪些,下面就是实战案例,一起来看一下。 前言 前段时间和公司一个由技术转产品的同事探讨他的职业道路,对我说了一句深以为然的话: “不要把自己禁锢在某一个领域…

    2025年3月8日
    200
  • code spliting优化Vue打包步骤详解

    这次给大家带来code spliting优化Vue打包步骤详解,code spliting优化Vue打包的注意事项有哪些,下面就是实战案例,一起来看一下。 在http1的时代,比较常见的一种性能优化就是合并http的请求数量,通常我们会把许…

    2025年3月8日 编程技术
    200
  • slot分发内容步骤详解

    这次给大家带来slot分发内容步骤详解,slot分发内容的注意事项有哪些,下面就是实战案例,一起来看一下。 一、什么是slot 在使用组件时,我们常常要像这样组合它们: 登录后复制 当需要让组件组合使用,混合父组件的内容与子组件的模板时,就…

    2025年3月8日
    200
  • props传递数据步骤详解

    这次给大家带来props传递数据步骤详解 ,props传递数据的注意事项有哪些,下面就是实战案例,一起来看一下。 在 Vue 中,父子组件的关系可以总结为 props向下传递,事件向上传递。父组件通过 props 给子组件下发数据,子组件通…

    2025年3月8日
    300
  • vue-i18n标准的使用步骤

    这次给大家带来vue-i18n标准的使用步骤,vue-i18n标准使用的注意事项有哪些,下面就是实战案例,一起来看一下。 需求 公司项目需要国际化,点击按钮切换中文/英文 1、安装 npm install vue-i18n –save 登…

    编程技术 2025年3月8日
    200
  • Vue做出内部组件轮播切换步骤详解

    这次给大家带来Vue做出内部组件轮播切换步骤详解,Vue做出内部组件轮播切换的注意事项有哪些,下面就是实战案例,一起来看一下。 对于那些不需要路由的内部组件,在切换的时候希望增加一个轮播过渡的效果,效果如下: 我们可以引入一个轮播组件,但是…

    2025年3月8日 编程技术
    400
  • Vue使用Sortable步骤详解

    这次给大家带来Vue使用Sortable步骤详解,Vue使用Sortable的注意事项有哪些,下面就是实战案例,一起来看一下。 之前开发一个后台管理系统,里面用到了Vue和Element-UI这个组件库,遇到一个挺有意思的问题,和大家分享一…

    编程技术 2025年3月8日
    400
  • JS实现动态进度条步骤分析

    这次给大家带来JS实现动态进度条步骤分析,JS实现动态进度条的注意事项有哪些,下面就是实战案例,一起来看一下。 本文实例为大家分享了js实现动态进度条效果的具体代码,供大家参考,具体内容如下 1.效果 2.源码 window.onload …

    2025年3月8日
    200
  • Nuxt.js实现服务端渲染步骤详解

    这次给大家带来Nuxt.js实现服务端渲染步骤详解,Nuxt.js实现服务端渲染的注意事项有哪些,下面就是实战案例,一起来看一下。 2016 年 10 月 25 日,zeit.co 背后的团队对外发布了一个 React 的服务端渲染应用框架…

    2025年3月8日 编程技术
    200
  • 实现js同源策略与跨域访问步骤详解

    这次给大家带来实现js同源策略与跨域访问步骤详解,实现js同源策略与跨域访问的注意事项有哪些,下面就是实战案例,一起来看一下。 1. 什么是同源策略 理解跨域首先必须要了解同源策略。同源策略是浏览器上为安全性考虑实施的非常重要的安全策略。 …

    编程技术 2025年3月8日
    200

发表回复

登录后才能评论