Disabling tabbar swiping over Tinder Like Cards UI - javascript

I've 3 Tabs, built using react-native-router-flux.
I'm using react-native-swiper-cards to create a Tinder Like Card Swiping UI on the center tab.
But if the card is been tried to swipe with a "complete or almost close to horizontal gesture", then instead of the card been dragged, the tabs are getting swiped. In all other cases cards are been dragged without the tab getting swiped.
How to disable the Tab Swiping when cards are getting dragged totally horizontally?
Tabs.js
<Scene
{...navbarPropsTabs}
key={'tabBar'} tabs={true}
swipeEnabled={true}
default="swipe">
<Scene
{...navbarPropsTabs}
key={'settings'}
component={SettingsScreen}
></Scene>
<Scene
{...navbarPropsTabs}
key={'swipe'}
component={SwipeScreen}
initial
></Scene>
<Scene
{...navbarPropsTabs}
key={'profile'}
component={ProfileScreen}>
</Scene>
Thats the code exact from the react-native-swipe-cards module.
this._panResponder = PanResponder.create({
onMoveShouldSetPanResponderCapture: (e, gestureState) => {
if (Math.abs(gestureState.dx) > this.minDeltaThreshold || Math.abs(gestureState.dy) > this.minDeltaThreshold) {
this.props.onDragStart();
return true;
}
return false;
},
onPanResponderGrant: (e, gestureState) => {
this.state.pan.setOffset({ x: this.state.pan.x._value, y: this.state.pan.y._value });
this.state.pan.setValue({ x: 0, y: 0 });
},
onPanResponderTerminationRequest: (evt, gestureState) => this.props.allowGestureTermination,
onPanResponderMove: Animated.event([
null, { dx: this.state.pan.x, dy: this.props.dragY ? this.state.pan.y : 0 },
]),
onPanResponderRelease: (e, {vx, vy, dx, dy}) => {
this.props.onDragRelease()
this.state.pan.flattenOffset();
let velocity;
if (Math.abs(dx) <= 5 && Math.abs(dy) <= 5) //meaning the gesture did not cover any distance
{
this.props.onClickHandler(this.state.card)
}
if (vx > 0) {
velocity = clamp(vx, 3, 5);
} else if (vx < 0) {
velocity = clamp(vx * -1, 3, 5) * -1;
} else {
velocity = dx < 0 ? -3 : 3;
}
const hasSwipedHorizontally = Math.abs(this.state.pan.x._value) > SWIPE_THRESHOLD
const hasSwipedVertically = Math.abs(this.state.pan.y._value) > SWIPE_THRESHOLD
if (hasSwipedHorizontally || (hasSwipedVertically && this.props.hasSwipeUpAction)) {
let cancelled = false;
const hasMovedRight = hasSwipedHorizontally && this.state.pan.x._value > 0
const hasMovedLeft = hasSwipedHorizontally && this.state.pan.x._value < 0
const hasMovedUp = hasSwipedVertically && this.state.pan.y._value < 0
if (hasMovedRight) {
cancelled = this.props.onSwipeRight(this.state.card);
} else if (hasMovedLeft) {
cancelled = this.props.onSwipeLeft(this.state.card);
} else if (hasMovedUp && this.props.hasSwipeUpAction) {
cancelled = this.props.onSwipeUp(this.state.card);
} else {
cancelled = true
}
//Yup or nope was cancelled, return the card to normal.
if (cancelled) {
this._resetPan();
return;
};
this.props.onCardRemoved(currentIndex[this.guid]);
if (this.props.smoothTransition) {
this._advanceState();
} else {
this.cardAnimation = Animated.decay(this.state.pan, {
velocity: { x: velocity, y: vy },
deceleration: 0.98
});
this.cardAnimation.start(status => {
if (status.finished) this._advanceState();
else this._resetState();
this.cardAnimation = null;
}
);
}
} else {
this._resetPan();
}
}
});

Related

Fetch all images at once and show 360 product view in Reactjs

