how to scroll to exact item after scrolling stop in react? - javascript

I make a simple demo of carousel. I am showing three cards at one time (two full cards and one small portion). When I click next and previous button it behave the same always by showing three cards except when user react to end there I am showing only two cards. This functionality is working fine, but I am facing one issue.
The issue is when I scroll I want snipped my item.
I have items like blue,red,green,yellow,blue.
Issue.when I scroll 5px or 10px it will snipped in other words if we scroll blue after stop scrolling scroll it should be show red,green,yellow.
Input If I scroll this part .
Expected output it should scroll to next slide
here is my code
https://codesandbox.io/s/youthful-greider-3k0md?file=/src/App.js
const onscroll = (e) => {
const ratio = Math.round(
parent.current.scrollLeft / slide.current.offsetWidth
);
console.log(ratio, "ratio");
const newIndex = ratio >= 1 ? ratio : 0;
setxx(newIndex);
setIndex(newIndex - 1);
};
any update .?

Related

How do I handle keyboard navigation to bring elements outside the visible bounds into view in dropdown?

I have a <ul> with a bunch of <li> options.
I wrote the following function that gets called when the user navigates my dropdown using the arrow keys:
function scrollIntoViewIfNeeded(target, reverse = false) {
const offsetTop = target.offsetTop;
const scrollHeight = document.getElementById('autocomplete-container').scrollHeight;
if (offsetTop > scrollHeight) {
target.scrollIntoView(false);
}
}
This works fine when moving down (pressing the down arrow). However, I am struggling to get this working when moving up (pressing the up arrow).
I was messing around with a reverse check but I am stuck. Any idea how to handle scrolling back up?

Fading in/out scroller animation

I have a slider that slides from right to left as the user scrolls down the page.
I want the slider to fade out when the last slide is out of view when the user scrolls down, and I want the slider to fade in when the last slide is in view when the user scrolls up.
An example of what I want can be seen here
I have the slider working fine but am having trouble with the fade out/fade in animation.
My idea was to first store an opacity value of 1 in a useRef
const countRefOpacity = useRef(1);
And add a wheel event to my window
useEffect(() => {
window.addEventListener("wheel", handleWheel);
return () => {
window.removeEventListener("wheel", handleWheel);
};
}, []);
My handleWheel function decreases my slider opacity when the user scrolls down a certain point and increases the opacity when the user scrolls up a certain point
const handleWheel = (e) => {
if (window.scrollY >= pan.current?.offsetTop * 2 && e.deltaY > 0) {
countRefOpacity.current = countRefOpacity.current - 0.007;
slider.current.style.opacity = countRefOpacity.current;
} else if (window.scrollY < pan.current?.offsetTop * 2 && e.deltaY < 0) {
countRefOpacity.current = countRefOpacity.current + 0.007;
slider.current.style.opacity = countRefOpacity.current;
}
};
I can see the fade out animation working when the user scrolls down, but the fade in animation is pretty slow when the user scrolls up.
How can I revise my code so I can make the fade in/fade out animation smooth and natural?
Codesandbox Link
If you want a CSS property to change smoothly, you can use the transition property, e.g. in your case
transition: opacity .5s;
So, assuming I understood your question correctly, I would probably use the transition property as well as add an event listener to the window's scroll event that checks whether the user has scrolled past a certain point and updates the opacity accordingly, i.e. sets it to 1 if it's before that point and to 0 otherwise. There's no need for you to calculate a delta since the transition property will handle the fade-in/fade-out for you.
Also, I believe you probably want to use scroll instead of wheel, since I think wheel only fires when you use a mouse wheel and doesn't apply to other types of scrolling.

Change element background on scroll, based on current section and performance optimized

The idea is to change the .wrapper background depending on the current section. Unfortunately, there are some limitations. I need to change it based on the current window.scrollTop and section.offsetTop and NOT on which section is in the viewport at the moment. (IntersectionObserver will not work)
It will be something like scrollSpy, but I am not able to loop over all of the sections in every scroll event because of performance reasons. Also, I am using a virtual scroll plugin for smooth scrolling, which makes even more calculations on scroll.
To the code I have till the moment:
My sections look like this:
<section class="intro"
data-bg="#ffffff"
data-color="#1A1919"
>
Section content goes here
</section>
I have an array of objects. In each object, I have the node element and its top.
const sections = [
{
el: section.intro, // Node
top: 1224 // Number
},
....
]
I can take the scrollTop from the plugin on scroll. So the code for now looks like:
const wrapper = document.querySelector('.wrapper');
const changeBg = (scrollTop, sections) => {
sections.map(({ el, top }) => {
const bg = el.getAttribute('data-bg');
const color = el.getAttribute('data-color');
if (top < scrollTop + window.innerHeight) {
console.log(bg);
wrapper.style.backgroundColor = bg;
wrapper.style.color = color;
}
});
};
myCustomPluginForScroll.on('scroll', e => {
const { y } = e.scroll;
changeBg(y, sections);
});
The problems are:
I loop all of the sections on every scroll event (which is multiplied by many from the plugin to make smooth scrolling) and it causes lag on scroll;
As I scroll more downwards, more and more sections become valid for the condition top < y + window.innerHeight which changes the wrapper background even more times per scroll.
I can't use debouncer to make number of calls of changeBg lower, because of the smooth scroll plugin.
To the question:
How can I make call of changeBg only once when new section is added/removed from the condition of top < y + window.innerHeight and take the bg/color attributes only on the last added/removed section.
Attaching screenshot with log of bg with 6 sections and scrolled to the bottom of the site. That way, on every single scroll I have 6 changes of wrapper's background.

