Drag and Drop with resizing browser window (react-use-gesture) - javascript

I used hook useDrag from 'react-use-gesture'.
const drag = useDrag((params) => {},
{
bounds: {...bounds}
}
)
I use bounds in full clientWidth.
It works fine with constant bounds. But after resizing browser window (open devtools panel in my case) hook use previous offset value(I need to drag all width of resizing clientWidth).
How can I use useDrag with document.body.clientWidth?

I found solution. If we need dynamic bounds we can rewrite values in hook callback
const bind = useGesture({
onDrag: (params) => {
params._bounds[0] = [1, smth1];
if (params.offset[0] > smth2) {
params.lastOffset[0] = smth2
params.offset[0] = smth2
}
})

Related

Is there a way to watch screenX and screenY positions and have a method run when they change?

I am trying to run a method whenever the screenX or screenY position of a rendered window changes. I have set up a a default value for these positions in my data section here:
data() {
return {
initialX: window.screenX,
initialY: window.screenY
};
},
I have computed properties here:
currentPopupX() {
return window.screenX;
},
currentPopupY() {
return window.screenY;
}
},
Finally, I have a watch set up here:
watch: {
currentPopupX() {
if (this.currentPopupX !== this.initialX) {
movePopup(this.popup, this.currentPopupX, this.currentPopupY);
}
},
currentPopupY() {
if (this.currentPopupY !== this.initialY) {
movePopup(this.popup, this.currentPopupX, this.currentPopupY);
}
},
However the computed property seems to only return on initial render and does not update after that. Is there something I am missing?
I have tried comparing initial data to computed properties in the watch expecting for the method to be executed on change, however it never changes.
Note:
The rendered window is a popup notification. A user wants to drag that notification to a new location (currently it renders in the center of the screen) and have that popup render in the position they dragged it to the next time it is rendered. For additional context, I'm trying to grab the new positions to pass them along to an IPC event.
At my opinion, you have to use an interval to detect the browser position since there's no window move event.
if the browser position.x or the browser position.y change, you may dispatch a custom event.
The event here when you move the window will change the color of the text from black to red.
const event = new CustomEvent("browserMove",{ detail: "browserPosition" });
window.addEventListener("browserMove", onBrowserMove);
let moveTimer=null;
let content=null;
let oldX = 0;
let oldY = 0;
document.addEventListener("DOMContentLoaded",onReady);
function onReady(e){
content = document.getElementById("content");
oldX = window.screenX;
oldY = window.screenY;
moveTimer = setInterval(detectBrowserMove,200);
}
function detectBrowserMove(){
let r = browserPosition();
if(r.x !== oldX || r.y !== oldY){
// dispatch an event here
window.dispatchEvent(event);
oldX = window.screenX;
oldY = window.screenY;
}
content.innerHTML = ("browser.x = " + r.x + ", browser.y = " + r.y);
}
function browserPosition() {
let position={};
position = {x:window.screenX, y:window.screenY};
return(position);
}
function onBrowserMove(e){
// write your code here
let x = window.screenX;
let y = window.screenY;
content.style.color="#ff0000";
}
<div id="content">
</div>
If I catch your problem correctly, you are saying that-
A notification window will open in the center by default. The user can
drag it anywhere and when next time the notification window will appear, it should pop up at the position where the user last dragged it.
If we take the problem in some other way, you need the last dragged position of the window to send to the API for the next time opening. So, instead of checking the window's position every time why not check for only the last/latest position before it closes?
What I mean is-
Let the notification window open.
Attach a unload listener to it.
Drag it anywhere you want multiple times.
When the window is about to close, look into the listener, and grab the latest position.
Here is how you can do it in Vue-
Create a window data variable in your Vue and assign your newly opened window object to it-
data() {
return {
// Save notification window object to this data property
vue_window: null;
}
}
Apply a watcher that when vue_window has some value, set a listener to it-
watch: {
vue_window(newVal) {
// Only if vue_window variable has some value
if (newVal) {
// this will fire when the window is about to close
newVal.onunload = () => {
// Here are the latest screen positions of the window
console.log(this.vue_window.screenX, this.vue_window.screenY);
};
}
},
},
That's it. When the window will be about to close, you will have the last and latest position of the window which you can save wherever you want.
Here is the demo of this logic- CodeSandBox Link
I couldn't create a snippet because the window is not opening in the snippet environment. I will add the snippet if found any solution to work with the window obj.

Is there a way to resize the pip window without keeping the aspect ratio fixed?

I'm trying to modify Google's Picture in Picture Extension so I can resize the pip window without maintaining the fixed aspect ratio (stretch the sides individually). However, I'm not very familiar with Javascript.
Looking at the code, I expect the changes to be situated in these functions
async function requestPictureInPicture(video) {
await video.requestPictureInPicture();
video.setAttribute('__pip__', true);
video.addEventListener('leavepictureinpicture', event => {
video.removeAttribute('__pip__');
}, { once: true });
new ResizeObserver(maybeUpdatePictureInPictureVideo).observe(video);
}
function maybeUpdatePictureInPictureVideo(entries, observer) {
const observedVideo = entries[0].target;
if (!document.querySelector('[__pip__]')) {
observer.unobserve(observedVideo);
return;
}
const video = findLargestPlayingVideo();
if (video && !video.hasAttribute('__pip__')) {
observer.unobserve(observedVideo);
requestPictureInPicture(video);
}
}
Perhaps by tweaking the ResizeObserver?
the entirety of the source code can be found here. Is there a way to do this?

How to place the map popup at a fixed place next to some element using TypeScript and React?

I am new to using Google Maps API and I want to place the popup always at the fixed place next to the element on the map such that it is draggable with the map and is placed on the map using TypeScript and React.
what i am trying to do?
I have a popup that is shown when one of the buttons on the bottom of page is clicked. These buttons are called areas and they might have polygons related to them.
when user clicks on one of these buttons then a popup is displayed on the map. the position of this popup is such that,
if there is a polygon for the area button then it reads the coordinates for this polygon and positions the popup at the polygon coordinates. if there is no polygon and has address set then it sets the popup position at the address coordinates. if there is no polygon and no address then it sets the popup position to the center of the google map.
But now i want to place the popup always at a place next to the tools element such that the popup is 16px from tools element to the left and the popup is draggable with the map.
below is my code,
function useArea() {
const googleMap = useGoogleMaps();
const popUpRef = React.useRef<any>();
const [position, setPosition] = React.useState<google.maps.LatLng>();
const [mapPopups, setMapPopups] = React.useState<MapPopup[]>([]);
const navigateToPolygon = (area: area, address?: address) => {
const polygon = head(area.polygons);
const coords = polygon && head(polygon.coordinate);
let latLng = googleMap.googleMap.getCenter();
if (polygon && coords) {
latLng = new google.maps.LatLng(coords.latitude, coords.longitude);
} else if (address) {
latLng = new google.maps.LatLng(
address.coordinate.latitude,
address.coordinate.longitude
);
}
setPosition(latLng); //this is where the popup position is set
googleMap.googleMap.panTo(latLng);
googleMap.googleMap.panBy(0, 100);
};
const showPopup = React.useCallback(() => { //this is where the popup is created
if (popUpRef.current && pos) {
const popup = new MapPopup(position, popUpRef.current);
popup.setMap(googleMap.googleMap);
setMapPopups([popup]);
}
}, [position, popUpRef, googleMap]);
return {
popUpRef,
showPopUp,
navigateToPolygon,
}
}
function GoogleMapView() {
const area = useArea();
return (
<Tools/> //this is the element to which the popup should be positioned in relation to
<Popup
popUpRef={area.popUpRef}
showPopUp={area.showPopUp}
/>
<Areas area={area}/>
);
}
function Tools () { //this is a component containing some icons
return (
<Wrapper>
//some icons included
</Wrapper>
);
}
function Areas() {
const selectPolygon = React.useCallback() => {
area && area.navigateToPolygon(area, address);
},[area]
return (
{
areas.map(area => {
return(
<Area onSelect={selectPolygon}/>
);
}
}
);
}
function Area ({onSelect}: Props) { //individual button Area component
return (
<Wrapper onClick={onSelect}/> // this calls the selectPolygon which in turn // calls navigateToPolygon function
);
}
function PopUp ({showPopUp, popUpRef}: Props) {
useEffect(() => {
showPopup();
}, [showPopup]);
return (
<Wrapper ref={popUpRef}>
//some other divs included here
</Wrapper>
);
};
From the above code, it is seen that in the MapView the Tools, PopUp, Areas components are rendered. clicking on one of the area displays a popup. if the popup is present for the area then the popup is positioned based on polygon coordinates. if no polygon and has address, popup is positioned based on the address coordinates. if no polygon and address then the popup is positioned at the center of the google map.
now i want to modify this code such that the popup is always displayed at a fixed place (irrespective of polygon, address present or not) on the google map such that it is 16px away to the left from Tools component on the google map....
I have to modify setPosition in navigateToPolygon method. but i am not sure how to do it. could someone help me with this. thanks.
i am new to using or writing code in relation to google maps. could someone help me with this. thanks.
EDIT:strong text
below is the picture how the popup is shown currently for the polygon with above code,
how i wanted it to be?
what i tried to do?
i was thinking to get the wrapper div inside Tools component height and set that as top value for the popup. but this popup position is set using setPos(latlng) as seen in above code in navigateToPolygon component. what value should be set in this case?????
EDIT:
If not the complete solution could someone atleast provide some starting points as how it can be done. thanks

React Chartist component onTouchMove Events not firing

I tried asking this question before but the way I asked it was so confusing that I didn't get any help. I originally thought it was React to blame for my touchmove events to ceasefire when updating subcomponents. I now am pretty sure it is the Chartist.js library, or possibly how I'm wrapping chartist into a react component, that is stopping the action.
Instead of rambling on about my question I've created two JSfiddles. One that shows you can create a React slider that updates it's values continuously, regardless of being called from mousemove or touchmove.
http://jsfiddle.net/higginsrob/uf6keps2/
// please follow the link for full example
The Second fiddle implements my react wrapper for chartist, and a simplified example of how I'm using it. When you click/drag on the chart it will select the data point at the current x value. This is working fine with a mouse, but trying it on mobile touch devices (or chrome's mobile emulator) it will only fire a few times, and only update the chart once.
http://jsfiddle.net/higginsrob/Lpcg1c6w/
// please follow the link for full example
Any help is appreciated!
Ok, so you need to put a transparent div in front of the chartist chart that captures the mousedown/touchstart, mousemove/touchmove, and mouseup/touchend events.
working example:
http://jsfiddle.net/higginsrob/jwhbzgrb/
// updated event functions:
onTouchStart: function (evt) {
evt.preventDefault();
this.is_touch = (evt.touches);
var node = evt.currentTarget.previousSibling;
var grid = node.querySelector('.ct-grids');
var bbox = grid.getBBox();
this.columnwidth = bbox.width / this.props.data.length;
this.offset = this.getScrollLeftOffset(node) + bbox.x + (this.columnwidth / 2);
this.istouching = true;
this.onTouchMove(evt);
}
onTouchMove: function (evt) {
if(this.istouching){
var x;
if (this.is_touch) {
if(evt.touches && evt.touches[0]){
x = evt.touches[0].clientX - this.offset;
}
} else {
x = evt.clientX - this.offset;
}
this.setState({
index: Math.round(x / this.columnwidth)
});
}
}
onTouchEnd: function(evt){
this.istouching = false;
}
// updated render function:
render: function () {
return React.DOM.div(
{
style: {
position: "relative"
}
},
ReactChartist({ ... your chartist chart here .... }),
// this div sits in front of the chart and captures events
React.DOM.div({
onMouseDown: this.onTouchStart,
onTouchStart: this.onTouchStart,
onMouseMove: this.onTouchMove,
onTouchMove: this.onTouchMove,
onMouseUp: this.onTouchEnd,
onTouchEnd: this.onTouchEnd,
style: {
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0
}
})
);
}

clicking items inside a famo.us scrollview

I have a scrollview with a number of images.
events are working such that the scrollview can be dragged around.
i want to be able to click on a single image to get a detail view so used a:
surface.on 'click', => #parent.navTo('detail')
however, this has the effect that if i click an image when scrolling the above event will also fire.
i guess this is the whole reason mobile browsers have that 300ms delay - to tell if you're clicking or dragging. does Famous have other events to listen for?
In inputs/FastClick.js i only see 'touchstart' 'touchmove' and 'touchend'
do we have to track if a drag motion happened, in our own code, or does the famous engine assist with this?
FWIW i'm using a GenericSync to pipe the events around, and to make the view also work on the desktop.
constructor: (#parentView, #data) ->
SCROLLDIR = 1 # 0 horiz, 1 vertical
super({
direction: SCROLLDIR
paginated: true
pageStopSpeed: 5
})
#addContent()
#mouseSync = new famous.inputs.MouseSync({direction: SCROLLDIR})
#mouseSync.pipe(this)
addContent: () ->
comics = Comics.find()
surfaces = []
#sequenceFrom(surfaces)
for item in comics.fetch()
div = document.createElement('div')
comp = UI.renderWithData(Template.comicCover, item)
UI.insert(comp, div)
surface = new famous.core.Surface({
content: div
size: [undefined, 400]
properties:
backgroundColor: "#eff"
})
surfaces.push(surface)
surface.pipe(this)
surface.on 'click', => #parentView.navTo('comicPages')
# surface.pipe(#mouseSync)
return #surface
I have encountered a similar issue. To get around it, I simply track when the scrollview is scrolling and then ensure not scrolling on click.
Here is the code that I would add.. Hope it helps!
# In constructor
this.sync.on 'update', () => #scrolling = true
this.sync.on 'end', () => #scrolling = false
# In addContent
surface.on 'click', () =>
if !#scrolling
#parentView.navTo('comicPages')

Categories

Resources