最近因在學(xué)習(xí)RN的混合開(kāi)發(fā),項(xiàng)目中有個(gè)色盤(pán)的需求,難為了好幾天才搞出來(lái),現(xiàn)在記錄下色盤(pán)實(shí)現(xiàn),話不多說(shuō),開(kāi)始敲代碼:
在研究色盤(pán)的繪制之前 我們先來(lái)看下RN中我們使用到的幾個(gè)手勢(shì)和參數(shù)
RN手勢(shì)
// 返回值為布爾值, 如果返回值為 true,則表示這個(gè) View 能夠響應(yīng)滑動(dòng)手勢(shì), 兩者有一個(gè)為true即可響應(yīng)
onMoveShouldSetPanResponder: (e, gestureState) => {...}
onMoveShouldSetPanResponderCapture: (e, gestureState) => {...}
// 返回值為布爾值, 如果返回值為 true,則表示這個(gè) View 能夠響應(yīng)觸摸手勢(shì), 兩者有一個(gè)為true即可響應(yīng)
onStartShouldSetPanResponder: (e, gestureState) => {...}
onStartShouldSetPanResponderCapture: (e, gestureState) => {...}
onPanResponderRelease: (e, gestureState) => {...}
// 用戶滑動(dòng)手指時(shí),調(diào)用該方法
onPanResponderMove: (e, gestureState) => {...}
// 返回值為布爾值, 如果返回值為 true,則表示這個(gè) View 能夠響應(yīng)觸摸手勢(shì)
onStartShouldSetResponder={(evt) => {}
// 手勢(shì)點(diǎn)擊時(shí)候的事件
onResponderRelease={(evt) => {}
參數(shù)event(e)
獲取觸摸的位置在被響應(yīng)的 View 中的相對(duì)坐標(biāo),evt.nativeEvent.locationX
nativeEvent
changedTouches- 在上一次事件之后,所有發(fā)生變化的觸摸事件的數(shù)組集合(即上一次事件后,所有移動(dòng)過(guò)的觸摸點(diǎn))
identifier- 觸摸點(diǎn)的ID
locationX- 觸摸點(diǎn)相對(duì)于父元素的橫坐標(biāo)
locationY- 觸摸點(diǎn)相對(duì)于父元素的縱坐標(biāo)
pageX- 觸摸點(diǎn)相對(duì)于根元素的橫坐標(biāo)
pageY- 觸摸點(diǎn)相對(duì)于根元素的縱坐標(biāo)
target- 觸摸點(diǎn)所在的元素ID
timestamp- 觸摸事件的時(shí)間戳,可用于移動(dòng)速度的計(jì)算
touches- 當(dāng)前屏幕上的所有觸摸點(diǎn)的集合
gestureState對(duì)象
stateID-- 觸摸狀態(tài)的ID。在屏幕上有至少一個(gè)觸摸點(diǎn)的情況下,這個(gè)ID會(huì)一直有效。
moveX- 最近一次移動(dòng)時(shí)的屏幕橫坐標(biāo)
moveY- 最近一次移動(dòng)時(shí)的屏幕縱坐標(biāo)
x0- 當(dāng)響應(yīng)器產(chǎn)生時(shí)的屏幕坐標(biāo)
y0- 當(dāng)響應(yīng)器產(chǎn)生時(shí)的屏幕坐標(biāo)
dx- 從觸摸操作開(kāi)始時(shí)的累計(jì)橫向路程
dy- 從觸摸操作開(kāi)始時(shí)的累計(jì)縱向路程
vx- 當(dāng)前的橫向移動(dòng)速度
vy- 當(dāng)前的縱向移動(dòng)速度
numberActiveTouches- 當(dāng)前在屏幕上的有效觸摸點(diǎn)的數(shù)量
代碼實(shí)現(xiàn):
書(shū)寫(xiě)UI:
render() {
const { radius } = this.state
return (
<View
ref={node => {this.self = node}}
onLayout={nativeEvent => this.onLayout(nativeEvent)}
style={[styles.coverResponder, this.props.style]}>
<View style={[styles.img, {
height: radius * 2 ,
width: radius * 2,
}]}>
// 色盤(pán)
<Image style={{
height: radius * 2 ,
width: radius * 2,
alignItems:'center',
position:'absolute'}}
source={require('../images/colorwheel_image_caiguang.png')}
onStartShouldSetResponder={(evt) => {
return true
}}
onResponderMove={(evt) => {
return false
}}
onResponderRelease={(evt) => {
this.handleTapData(evt)
this.updateColor(evt.nativeEvent.locationX,evt.nativeEvent.locationY,false)
}}/>
// 此View是圖標(biāo)的白色底部view
<View
{...this._panResponder.panHandlers}
style={{
marginTop:this.state.offset.y,
marginLeft:this.state.offset.x,
width: this.state.thumbSizeW,
height: this.state.thumbSizeW,
position:'absolute',
backgroundColor: '#fff',
borderRadius: this.state.thumbSizeW / 2,
}}>
//此View是填充顏色的View
<View
style={{
marginTop:3,
marginLeft:3,
width: this.state.thumbSizeW - 6,
height: this.state.thumbSizeW - 6,
position:'absolute',
backgroundColor: this.state.currentColor,
borderRadius: (this.state.thumbSizeW - 6) / 2,
}}>
</View>
</View>
</View>
</View>
)
}
1、
onResponderRelease={(evt): 這個(gè)點(diǎn)擊事件是點(diǎn)擊色盤(pán)的時(shí)候,定位圖標(biāo)的位置
2、{...this._panResponder.panHandlers}: 監(jiān)視器與監(jiān)視區(qū)域關(guān)聯(lián),把圖標(biāo)和手勢(shì)關(guān)聯(lián)起來(lái)
定義手勢(shì)
UNSAFE_componentWillMount() {
// 圖標(biāo)的手勢(shì)
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)
// 記錄滑動(dòng)前 圖表的位置
this.lastX = this.state.offset.x;
this.lastY = this.state.offset.y;
},
});
}
關(guān)鍵的數(shù)據(jù)處理
handleTapData(evt,) {
console.log('開(kāi)始點(diǎn)擊',[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('開(kāi)始滑動(dòng)',[this.lastX,this.lastY])
//此時(shí)視圖的坐標(biāo)
let locationX = this.lastX + ges.dx + this.state.thumbSizeW / 2
let locationY = this.lastY + ges.dy + this.state.thumbSizeW / 2
// 獲取在父元素的實(shí)際坐標(biāo)點(diǎn)值
let result = this.getPointValues(locationX,locationY)
let maxX = result.maxX
let maxY = result.maxY
// 獲取此點(diǎn)的顏色值
this.updateColor(locationX,locationY,moving)
this.setState({
offset:{
x: maxX ,
y: maxY ,
}
})
}
// 獲取實(shí)際坐標(biāo)的 角度 坐標(biāo)值 步長(zhǎng)
getPointValues(locationX,locationY) {
//半徑
let radius = this.state.radius
// 求斜邊的長(zhǎng)度
let offsetX = Math.abs(radius - locationX)
let offsetY = Math.abs(radius - locationY)
let length = Math.sqrt(offsetX * offsetX + offsetY * offsetY)// 斜邊長(zhǎng)度
//求角度
// let angle = Math.atan2(offsetY, offsetX) * (180 / Math.PI)
let angle = this.getPointAngle(radius - locationX,radius - locationY)
// console.log('是否出界'+[locationX,locationY,angle])
// 最終的坐標(biāo)
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
// 此時(shí)的角度是從左邊為 0°開(kāi)始 順時(shí)針 到右邊為 180° 逆時(shí)針到最右邊為 -180°
let angle = Math.atan2(y, x) * (180 / Math.PI)
// 此轉(zhuǎn)換為左邊 0° 順時(shí)針轉(zhuǎn)一圈為 360°
angle = 180 - angle
console.log('獲取角度'+angle)
return angle
}
// 根據(jù)顏色值更新圖標(biāo)的位置 color : #ffffff
updateUIWithColor(color) {
const {radius} = this.state
// 根據(jù)初始的顏色值回去圖標(biāo)的坐標(biāo)
const hsv = colorsys.hex2Hsv(color)
console.log('初始值顏色'+[hsv.h,hsv.s,hsv.v])
//角度
let angle = 180 - hsv.h
// 在色盤(pán)中的步長(zhǎng)
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
};
}
// 根據(jù)坐標(biāo)的位置 獲取顏色值
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('獲取當(dāng)前的顏色'+[currentColor,angle])
//滑動(dòng)結(jié)束 發(fā)送當(dāng)前顏色值
if (moving === false) {
this.props.onColorChange(hsv)
}
}
注意:
1、因?yàn)槔锩嬗玫搅?code>RGB和HSV的轉(zhuǎn)化,所以我們需要導(dǎo)入"colorsys": "^1.0.17",這個(gè)庫(kù)
2、里面的色盤(pán)是標(biāo)準(zhǔn)色盤(pán)圖片
效果圖
原文地址:https://editor.csdn.net/md/?articleId=111683437
完整代碼地址: https://download.csdn.net/download/qq_30963589/13781232