need to adjust scroll left for the tabs to occupy in angularjs application

In my angularjs application I have approximately 23 tabs. So 90% of the width I have given for tabs with overflow : hidden, and 10% width Prev and Next icons. Each Tab has 36px width. So by default on the page 5 tabs are getting visible. Now when I click Next, I just want to scroll 5 tabs to left, like this continue till the last tab visible. So in my Next icon click event I am trying to do this some thing like the following, in my controller, to get the max scroll left and then use it but in my code maxScrollLeft is returning NaN.
_self.getNextTabs = function () {
var maxScrollLeft = $element.scrollWidth - $element.clientWidth;
alert(maxScrollLeft);
angular.element(document.querySelector('.main_tabSet .nav-tabs')).scrollLeft(600);
}
Here I have hard coded .scrollLeft(600); for testing purpose, so when I click Next, the tabs are scrolling left. But I want to make this scrolling to work correctly based on the max scroll left and the number of tabs that are being occupied. can any one help me in fixing it.
Not sure if _self.getNextTabs is in a directive, a controller or some other location, but the name $element indicates (to me) that it may be inside a directive?
If so, then I think $element may be a jquery object. AND - if that's the case, then you would need to use the syntax $element[0].scrollWidth et al...
_self.getNextTabs = function () {
var maxScrollLeft = $element[0].scrollWidth - $element[0].clientWidth;
angular.element(document.querySelector('.main_tabSet .nav-tabs')).scrollLeft(600);
}

jQuery: Move a DIV to left when its child element comes close to right edge of its parent element (type of auto-scrolling)