I have already written a component that enables 360 product views for users. Users can interact with image (rotate to the right or left). This Component works well when images are from local files.
I want to fetch images from Cloudinary and render them. A product may have 100+ images so I want to show loader till all images are loaded. Once done image will render and when the user rotates new image is rendered. I was also looking for npm lib that takes care of loading and rendering images.
import React, { Component } from "react";
import "./styles.css";
import { Row, Space, Typography, Image } from "antd";
import { Md3DRotation } from "react-icons/md";
// You can play with this to adjust the sensitivity
// higher values make mouse less sensitive
const pixelsPerDegree = 1;
class React360 extends Component {
static defaultProps = { dir: "puma", numImages: 83 };
state = {
dragging: false,
imageIndex: 0,
dragStartIndex: 0,
images: [],
show360: false,
imgs:null
};
componentDidMount = () => {
document.addEventListener("mousemove", this.handleMouseMove, false);
document.addEventListener("mouseup", this.handleMouseUp, false);
// document.addEventListener("touchstart", this.handleMouseMove, false);
// document.addEventListener("touchend", this.handleMouseUp, false);
};
componentWillUnmount = () => {
document.removeEventListener("mousemove", this.handleMouseMove, false);
document.removeEventListener("mouseup", this.handleMouseUp, false);
// document.removeEventListener("touchstart", this.handleMouseMove, false);
// document.removeEventListener("touchend", this.handleMouseUp, false);
};
handleMouseDown = (e) => {
// console.log("e.screenX",Math.round(e?.touches?.[0]?.clientX))
e.persist();
this.setState((state) => ({
dragging: true,
dragStart: e.screenX || Math.round(e?.touches?.[0]?.clientX),
dragStartIndex: state.imageIndex,
}));
};
handleMouseUp = () => {
this.setState({ dragging: false });
};
updateImageIndex = (currentPosition) => {
let numImages = this.props.numImages;
const pixelsPerImage = pixelsPerDegree * (360 / numImages);
const { dragStart, imageIndex, dragStartIndex } = this.state;
// pixels moved
let dx = (currentPosition - dragStart) / pixelsPerImage;
let index = Math.floor(dx) % numImages;
if (index < 0) {
index = numImages + index - 1;
}
index = (index + dragStartIndex) % numImages;
// console.log(index, dragStartIndex, numImages)
if (index !== imageIndex) {
this.setState({ imageIndex: index === 0 ? 1 : index });
}
};
handleMouseMove = (e) => {
// console.log("handleMouseMove",this.state.dragging)
console.log("screenX", Math.round(e?.touches?.[0]?.clientX));
if (this.state.dragging) {
this.updateImageIndex(e.screenX || Math.round(e?.touches?.[0]?.clientX));
}
};
preventDragHandler = (e) => {
e.preventDefault();
};
renderImage = () => {
const { imageIndex, images } = this.state;
return (
<>
<div className="react360">
<img
alt=""
src={
require(`../../assets/puma_opti/IMG_00${
imageIndex > 9 ? imageIndex : "0" + imageIndex
}.JPG`).default
}
/>
</div>
<Row justify="center" style={{ marginTop: "1.5em" }}>
<Space>
<Md3DRotation size={25} color="#8C8C8C" />
<Typography.Title
level={5}
type="secondary"
style={{ fontFamily: "Gilroy" }}
className="helptext-360"
>
Drag to view 360 degrees
</Typography.Title>
</Space>
</Row>
</>
);
};
render = () => {
return (
<div
className="react-360-img"
onMouseDown={this.handleMouseDown}
onDragStart={this.preventDragHandler}
// onTouchStart={this.handleMouseMove}
onTouchStart={this.handleMouseDown}
onTouchMove={this.handleMouseMove}
onTouchEnd={this.handleMouseUp}
>
{this.renderImage()}
</div>
);
};
}
1.For library you can use React Loading Skeleton : https://www.npmjs.com/package/react-loading-skeleton
2.If you want to display loading when you get data you have to add condition to your jsx, for instance:
fetchData.loading?(
return <Skeleton/>
):(
<div className="react360">
data.map(res=>{
<img
alt=""
src={
require(`../../assets/puma_opti/IMG_00${
imageIndex > 9 ? imageIndex : "0" + imageIndex}.JPG`).default
}
/>
}))
</div>
N.B: fetchdata is the status of your API call (if is loading or successful), data is data obtained from API.

