最近因在学习RN的混合开发,项目中有个色盘
的需求,难为了好几天才搞出来,现在记录下色盘
实现,话不多说,开始敲代码:
在研究色盘的绘制之前 我们先来看下RN中我们使用到的几个手势
和参数
RN手势
// 返回值为布尔值, 如果返回值为 true,则表示这个 View 能够响应滑动手势, 两者有一个为true即可响应
onMoveShouldSetPanResponder: (e, gestureState) => {...}
onMoveShouldSetPanResponderCapture: (e, gestureState) => {...}
// 返回值为布尔值, 如果返回值为 true,则表示这个 View 能够响应触摸手势, 两者有一个为true即可响应
onStartShouldSetPanResponder: (e, gestureState) => {...}
onStartShouldSetPanResponderCapture: (e, gestureState) => {...}
onPanResponderRelease: (e, gestureState) => {...}
// 用户滑动手指时,调用该方法
onPanResponderMove: (e, gestureState) => {...}
// 返回值为布尔值, 如果返回值为 true,则表示这个 View 能够响应触摸手势
onStartShouldSetResponder={(evt) => {}
// 手势点击时候的事件
onResponderRelease={(evt) => {}
参数event(e)
获取触摸的位置在被响应的 View 中的相对坐标,evt.nativeEvent.locationX
nativeEvent
changedTouches
- 在上一次事件之后,所有发生变化的触摸事件的数组集合(即上一次事件后,所有移动过的触摸点)
identifier
- 触摸点的ID
locationX
- 触摸点相对于父元素的横坐标
locationY
- 触摸点相对于父元素的纵坐标
pageX
- 触摸点相对于根元素的横坐标
pageY
- 触摸点相对于根元素的纵坐标
target
- 触摸点所在的元素ID
timestamp
- 触摸事件的时间戳,可用于移动速度的计算
touches
- 当前屏幕上的所有触摸点的集合
gestureState
对象
stateID
-- 触摸状态的ID。在屏幕上有至少一个触摸点的情况下,这个ID会一直有效。
moveX
- 最近一次移动时的屏幕横坐标
moveY
- 最近一次移动时的屏幕纵坐标
x0
- 当响应器产生时的屏幕坐标
y0
- 当响应器产生时的屏幕坐标
dx
- 从触摸操作开始时的累计横向路程
dy
- 从触摸操作开始时的累计纵向路程
vx
- 当前的横向移动速度
vy
- 当前的纵向移动速度
numberActiveTouches
- 当前在屏幕上的有效触摸点的数量
代码实现:
书写UI:
render() {
const { radius } = this.state
return (
{this.self = node}}
onLayout={nativeEvent => this.onLayout(nativeEvent)}
style={[styles.coverResponder, this.props.style]}>
// 色盘
{
return true
}}
onResponderMove={(evt) => {
return false
}}
onResponderRelease={(evt) => {
this.handleTapData(evt)
this.updateColor(evt.nativeEvent.locationX,evt.nativeEvent.locationY,false)
}}/>
// 此View是图标的白色底部view
//此View是填充颜色的View
)
}
1、
onResponderRelease={(evt)
: 这个点击事件是点击色盘
的时候,定位图标的位置
2、{...this._panResponder.panHandlers}
: 监视器与监视区域关联,把图标和手势关联起来
定义手势
UNSAFE_componentWillMount() {
// 图标的手势
this._panResponder = PanResponder.create({
onMoveShouldSetPanResponder: (evt, gestureState) => {
return true;
},
onPanResponderMove: (evt, gestureState) => {
this.handlerData(evt,gestureState,true)
// this.updateColor(evt)
},
onPanResponderRelease: (evt, gestureState) => {
this.handlerData(evt,gestureState,false)
// 记录滑动前 图表的位置
this.lastX = this.state.offset.x;
this.lastY = this.state.offset.y;
},
});
}
关键的数据处理
handleTapData(evt,) {
console.log('开始点击',[evt.nativeEvent.locationX,evt.nativeEvent.locationY])
let tapX = evt.nativeEvent.locationX
let tapY = evt.nativeEvent.locationY
let result = this.getPointValues(tapX,tapY)
let maxX = result.maxX
let maxY = result.maxY
this.setState({
offset:{
x: maxX,
y: maxY,
}
})
this.lastX = maxX ;
this.lastY = maxY ;
}
handlerData(evt,ges,moving) {
console.log('开始滑动',[this.lastX,this.lastY])
//此时视图的坐标
let locationX = this.lastX + ges.dx + this.state.thumbSizeW / 2
let locationY = this.lastY + ges.dy + this.state.thumbSizeW / 2
// 获取在父元素的实际坐标点值
let result = this.getPointValues(locationX,locationY)
let maxX = result.maxX
let maxY = result.maxY
// 获取此点的颜色值
this.updateColor(locationX,locationY,moving)
this.setState({
offset:{
x: maxX ,
y: maxY ,
}
})
}
// 获取实际坐标的 角度 坐标值 步长
getPointValues(locationX,locationY) {
//半径
let radius = this.state.radius
// 求斜边的长度
let offsetX = Math.abs(radius - locationX)
let offsetY = Math.abs(radius - locationY)
let length = Math.sqrt(offsetX * offsetX + offsetY * offsetY)// 斜边长度
//求角度
// let angle = Math.atan2(offsetY, offsetX) * (180 / Math.PI)
let angle = this.getPointAngle(radius - locationX,radius - locationY)
// console.log('是否出界'+[locationX,locationY,angle])
// 最终的坐标
let maxX = locationX - this.state.thumbSizeW / 2//this.lastX + ges.dx
let maxY = locationY - this.state.thumbSizeW / 2//this.lastY + ges.dy
if (length > this.state.radius) {
// 超出边界
let maxOffsetX = this.state.radius * (locationX - radius) / length
let maxOffsetY = this.state.radius * (radius - locationY) / length
maxX = radius + maxOffsetX - this.state.thumbSizeW / 2
maxY = radius - maxOffsetY - this.state.thumbSizeW / 2
length = this.state.radius
}
return {
angle: angle,
maxX: maxX,
maxY: maxY,
length: length,
}
}
getPointAngle(x,y) {
let radius = this.state.radius
// 此时的角度是从左边为 0°开始 顺时针 到右边为 180° 逆时针到最右边为 -180°
let angle = Math.atan2(y, x) * (180 / Math.PI)
// 此转换为左边 0° 顺时针转一圈为 360°
angle = 180 - angle
console.log('获取角度'+angle)
return angle
}
// 根据颜色值更新图标的位置 color : #ffffff
updateUIWithColor(color) {
const {radius} = this.state
// 根据初始的颜色值回去图标的坐标
const hsv = colorsys.hex2Hsv(color)
console.log('初始值颜色'+[hsv.h,hsv.s,hsv.v])
//角度
let angle = 180 - hsv.h
// 在色盘中的步长
let length = (radius / 100) * hsv.s
//
let offsett = this.hypotenuse(length,angle)
offx = radius - offsett.x - this.state.thumbSizeW / 2
offy = radius - offsett.y - this.state.thumbSizeW / 2
this.lastX = offx;
this.lastY = offy;
this.setState({
currentColor: color,
offset:{
x: offx,
y: offy,
}
})
}
//已知角度和斜边,求直角边
hypotenuse(long,angle) {
//获得弧度
var radian = 2*Math.PI/360*angle;
return {
x:Math.cos(radian) * long,
y:Math.sin(radian) * long
};
}
// 根据坐标的位置 获取颜色值
updateColor(x,y, moving) {
const {angle,maxX, maxY,length} = this.getPointValues(x,y)
let radius = length / this.state.radius
const rad = radius > 1 ? 1 : radius
const hsv = {h:angle, s: 100 * rad, v: 100};
const currentColor = colorsys.hsv2Hex(hsv)
this.setState({ currentColor })
console.log('获取当前的颜色'+[currentColor,angle])
//滑动结束 发送当前颜色值
if (moving === false) {
this.props.onColorChange(hsv)
}
}
注意
:
1、因为里面用到了RGB
和HSV
的转化,所以我们需要导入"colorsys": "^1.0.17",
这个库
2、里面的色盘是标准色盘图片
效果图
原文地址
:https://editor.csdn.net/md/?articleId=111683437
完整代码地址
: https://download.csdn.net/download/qq_30963589/13781232