I'm currently building a somewhat basic video timeline. On this timeline are various media assets of variable duration and the width of the DIVs representing the assets reflect the duration, with 1 second being measured as 35px. So, for example, a 5 second asset would take up 175px in width on the timeline.
Because the timeline needs to be longer than the width of my usable space on the page, it needs to scroll horizontally. Instead of using an ugly standard scrollbar, I'm using a jQuery plugin scrollbar, which requires that the full-width DIV of the timeline sits inside another DIV that is the width of the usable area of the page and acts as a frame, with the inner DIV being absolutely positioned. When you move the scrollbar left or right it changes the "left" value of the inner DIV.
Having given that context, I now come to my problem. The assets on the timeline need be horizontally resizable to adjust their duration. I have that working using jQuery UI, but I need to make it so that when I drag the right edge of an asset near to the right edge of the outer DIV framing the timeline, the inner DIV of the timeline moves (basically scrolls) left and the width of the asset increases by 1 second (35px).
Even this last bit I have working to a certain degree, but not well enough. What I need is that when I drag far enough to the right, so that I'm within 35-70 pixels of the right edge of the framing DIV, the inner DIV timeline will move to the left, the width of the asset will increase, and this will keep happening until I move my mouse back towards the left.
The best example I can think of is like when you're selecting text in your browser and you drag past the bottom of the screen, the screen starts scrolling down and it keeps doing that till you move your mouse up.
Currently I'm trying to to this by drawing on the "resize" event of the jQuery UI resizable element, but the problem is that I can't get that continuous effect I was just talking about, I have to keep dragging my mouse further to the right rather than just keeping it still. And when I reach the right edge of the window I have to release the mouse button, move back over to the resizing handle and start dragging again.
Here's the function I was trying to write (FYI, a .mediaInstance is an asset on the timeline):
//Scroll Timeline when resized handle comes close to right edge
function timelineScroll() {
//console.log('running');
var mediaElement = $('#mediaTrack .mediaInstance.resizing');
var track = $('#horiz_container_inner');
//Determine location of right edge of the timeline viewport
var timeline = $('#horiz_container_outer');
var timelineOffset = timeline.offset();
var timelineLeft = timelineOffset.left;
var timelineRight = timelineLeft + $('#horiz_container_outer').width();
//Find right edge of current .mediaInstance
var instanceOffset = mediaElement.offset();
var instanceLeft = instanceOffset.left;
var instanceRight = instanceLeft + mediaElement.width();
if ( (timelineRight-instanceRight) < 35 ) {
var timelineCurrentLeft = Number(track.css('left').replace('px',''));
var timelineNewLeft = timelineCurrentLeft - 70;
track.css('left',timelineNewLeft);
mediaCurrentWidth = mediaElement.width();
mediaElement.width(mediaCurrentWidth+35);
if (currentMousePos.x > timelineRight) {
while (currentMousePos.x > timelineRight) {
var timelineCurrentLeft = Number(track.css('left').replace('px',''));
var timelineNewLeft = timelineCurrentLeft - 35;
track.css('left',timelineNewLeft);
mediaCurrentWidth = mediaElement.width();
mediaElement.width(mediaCurrentWidth+35);
}
}
}
}
You'll notice I even tried a loop at the end there based on the mouse position being farther right than the right edge of the framing DIV, but I didn't think it would work, and it didn't ... just seemed to put me in an infinite loop.
In any case, I'd really appreciate any help anyone can offer on this. I'm working on a project with a really short turnaround time and I've never really done any of this particular stuff before.
It turns out I solved my own problem. I just needed to use a setTimeout within the 'resize' function checking if the mouse was beyond the right edge of the framing DIV every 250 milliseconds and, if so, move the inner DIV left and increase the width of the asset. Here's what I used...
EDIT: It turns out my solution didn't work as I'd hoped, so I could use some help after all.
Here's the HTML:
<div id="horiz_container_outer">
<div id="horiz_container_inner" style="position: absolute; left: 0px; top: 0px; visibility: visible;">
<div id="horiz_container" style="width: 10500px;">
<div id="transitionTrack"></div>
<div id="mediaTrack" class="ui-sortable ui-droppable">
<div class="transitionBoxContainer first ui-droppable"></div>
<div class="mediaInstance" assetid="001" assettype="video" thumb="video1_thumb.jpg" style="display: block; width: 419px;" type="asset" duration="12">
<div class="mediaThumbnail" style="background-image: url(./images/assets/thumbnails/video1_thumb.jpg);"></div>
<div class="mediaInfo">
<div class="mediaFilename">video1.avi</div>
<div class="mediaDuration">12s</div>
<div class="mediaHandle"></div>
</div>
<div class="transitionBoxContainer ui-droppable"></div>
<div class="deleteButton"></div>
</div>
</div><!-- End of #mediaTrack -->
</div><!-- End of #horiz_container -->
</div><!-- End of #horiz_container_inner -->
</div><!-- End of #horiz_container_outer -->
And here's my code to make the mediaInstance on the timeline resizable, snapping to 35px increments:
//Watch timeline assets for click on resize handle
$("#mediaTrack").on('mousedown','.mediaInstance .mediaHandle', function(e) {
e.stopPropagation();
e.preventDefault();
//Find offset location of right edge of the timeline viewport
var timelineViewport = $('#horiz_container_outer');
var timelineViewportLeft = timelineViewport.offset().left;
var timelineViewportRight = timelineViewportLeft + timelineViewport.width();
//Assign track object to variable
var track = $('#horiz_container_inner');
var thisInstance = $(this).parents('.mediaInstance');
//Store the mouse position before we start moving
var startMousePos = e.pageX;
$(document).mousemove(function(e){
var currentInstanceWidth = thisInstance.width();
//Find right edge offset of current .mediaInstance
var instanceLeft = thisInstance.offset().left;
var instanceRight = instanceLeft + currentInstanceWidth;
if ( (e.pageX < (startMousePos-35)) || (e.pageX > (startMousePos+35)) ) {
if ( e.pageX < (startMousePos-35) ) {
thisInstance.width(currentInstanceWidth-35);
} else {
thisInstance.width(currentInstanceWidth+35);
}
startMousePos = e.pageX;
recalcDuration(thisInstance);
calcTotalDuration();
}
});
});
$(document).mouseup(function(e){
$(document).unbind('mousemove');
});
That works great for the actual resizing, but the problem I'm having is that when I move the mouse past the right edge of #horiz_container_outer, which acts as a frame for the timeline, I want the #horiz_container_inner DIV to start moving its left position to the left by increments of 35px while also continuing to resize the .mediaInstance div to make it 35px wider ... and I want both those things to happen every 0.25 seconds ... HOWEVER, I don't want the "scrolling" of #horiz_container_inner to continuously fire with the mousemoves. Once the mouse passes the right edge of #horiz_container_outer, I want some function to take over and start scrolling and resizing the .mediaInstance DIV at a set interval until the mouse once again moves left, past the right edge of #horiz_container_outer, at which point the original resizing shown above takes over again.
The problem is that I have no idea how to achieve this. I tried using a flag variable and conditional to tell me when my mouse is "in the zone", with inTheZone = false to begin with, running a conditional to run my initial code only when inTheZone == false, then setting it to true once the mouse enters the right area and having a setTimeout takeover to loop the scrolling and resizing. This worked to a certain degree, but the mouse position suddenly became unavailable so I couldn't tell when I moved outside the zone and the div just kept scrolling indefinitely.
Any ideas?

Categories

Resources