效果如上图⬆️
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;
}
}