tuya-panel-kit
Version:
a functional component library for developing tuya device panels!
230 lines (187 loc) • 6.61 kB
JavaScript
import PropTypes from 'prop-types';
import React, { PureComponent } from 'react';
import { View, PanResponder, I18nManager, ReactNativeComponentTree } from 'react-native';
let eTargetId = 0;
const isRtl = I18nManager.isRTL;
export default class Gesture extends PureComponent {
static propTypes = {
children: PropTypes.any,
pointerEvents: PropTypes.oneOf(['box-none', 'none', 'box-only', 'auto']),
disabled: PropTypes.bool,
};
static defaultProps = {
children: null,
pointerEvents: 'box-only',
disabled: false,
};
constructor(props) {
super(props);
this.eTargetId = eTargetId++;
this.notHandleReceivePropsWhenTouching = false;
let fixedEvent = {};
const fixEventHandle = (e, gesture) => [
{
nativeEvent: {
...e.nativeEvent,
...fixedEvent,
},
originEvent: e,
},
this.fixRtl(fixedEvent, gesture),
];
const responder = PanResponder.create({
onStartShouldSetPanResponder: (e, gesture) => {
if (this.props.disabled) return false;
return this.onStartShouldSetResponder.call(this, e, gesture);
},
onMoveShouldSetPanResponder: (e, gesture) => {
if (this.props.disabled) return false;
return this.onMoveShouldSetResponder.call(this, e, gesture);
},
onPanResponderGrant: (e, gesture) => {
const { nativeEvent } = e;
const { locationX, locationY, pageX, pageY } = nativeEvent;
fixedEvent = { locationX, locationY, pageX, pageY };
const event = fixEventHandle(e, gesture);
this.onGrant.call(this, ...event);
},
onPanResponderMove: (e, gesture) => {
const event = fixEventHandle(e, gesture);
if (this._initialMoveDirection === undefined) {
const [_e, _gesture] = event;
if (Math.abs(gesture.dx) < 5 && Math.abs(gesture.dy) < 5) return;
const horizontal = Math.abs(gesture.dx) >= Math.abs(gesture.dy);
const toRight = horizontal && _e.nativeEvent.locationX < _gesture.locationX;
const toLeft = horizontal && !toRight;
const toBottom = !horizontal && _e.nativeEvent.locationY < _gesture.locationY;
const toTop = !horizontal && !toBottom;
this._initialMoveDirection = {
horizontal,
toTop,
toRight,
toBottom,
toLeft,
};
} else {
this.notHandleReceivePropsWhenTouching = true;
this.onMove.call(this, ...event);
}
},
onPanResponderRelease: (e, gesture) => {
this.notHandleReceivePropsWhenTouching = false;
const event = fixEventHandle(e, gesture);
this.onRelease.call(this, ...event);
this._initialMoveDirection = undefined;
},
onPanResponderTerminationRequest: () => false,
onPanResponderTerminate: (e, gesture) => {
this.notHandleReceivePropsWhenTouching = false;
const event = fixEventHandle(e, gesture);
this.onRelease.call(this, ...event);
this._initialMoveDirection = undefined;
},
onStartShouldSetResponderCapture: () => false,
onMoveShouldSetPanResponderCapture: () => false,
});
this.getResponder = () => ({
...responder.panHandlers,
onLayout: this.onLayout,
pointerEvents: this.props.pointerEvents,
});
this.onLayout = this.onLayout.bind(this);
this.getLayout = this.getLayout.bind(this);
this.getETargetElement = this.getETargetElement.bind(this);
this.getETargetId = this.getETargetId.bind(this);
this.getInitialMoveDirection = this.getInitialMoveDirection.bind(this);
this.getTouchDirection = this.getTouchDirection.bind(this);
this.checkETargetIsExpected = this.checkETargetIsExpected.bind(this);
}
onLayout({ nativeEvent }) {
if (this._layout !== undefined && this._layout === nativeEvent.layout) return;
// if (this._layout !== undefined) return;
this._layout = { ...nativeEvent.layout };
this.forceUpdate();
}
onStartShouldSetResponder() {
return true;
}
onMoveShouldSetResponder() {
return true;
}
onMove() {}
onGrant() {}
onRelease() {}
getETargetInstance(e) {
return ReactNativeComponentTree.getInstanceFromNode(e.target);
}
getETargetElement(e) {
const inst = this.getETargetInstance(e);
return inst._currentElement;
}
getETargetId() {
return this.eTargetId;
}
getInitialMoveDirection() {
return this._initialMoveDirection;
}
getTouchDirection(x, y) {
const rect = this.getLayout();
if (!rect) return;
/* 以节点的左上角建立坐标系 */
// 表示左上角和右下角及中心点坐标
// 求各个点坐标 注意y坐标应该转换为负值,因为节点的左上角为(0,0),整个可视区域属于第四象限
// 表示鼠标当前位置的点坐标
const _y = -y;
const x1 = rect.x;
const y1 = -rect.y;
const x4 = rect.x + rect.width;
const y4 = -(rect.y + rect.height);
const x0 = rect.x + rect.width / 2;
const y0 = -(rect.y + rect.height / 2);
// 矩形不够大,不考虑
if (Math.abs(x1 - x4) < 0.0001) return 4;
// 表示左上角和右下角的对角线斜率
// 计算对角线斜率
const k = (y1 - y4) / (x1 - x4);
const range = [k, -k];
// 表示鼠标当前位置的点与元素中心点连线的斜率
const kk = (_y - y0) / (x - x0);
// 如果斜率在range范围内,则鼠标是从左右方向移入移出的
if (isFinite(kk) && range[0] < kk && kk < range[1]) {
// 根据x与x0判断左右
return x > x0 ? 1 : 3;
}
// 根据y与y0判断上下
return _y > y0 ? 0 : 2;
}
getLayout() {
return this._layout;
}
fixRtl(fixedEvent, gesture) {
const { width } = this._layout || {};
const dx = isRtl ? -gesture.dx : gesture.dx;
const locationX = isRtl && width ? width - fixedEvent.locationX : fixedEvent.locationX;
return {
...gesture,
locationX: locationX + dx,
locationY: fixedEvent.locationY + gesture.dy,
pageX: fixedEvent.pageX + gesture.dx,
pageY: fixedEvent.pageY + gesture.dy,
dx,
};
}
checkETargetIsExpected(e) {
const element = this.getETargetElement(e);
return this.eTargetId === element.props.eTargetId;
}
render() {
const { children, ...props } = this.props;
const responder = this.getResponder();
delete props.pointerEvents;
return (
<View {...props} {...responder}>
{children}
</View>
);
}
}