I am trying to get the translateY.value into a state or some other way to control other component. This is inside a "bottomSheet" but in my Main where i import the bottomSheet i want to use the y value it is at while it is moving to control other components.
Using react-native-gestru-handler
const [tapNumber, setTapNumber] = useState(0);
const gesture = Gesture.Pan()
.onStart(() => {
animatedByValue.value = { y: translateY.value };
}).onUpdate((event) => {
translateY.value = event.translationY + animatedByValue.value.y;
translateY.value = Math.max(translateY.value, MAX_TRANSLATE_Y)
setTapNumber((value) => value + 1); // makes the app crash
}).onEnd(() => {
if (translateY.value > -SCREEN_HEIGHT / 4) { // snap close
scrollTo(-SCREEN_HEIGHT * 0.2);
} else if (translateY.value < -SCREEN_HEIGHT / 1.5) { // snap to top
scrollTo(MAX_TRANSLATE_Y);
}
});
I belive i need to add some kind of listner, but haven't had any luck doing so. Trying to set a state inside the onUpdate crashes the app with:
JNI DETECTED ERROR IN APPLICATION: JNI GetObjectRefType called with pending exception java.lang.RuntimeException: Tried to synchronously call function {bound dispatchSetState} from a different thread.
But works fine in debug mode.
If there is a simple way to get y value while inside "onUpdate" in Animated.event from react-native, please share a solution.
Didn't find a good solution. Switched to passing the Sharedvalue as a prop. Found out the Reanimated 2 works on the UI thread, and state is handled in the JS thread.
Related
I am trying to call an api base on scroll View current position but not sure how to I achieve that.
This is my code
<ScrollView
ref={scrollViewRef}
scrollEventThrottle={0}
onScroll={({nativeEvent}) => {
console.log(
nativeEvent.contentSize.height -
nativeEvent.layoutMeasurement.height,
);
console.log(nativeEvent.contentOffset);
}}>
I tried to call the api inside onScroll but that didnt work well.
Try adding an event listener at the particular scroll location you want the function to execute.
useEffect(() => {
Window.addEventListener(‘scroll’_.debounce(setScroll,1000));},[]);
I have solved the issue by using adding an if check. If the api data exist then the function wont execute anymore.
here's the code
const [apiData, setApiData] = useState();
onScroll={({nativeEvent}) => {
if (!apiData) {
if (
nativeEvent.contentSize.height -
nativeEvent.layoutMeasurement.height -
nativeEvent.contentOffset.y <
250
) {
getApiDataHandler();
}
}
}}
I'm building a sports app with Expo / React Native and trying to figure out a good way to track user location while the app is in the background. I have built a solution with expo-location (https://docs.expo.io/versions/latest/sdk/location/#locationstartlocationupdatesasynctaskname-options) which successfully receives location updates, and I even managed to send the location updates to the UI with the help of an EventEmitter.
Now the problem here is, the Expo Location task keeps the location updates deferred for a VERY long time (like 12 minutes) before sending a location update to the UI. This is despite setting all the relevant options to zero or very small.
I would love to use Expo Location because I got it mostly working, but unbelievably it seems that the library lacks an option/tool to force the background task to send updates often enough (like once in 5 seconds).
I would be grateful if anyone had a solution to really making the Expo background location send updates often enough. Now it sends updates "when I feel like it", about once in 5 or 12 minutes, despite setting all the relevant options and parameters I found in the documentation.
I already came to the conclusion that Expo background location is practically broken and I should switch to another location library (https://github.com/mauron85/react-native-background-geolocation). However I'm using Expo managed workflow and installing this mauron85 location library (otherwise really promising) doesn't work, because it requires setting manually dependencies --> I need to eject from Expo managed workflow --> ejecting breaks my project structure in a way I don't know how to solve. Really frustrating!
Relevant parts of my code:
top part:
import * as Location from 'expo-location';
import * as TaskManager from 'expo-task-manager';
import EventEmitter from 'EventEmitter'
const locationEmitter = new EventEmitter();
const BACKGROUND_LOCATION_TRACKER = 'BACKGROUND_LOCATION_TRACKER'
const LOCATION_UPDATE = 'LOCATION_UPDATE'
componentDidMount after requesting location permissions:
await Location.startLocationUpdatesAsync(BACKGROUND_LOCATION_TRACKER, {
accuracy: LocationAccuracy.BestForNavigation,
timeInterval: 0, // all set to 0 to make it update as often as possible!!! doesn't help
distanceInterval: 0,
deferredUpdatesInterval: 0,
deferredUpdatesDistance: 0,
showsBackgroundLocationIndicator: true,
foregroundService: {
notificationTitle: 'title',
notificationBody: 'recording',
notificationColor: '#008000',
},
// pausesUpdatesAutomatically: true,
});
locationEmitter.on(LOCATION_UPDATE, (locationData) => {
console.log('locationEmitter locationUpdate fired! locationData: ', locationData);
let coordinatesAmount = locationData.newRouteCoordinates.length - 1;
this.setState({
latitude: locationData.newRouteCoordinates[coordinatesAmount - 1].latitude,
longitude: locationData.newRouteCoordinates[coordinatesAmount - 1].longitude,
routeCoordinates: this.state.routeCoordinates.concat(locationData.newRouteCoordinates)
})
})
define location task:
TaskManager.defineTask(BACKGROUND_LOCATION_TRACKER, async ({ data, error }) => {
if (error) {
console.error(error);
return;
}
if (data) {
const { locations } = data;
console.log('backgroundLocationTracker received new locations: ', locations)
// const [location] = locations;
const locationsLength = locations.length;
const newRouteCoordinates = [];
// const totalNewDistance = 0.0;
for (i = 0; i < locationsLength; i++) {
const { latitude, longitude } = locations[i].coords;
const tempCoords = {
latitude,
longitude,
};
newRouteCoordinates.push(tempCoords);
// totalNewDistance += GLOBAL.screen1.calcDistance(newRouteCoordinates[i], newRouteCoordinates[i - 1])
};
console.log('backgroundLocationTracker: latitude ', locations[locationsLength - 1].coords.latitude,
', longitude: ', locations[locationsLength - 1].coords.longitude, ', routeCoordinates: ', newRouteCoordinates,
', prevLatLng: ', newRouteCoordinates[locationsLength - 1]);
let locationData = { newRouteCoordinates }
locationEmitter.emit(LOCATION_UPDATE, locationData)
}
});
As I said, it all WORKS (!) in the sense that I do get location updates from the background to the UI. The only problem here is that I can't figure out how to make the background task send location updates more often! It just keeps collecting a huge batch of 50+ location updates for even 10+ minutes before it bothers to send them to the UI!
All help appreciated, thanks.
I thought I would have to eject from expo too, but I changed my options and now it updates just as I intended.
The problem was the acuracy, I switched to "best for navigation" just as your example and it worked.
Here is my options, I think you should try to define some milisseconds, don't leave interval at 0 and don't set distance interval nor defered distance:
{accuracy: Location.Accuracy.BestForNavigation,
timeInterval: 1000,
showsBackgroundLocationIndicator: true,
foregroundService:{
notificationTitle: "Covid Tracker",
notificationBody: "Rastreando sua localização",
notificationColor: "#AA1111"
},
deferredUpdatesInterval: 100
}
I'm super new to Vue and I'm trying to get into components.
Basically, I have the main script file with my new vue app (with a countdown function based on setInterval and in the same file a vue component( it's a stamina bar made with canvas) with width increasing and decreasing methods. Basically, I would like to decrease the bar width every second but I don't get how to call the methods declared in the component. I guess that when they are declared inside the component I could access them from everywhere, but I didn't find a proper solution ( I tried with $refs but it's not working)
Is there any way where I can call the addWidth() and subWidth() function from a method in my new Vue app?
thank you
this is the component
Vue.component('stamina', {
template: '<div><canvas ref="stamina" style="height:25px;width:300px;" /></div>',
data(){
return {
stamina_width:300,
// vueCanvas: null
}
},
methods: {
drawStamina(){
// clear canvas
this.vueCanvas.clearRect(0, 0, 300, 50);
// draw rect
this.vueCanvas.rect(20,20, this.stamina_width, 100);
//colorize with gradient
var grd = this.vueCanvas.createLinearGradient(0, 0, 300, 0);
grd.addColorStop(0, "red");
grd.addColorStop(0.5, "orange");
grd.addColorStop(1, "green");
this.vueCanvas.fillStyle = grd;
//fill the rect with gradient
this.vueCanvas.fillRect(0, 10,this.stamina_width, 10);
},
addWidth() {
this.stamina_width += 20
this.drawStamina()
},
subWidth() {
this.stamina_width -= 20
this.drawStamina()
}
},
mounted () {
var ctx = this.$refs.stamina.getContext('2d');
this.vueCanvas = ctx;
var stam = this.stamina_width;
var grd = this.vueCanvas.createLinearGradient(0, 0, 300, 0);
grd.addColorStop(0, "red");
grd.addColorStop(0.5, "orange");
grd.addColorStop(1, "green");
this.vueCanvas.fillStyle = grd;
//fill the rect with gradient
this.vueCanvas.fillRect(0,25,stam,25);
}
});
and this is the main vue
var match = new Vue({
el:'#app',
methods:{
//call the methods from component stamina
}
yes you can call methods up/down of parent/child components. You may need to look at creating custom events and listen for them.
Another option I believe might be less complex is to consider using Vuex.
All of your addWidth and subWidth logic will reside in a centralized place this way. All of your components--no matter how nested can "subscribe" to these things (and modify them if needed).
Check out actions. You'll dispatch an action addWidth which sends it to Vuex. In your component that is responsible for rendering the stamina bar I would use a computed property to watch for updates. Something like this:
computed: {
barWidth() {
return this.$store.getters["bar/width"]; // 20
}
}
That is just a really rough example and will not likely be what you're looking for.
VueSchool has some nice (free) courses to help get you rolling with Vuex as well.
If you go here: http://ionicframework.com/docs/api/components/infinite-scroll/InfiniteScroll/
Inspect the demo and click the last item on the list:
Then in the console type: $0.scrollIntoView()
Infinite Scroll is never triggered.
Is there a way to programmatically trigger infinite-scroll in protractor context?
The implementation of the scroll in your example rely on the speed/velocity of the scroll which I guess falls far from the expected range when scrollIntoView is called.
One workaround is to simulates a smooth scroll by emitting multiple scroll events over a reasonable time. The idea is to reproduce as close as possible the behavior of a real user.
Some browsers already provides the option via scrollIntoView (supported by Chrome 62) :
$0.scrollIntoView({behavior: "smooth", block: "end"});
Using the accepted answer, in my case, I used ion-infinite-scroll as the argument.
Complete test to check if more content is loaded in Ionic:
describe('Scroll', () => {
it('should load more when reached end', async () => {
let list = getList();
let currentCount = await list.count();
const refresher = element(by.tagName('ion-infinite-scroll')).getWebElement();
let count = 0;
while(true){
browser.executeScript(`arguments[0].scrollIntoView({behavior: "smooth", block: "end"});`, refresher);
browser.sleep(1000); // wait for data to be loaded from api
list = getList();
let newCount = await list.count();
expect(newCount).toBeGreaterThanOrEqual(currentCount)
expect(newCount).toBeLessThanOrEqual(currentCount * 2)
if(newCount === currentCount){
break;
}
currentCount = newCount;
count++;
}
expect(count).toBeGreaterThan(0);
})
});
function getList() {
return element(by.className(pageId + ' list')).all(by.tagName('ion-item'));
}
It will have a callback function called repeatedly to when I start a download task. But It will be so slow that it can't give feedback when touch a button.
Fileio.downloadFile(downloadData[downloadFileIndex].uri, '1.jpg',this.progressFunc.bind(this)).then((DownloadResult)=> {
if (DownloadResult.statusCode == 200) {
let nextDownloadFileIndex = this.props.downloadFileIndex + 1;
this.props.dispatch(DOWNLOADED_A_FILE({
downloadFileIndex:nextDownloadFileIndex,
progressNum:0
}))
}
}).catch((error)=> {
console.log(error)
})
This is my code and the callback function are as be folllowed
progressFunc(DownloadBeginCallbackResult) {
let progressNum = DownloadBeginCallbackResult.bytesWritten / DownloadBeginCallbackResult.contentLength;
if(progressNum<=0.99){
this.props.dispatch(DOWNLOADING_A_FILE({
progressNum:progressNum,
jobId:this.props.jobId
}));
}else{
this.props.dispatch(DOWNLOADING_A_FILE({
progressNum:0,
jobId:this.props.jobId
}));
}
}
I mean I can't get feedback immediately when I touch button. I think that it is because I have a callback function called repeatedly. so js can't handle so many tasks;
It does sound like JS thread is busy doing the request and not able to communicate back to UI thread. One thing you can try is to wrap you on press handler in an InteractionManager.runAfterInteractions(() => ...)
See https://facebook.github.io/react-native/docs/interactionmanager.html