react canvas的三角形结合antd-mobile的滑动输入条,实现动态控制三角形的大小

本文介绍了一个使用Ant Design Mobile和MobX实现的滑动条组件,该组件不仅包含标准的滑动条功能,还创新地集成了一个根据滑动条值动态变化的三角形进度指示器。文章详细描述了如何利用React、MobX和HTML5 Canvas来创建这个交互式的UI元素,同时展示了如何将滑动条的值映射到三角形的大小和位置,提供了一种新颖的数据可视化方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述
效果如上图⬆️

import { Slider } from 'antd-mobile';
import { inject, observer } from 'mobx-react';
//当前屏幕宽度
const screenWidth = window.screen.width;
@inject('configStore')
@observer
export default class HomePage extends React.Component {
  //初始化三角形
  async componentDidMount() {
    const { configStore } = this.props;
    this.loadTriangle(configStore.incomeRating);
  }
  
  render = () => { 
	const { incomeRating, sliderX } = this.props.configStore;
	const transX = ((Number(sliderX) + 3) * 100) / 20;
	return (
	   //三角形	
	 <canvas
	     className="arcBar"
	     id="triangle"
	     width={(screenWidth - 40) * 3}
	     height="480"
	     style={{ width: `${screenWidth - 40}px`, height: '160px' }}
	    />
	    //滑动输入条
	    <Slider
	     //默认停留值
	      defaultValue={6.5}
	      min={-3}
	      max={17}
	      //点击幅度
	      step={0.25}
	      onChange={this.onSliderChange}
	      handleStyle={{
	        borderWidth: '4px',
	        marginTop: '-7px',
	        borderColor: '#FFF',
	        background: '#468BFD',
	        height: '18px',
	        width: '18px',
	      }}
	      //选中部分滑动条的样式
	      trackStyle={{
	        // background: '#4288f7',
	        background: `url(${historyIncomeRateImg})`,
	        height: '4px',
	      }}
	      //未选中部分滑动条的样式
	      railStyle={{
	        background: '#c9d2dc',
	        height: '4px',
	      }}
	    />
	    //刻度标记
	    <div className="slider-mark">
	      <span>{transX > 12 ? '-3%' : ''}</span>
	      <span
	        className="slider-mark-move"
	        //Math.min(x,y) 取得指定数字中带有最低值的数字
	        style={{ left: `${Math.min(transX, 95)}%`, marginLeft: '-2%' }}>
	        {sliderX}%
	      </span>
	      <span>{transX < 85 ? '17%' : ''}</span>
	    </div>
	  </div>
	)
  }
  //滑动输入条值改变事件
  onSliderChange = (n) => {
  //固定数据,通过滑动的值拿到对应的概率    
	let rate = probabilityMap.filter((p) => p.incomeRate === n)[0].probability;
	console.log('onSliderChange: ', n, 'rate:', rate);
	
	this.props.configStore.setIncomeRating(rate);
	this.props.configStore.setSliderX(n);
	
	if (rate > 95) {
	 rate = 95;
	}
	if (rate < 5) {
	 rate = 5;
	}
	//传入不同的概率 渲染不同体积的三角形
	this.loadTriangle(rate);
	};
	
	//渲染三角形
	loadTriangle = (_rate) => {
	  const canvas = document.getElementById('triangle') as HTMLCanvasElement;
	  const ctx = canvas.getContext('2d');
	  const { width, height } = canvas;
	  const h = (160 - 90) * 3;
	  // const rate = 1 - _rate;
	  const rate = _rate / 100;
	
	  ctx.clearRect(0, 0, width, height);
	  drawTriangle(ctx, 0, height, width, height, width, h, '#F4F6F9', '#F4F6F9');
	  drawTriangle(
	    ctx,
	    0,
	    height,
	    width * rate,
	    height,
	    width * rate,
	    height - rate * (height - h),
	    '#FFF',
	    '#468BFD',
	  );
	
	  let startX = rate;
	  if (rate <= 0.07) {
	    startX = 0.07;
	  }
	  if (rate >= 0.93) {
	    startX = 0.93;
	  }
	
	  ctx.beginPath();
	  // 起始位置
	  ctx.lineWidth = 1 * 3;
	  ctx.strokeStyle = '#468BFD';
	  ctx.moveTo(width * rate, height);
	  ctx.lineTo(width * rate, height - rate * (height - h) - 10 * 3);
	  ctx.stroke();
	
	  const reactW = 46 * 3;
	  const reactH = 25 * 3;
	  const radius = 4 * 3;
	  drawRoundReact(
	    ctx,
	    width * startX - reactW / 2,
	    height - rate * (height - h) - reactH - 10 * 3 - 6 * 3,
	    reactW,
	    reactH,
	    radius,
	    rate,
	  );
	};

}

CSS部分

.am-slider {
  padding-top: 20px;
}
.am-slider-step {
  height: 60px !important;
}
.slider-mark {
   width: 100%;
   position: relative;
   display: flex;
   justify-content: space-between;
   margin-top: 22px;

   > span {
     font-family: Roboto-Regular;
     font-size: 12px;
     color: #9c9fb0;
     letter-spacing: 0;
   }

   > .slider-mark-move {
     position: absolute;
     color: #466187;
   }
}

configStore.ts

import { observable,action } from 'mobx';
export default class HomeStore { 
 @observable incomeRating = 95; // 发生的概率
 @observable sliderX = 6.5; // x轴位置
 @action setIncomeRating(value) {
    this.incomeRating = value;
  }
 @action setSliderX(n) {
    this.sliderX = n;
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值