tsx:
import React, { Component, createRef } from 'react';
import './index.less'
interface Props { }
interface State { }
class Ball extends Component<Props, State> {
// ref获取线div
line = createRef<HTMLDivElement>()
// wrap左边到body的left值
x: number = 0
// 移动距离
mx: number = 0
// 整体最大盒子宽
px: number = 0
// 线的当前宽
wx: number = 0
// 左球 距离body
lboll: number = 0
// 右球 距离body
rboll: number = 0
constructor(props: Props) {
super(props)
this.state = {
}
}
FnStart(e: React.TouchEvent<HTMLDivElement>) {
// 球到左body的left减wrap到body的left值
this.x = e.changedTouches[0].pageX - (e.target as HTMLDivElement).offsetLeft
document.ontouchmove = this.FnMove.bind(this)
document.ontouchend = this.FnEnd.bind(this)
}
FnMove(e: TouchEvent) {
e.stopPropagation()
// 保证有线盒子,没有返回false
if (!this.line.current) return false
// 保证父盒子的div存在
if (!this.line.current.parentElement) return false
// 获取包裹盒子的宽
this.px = this.line.current.parentElement.clientWidth;
if (!this.wx) {
this.wx = this.px
}
const nowLx = this.lboll
const nowRx = this.rboll
this.lboll = this.line.current.parentElement.children[1].getBoundingClientRect().left
this.rboll = this.line.current.parentElement.children[2].getBoundingClientRect().left
// 把目标元素当做div盒子
const t = (e.target as HTMLDivElement)
// 移动的距离
this.mx = e.changedTouches[0].pageX - this.x
// 如果移动距离超过最大值 移动的值超过(包裹盒子的宽-球体的宽)
// 线的宽
if (this.mx > this.px - t.clientWidth) {
// 把移动距离等于最大值
this.mx = this.px - t.clientWidth
}
// 如果移动距离小于最小值 移动的值小于最开始的值 0
if (this.mx < 0) {
// 把移动的值赋值初始值
this.mx = 0
}
if (t.className === 'l-ball') {
// 当前线体的宽度
this.wx = this.rboll - this.lboll
// 线定位在移动的距离加上目标元素 球 一半宽
this.line.current.style.left = this.mx + t.clientWidth / 2 + 'px'
// 设置左球的定位
t.style.left = this.mx + 'px'
// 调整线条宽度
this.line.current.style.width = this.wx + 'px'
// 如果线条过短,也就是说两球距离太近,并且判断球移动的方向要继续减小线条长度
if (this.wx < 30 && nowLx <= this.lboll) {
// 判断球对盒子的left值是否在最大值(父盒子的宽减去一个球的宽度就是最大值)
if (t.offsetLeft + t.clientWidth < this.px - t.clientWidth) {
// 右球定位的left值为左球的left值加上球体宽度
(this.line.current.parentElement.children[2] as HTMLDivElement).style.left = t.offsetLeft + t.clientWidth + 'px'
} else {
// 右球最大值为父盒子减去一个球体的宽度
(this.line.current.parentElement.children[2] as HTMLDivElement).style.left = this.px - t.clientWidth + 'px'
}
}
// 左球的定位值大于或等于最大盒子的宽去掉左右两球,(触边)
if (t.offsetLeft >= this.px - t.clientWidth * 2) {
// 就把左球的定位等于最大值
t.style.left = this.px - t.clientWidth * 2 + 'px'
// 线体调整定位在中间
this.line.current.style.left = this.px - t.clientWidth * 1.5 + 'px'
}
} else {
// 操作目标元素的css样式的left
t.style.left = this.mx + 'px'
// 当前线体的宽度
this.wx = this.rboll - this.lboll
// 移动时调整线体宽度
this.line.current.style.width = this.wx + 'px'
// 如果两球距离过近,并且判断球移动的方向要继续减小线条长度
if (this.wx < 30 && nowRx > this.rboll) {
// 左球的定位调整为右球的定位left值减去一个小球的宽度
(this.line.current.parentElement.children[1] as HTMLDivElement).style.left = t.offsetLeft - t.clientWidth + 'px'
// 如果右球的定位减去半个球的宽度大于0
if (t.offsetLeft - t.clientWidth / 2 > 0) {
// 线体定位调整为定位宽减去一半球体宽
this.line.current.style.left = t.offsetLeft - t.clientWidth / 2 + 'px'
}
}
// 如果右球的定位小于或者等于球体宽度(触边)
if (t.offsetLeft <= t.clientWidth) {
// 右球定位值,最小为也要为左球留一个宽度,也就是右球最小宽为一个球体宽
t.style.left = t.clientWidth + 'px';
// 左球定位触边
(this.line.current.parentElement.children[1] as HTMLDivElement).style.left = 0 + 'px'
// 线体宽度调整为球体宽度
this.line.current.style.width = t.clientWidth + 'px'
}
}
}
FnEnd(e: TouchEvent) {
document.ontouchmove = null
}
render() {
return (
<div className='wrap'>
<div className='line' ref={this.line}></div>
<div className='l-ball' onTouchStart={this.FnStart.bind(this)} ></div>
<div className='r-ball' onTouchStart={this.FnStart.bind(this)}></div>
</div>
)
}
}
export default Ball
less:
.wrap {
width: 500px;
height: 40px;
position: relative;
background-color: #ddd;
.line {
background-color: aqua;
width: 500px;
height: 5px;
position: absolute;
top: 50%;
transform: translateY(-50%);
}
.l-ball {
width: 40px;
height: 40px;
border-radius: 50%;
background: rgb(148, 224, 6);
position: absolute;
cursor: pointer;
left: 0;
}
.r-ball {
width: 40px;
height: 40px;
cursor: pointer;
background: rgb(148, 224, 6);
border-radius: 50%;
position: absolute;
left: 500px-40px;
}
}
要记住写路由!