Not getting smooth cursor animation in react

I am using this react code for getting a custom cursor following the main cursor. animation is done using gsap and lerp function. But the animation is not seamless and the chrome performance monitor shows the CPU usage is passing 100%. pls help me figure out this problem. I have referred to this video link for getting the cursor animation: https://www.youtube.com/watch?v=MEO6yQLAgKw&list=PLtSHrBhMos7hXeImRWnnC38mdYp_4c333&index=2&t=630s
import gsap from 'gsap';
import React, {Component} from 'react';
import './cursor.scss';
class Cursor extends Component{
constructor(props){
super(props);
this.state={
x : 0,
y : 0
};
this.cursor = React.createRef();
this.cursorConfigs = {
x: { previous: 0, current: 0, amt: 0.2 },
y: { previous: 0, current: 0, amt: 0.2 },
};
this.lerp = (a, b, n) => (1 - n) * a + n * b;
}
componentDidMount(){
window.addEventListener("mousemove", e=> {
this.setState({
x: e.pageX,
y:e.pageY
})
});
this.cursor.current.style.opacity = 0;
this.onMouseMoveEv = () => {
this.cursorConfigs.x.previous = this.cursorConfigs.x.current = this.state.x;
this.cursorConfigs.y.previous = this.cursorConfigs.y.current = this.state.y;
gsap.to(this.cursor.current,{
duration: 1,
ease: "Power4.easeOut",
opacity: 1,
});
window.removeEventListener("mousemove", this.onMouseMoveEv);
requestAnimationFrame(() =>this.render());
};
window.addEventListener("mousemove", this.onMouseMoveEv);
}
render(){
this.cursorConfigs.x.current = this.state.x;
this.cursorConfigs.y.current = this.state.y;
for (const Key in this.cursorConfigs){
this.cursorConfigs[Key].previous = this.lerp(
this.cursorConfigs[Key].previous,
this.cursorConfigs[Key].current,
this.cursorConfigs[Key].amt
);
}
console.log(this.cursorConfigs.x.previous, this.cursorConfigs.x.current)
var styles = {
transform:`translateX(${this.cursorConfigs.x.previous}px) translateY(${this.cursorConfigs.y.previous}px)`
}
requestAnimationFrame(() =>this.render());
return(
<div className="cursor" ref={this.cursor} style={styles}>
<div className="cursor-media">
</div>
</div>
)
}
}
export default Cursor;```
You actualy don't need to update on every mousemove. Consider debounce setState
example:
// delay in ms
function debounced(delay, fn) {
let timerId;
return function (...args) {
if (timerId) {
clearTimeout(timerId);
}
timerId = setTimeout(() => {
fn(...args);
timerId = null;
}, delay);
};
}
function handler(e) {
this.setState({
x: e.pageX,
y: e.pageY,
});
};
window.addEventListener("mousemove", debounced(200, handler));

Change color to kelvin

I come to you because I have a problem with this library react-native-color-wheel, I would like to change the wheel so that it becomes a wheel of colors in kelvin. I've tried to tinker with several things but without any effect, I'm really struggling with polar calculations and all that goes with it if someone could point me to it.
Thanks to you guys
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { Animated, Image, Dimensions, PanResponder, View } from 'react-native';
import colorsys from 'colorsys';
import styles from './styles';
export default class ColorWheelWhite extends Component {
static propTypes = {
toggleMeToforceUpdateInitialColor: PropTypes.number,
initialColor: PropTypes.string,
onColorChangeComplete: PropTypes.func,
thumbSize: PropTypes.number,
onColorChange: PropTypes.func,
thumbStyle: PropTypes.object,
prevState: PropTypes.string,
style: PropTypes.object
};
static defaultProps = {
thumbSize: 50,
initialColor: '#ffffff',
onColorChange: () => {}
};
static getDerivedStateFromProps(props, state) {
const { toggleMeToforceUpdateInitialColor } = props;
if (toggleMeToforceUpdateInitialColor !== state.toggleMeToforceUpdateInitialColor) {
return {
toggleMeToforceUpdateInitialColor
};
}
return null;
}
constructor(props) {
super(props);
const { initialColor, toggleMeToforceUpdateInitialColor = 0 } = props;
this.state = {
offset: { x: 100, y: 100 },
currentColor: initialColor,
pan: new Animated.ValueXY(),
toggleMeToforceUpdateInitialColor,
radius: 200,
panHandlerReady: true,
didUpdateThumb: false
};
}
componentDidMount = () => {
this._panResponder = PanResponder.create({
onStartShouldSetPanResponderCapture: () => true,
onStartShouldSetPanResponder: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderGrant: ({ nativeEvent }) => {
if (this.outBounds(nativeEvent)) return;
if (!this.state.didUpdateThumb) {
this.updateColorAndThumbPosition(nativeEvent);
}
},
onPanResponderMove: (event, gestureState) => {
if (this.outBounds(gestureState)) return;
if (!this.state.didUpdateThumb) {
const { nativeEvent } = event;
this.updateColorAndThumbPosition(nativeEvent);
}
this.resetPanHandler();
return Animated.event(
[
null,
{
dx: this.state.pan.x,
dy: this.state.pan.y
}
],
{ listener: this.updateColor }
)(event, gestureState);
},
onMoveShouldSetPanResponder: () => true,
onPanResponderRelease: ({ nativeEvent }) => {
this.setState({
panHandlerReady: true,
didUpdateThumb: false
});
this.state.pan.flattenOffset();
const { radius } = this.calcPolar(nativeEvent);
if (radius < 0.1) {
this.forceUpdate('#ffffff');
}
if (this.props.onColorChangeComplete) {
this.props.onColorChangeComplete(this.state.hsv);
}
}
});
};
componentDidUpdate(prevProps, prevState) {
const { initialColor } = this.props;
if (initialColor && this.state.toggleMeToforceUpdateInitialColor !== prevState.toggleMeToforceUpdateInitialColor) {
this.forceUpdate(initialColor);
}
}
updateColorAndThumbPosition(nativeEvent) {
this.updateColor({ nativeEvent });
this.state.pan.setValue({
x: -this.state.left + nativeEvent.pageX - this.props.thumbSize / 2,
y: -this.state.top + nativeEvent.pageY - this.props.thumbSize / 2
});
this.setState({
didUpdateThumb: true
});
}
onLayout = () => {
this.measureOffset();
};
measureOffset() {
/*
* const {x, y, width, height} = nativeEvent.layout
* onlayout values are different than measureInWindow
* x and y are the distances to its previous element
* but in measureInWindow they are relative to the window
*/
this.self.measureInWindow((x, y, width, height) => {
const window = Dimensions.get('window');
const absX = x % width;
const radius = Math.min(width, height) / 2;
const offset = {
x: absX + width / 2,
y: (y % window.height) + height / 2
};
this.setState({
offset,
radius,
height,
width,
top: y % window.height,
left: absX
});
this.forceUpdate(this.state.currentColor);
});
}
calcPolar(gestureState) {
const { pageX, pageY, moveX, moveY } = gestureState;
const [x, y] = [pageX || moveX, pageY || moveY];
const [dx, dy] = [x - this.state.offset.x, y - this.state.offset.y];
return {
deg: Math.atan2(dy, dx) * (-180 / Math.PI),
// pitagoras r^2 = x^2 + y^2 normalized
radius: Math.sqrt(dy * dy + dx * dx) / this.state.radius
};
}
outBounds(gestureState) {
const { radius } = this.calcPolar(gestureState);
return radius > 1;
}
resetPanHandler() {
if (!this.state.panHandlerReady) {
return;
}
this.setState({ panHandlerReady: false });
this.state.pan.setOffset({
x: this.state.pan.x._value,
y: this.state.pan.y._value
});
this.state.pan.setValue({ x: 0, y: 0 });
}
calcCartesian(deg, radius) {
const r = radius * this.state.radius; // was normalized
const rad = (Math.PI * deg) / 180;
const x = r * Math.cos(rad);
const y = r * Math.sin(rad);
return {
left: this.state.width / 2 + x,
top: this.state.height / 2 - y
};
}
updateColor = ({ nativeEvent }) => {
const { deg, radius } = this.calcPolar(nativeEvent);
console.log("deg");
console.log(deg);
console.log("radius");
console.log(radius);
const hsv = { h: deg, s: 10 * radius, v: 100 };
const currentColor = colorsys.hsv2Hex(hsv);
this.setState({ hsv, currentColor });
console.log("LE HSV");
console.log(this.state.hsv);
this.props.onColorChange(hsv);
};
forceUpdate = color => {
const { h, s, v } = colorsys.hex2Hsv(color);
const { left, top } = this.calcCartesian(h, s / 100);
this.setState({ currentColor: color });
this.props.onColorChange({ h, s, v });
this.state.pan.setValue({
x: left - this.props.thumbSize / 2,
y: top - this.props.thumbSize / 2
});
};
animatedUpdate = color => {
const { h, s, v } = colorsys.hex2Hsv(color);
const { left, top } = this.calcCartesian(h, s / 100);
this.setState({ currentColor: color });
this.props.onColorChange({ h, s, v });
Animated.spring(this.state.pan, {
toValue: {
x: left - this.props.thumbSize / 2,
y: top - this.props.thumbSize / 2
}
}).start();
};
setRef = ref => {
this.self = ref;
};
render() {
const { radius } = this.state;
const thumbStyle = [
styles().circle,
this.props.thumbStyle,
{
width: this.props.thumbSize,
height: this.props.thumbSize,
borderRadius: this.props.thumbSize / 2,
backgroundColor: this.state.currentColor,
opacity: this.state.offset.x === 0 ? 0 : 1
}
];
const panHandlers = (this._panResponder && this._panResponder.panHandlers) || {};
return (
<View ref={this.setRef} onLayout={this.onLayout} style={[styles().coverResponder, this.props.style]} pointerEvents={'box-none'}>
<Image
style={[
styles().img,
{
height: radius * 2,
width: radius * 2,
borderRadius: radius - this.props.thumbSize
}
]}
source={require('../../assets/color-wheel-white.png')}
{...panHandlers}
/>
<Animated.View style={[this.state.pan.getLayout(), thumbStyle]} pointerEvents={'box-none'} />
</View>
);
}
}

How to detect component collision in react-native?

First off, I'm new to asking stack overflow questions, so go easy on me guys.
I am trying to figure out how to detect collision of components; namely, a draggable component containing a button-circle component. Sadly, there appears to be zero information online regarding this. I'm thinking the draggable component may be able to handle some sort of radius tracking, which is computed when the user takes their finger off the screen, but hard tracking the radius would seem to be very resource intensive. I really need some ideas here guys. Relevant code is below.
Draggable.js
import React, { Component } from "react";
import {
PanResponder,
Animated,
} from "react-native";
import CircleButton from 'react-native-circle-button';
export default class Draggable extends Component {
constructor(props) {
super(props);
this.state = {
pan: new Animated.ValueXY(),
scale: new Animated.Value(1),
};
}
//Function to handle animations and button pressing of bubbles.
componentWillMount() {
let finalx = 0;
let finaly = 0;
this._panResponder = PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => false,
onMoveShouldSetPanResponder: (evt, gestureState) => {
return gestureState.dx <= finalx && gestureState.dy <= finaly;
},
onMoveShouldSetPanResponderCapture: (evt, gestureState) => {
return gestureState.dx <= finalx && gestureState.dy <= finaly;
},
onPanResponderGrant: (e, gestureState) => {
this.state.pan.setValue({x: 0, y: 0});
this.finalx = gestureState.dx;
this.finaly = gestureState.dy;
Animated.spring(
this.state.scale,
{ toValue: 1.1, friction: 3 }
).start();
},
onPanResponderMove: Animated.event([
null, {dx: this.state.pan.x, dy: this.state.pan.y},
]),
onPanResponderRelease: (e, {vx, vy}) => {
this.state.pan.flattenOffset();
Animated.spring(
this.state.pan,
{ toValue: {x:0, y:0}},
this.state.scale,
{ toValue: 1, friction: 3 }
).start();
}
});
}
render() {
let { pan } = this.state;
let rotate = '0deg';
let [translateX, translateY] = [pan.x, pan.y];
let moveableStyle = {transform: [{translateX}, {translateY}, {rotate}]};
const panStyle = {
transform: this.state.pan.getTranslateTransform()
}
return (
<Animated.View
{...this._panResponder.panHandlers}
style={[moveableStyle, panStyle]}>
<CircleButton />
</Animated.View>
);
}
}
Homepage.js
<View style={styles.middleContainer}>
{ this.state.bubbles.map((item, key)=>(
<Draggable key={key}> { item }</Draggable>)
)}
</View>

detect when another view is touched - dragging with PanResponder in react native

I have a card game where the user can drag cards around a screen.
How can I detect if the cards have been dragged over other View components? My draggable card code is like this:
// A draggable card
// drag drop code example used: https://github.com/brentvatne/react-native-animated-demo-tinder
// panresponder docs: https://facebook.github.io/react-native/docs/panresponder.html
class Card extends React.Component {
componentWillMount() {
this.state = {
pan: new Animated.ValueXY(),
enter: new Animated.Value(1),
}
this._panResponder = PanResponder.create({
onMoveShouldSetResponderCapture: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderGrant: (e, gestureState) => {
Animated.spring(this.state.enter, {
toValue: .75,
}).start()
this.state.pan.setOffset({x: this.state.pan.x._value,
y: this.state.pan.y._value});
this.state.pan.setValue({x: 0, y: 0});
},
onPanResponderMove: Animated.event([
null, {dx: this.state.pan.x, dy: this.state.pan.y},
]),
onPanResponderRelease: (e, {vx, vy}) => {
// do stuff when card released
}
Animated.spring(this.state.enter, {
toValue: 1,
}).start()
this.state.pan.flattenOffset();
var velocity;
if (vx >= 0) {
velocity = clamp(vx, 3, 5);
} else if (vx < 0) {
velocity = clamp(vx * -1, 3, 5) * -1;
}
Animated.spring(this.state.pan, {
toValue: {x: 0, y: toValue},
friction: 4
}).start()
}
})
}
I don't know if this would be the best way to go about it, but I would just get the onLayout of the target Views to get the x,y,width, and height...
<View onLayout={({nativeEvent: {layout: {x,y,width,height}}) => { })} />
The coordinates covered by the container would simply be (x, x+width) and (y, y+height)
onPanResponderGrant get the location of the draggable:
onPanResponderGrant: (e) => {
this.originX = e.pageX - e.locationX;
this.originY = e.pageY - e.locationY;
}
onPanResponderMove you can do something like this:
onPanResponderMove: (e, gestureState) => {
const currentX0 = this.originX + gestureState.dx
const currentY0 = this.originY + gestureState.dy
}
The draggable now covers from currentX0 to currentX0 + width and from currentY0 to currentY0 + height...
You can use these values to check to see whether it overlaps with the target view (x, x+width) and (y, y+height)

Categories

Resources