Scroll to the center of a DIV element - Angular 7 - javascript

I want to scroll to the Y and X center of my element when a certain function is fired.
My HTML look like this (i want to scroll to the middle of #viewport):
</div>
<div #viewport class="document-view-port">
<div #viewBox></div>
</div>
Inside my TS i am importing the elements like this(here i try to find the y center):
#ViewChild('viewBox') viewBox: ElementRef;
#ViewChild('viewport') viewport: ElementRef;
The last methods that i try to extract this center point to scroll is:
zoomIn() {
const elementRect = this.viewport.nativeElement.getBoundingClientRect();
const absoluteElementTop = elementRect.top + window.pageYOffset;
const middle = absoluteElementTop - (elementRect.height / 2);
this.viewport.nativeElement.scrollTo(0, middle)
}
I just can't make it happen, any help would be appreciated
[edit]
The method scrollIntoView() don't do anything in my code. I want to find those cordinates and scroll without using any predefined function

You should use window.scrollTo(x, y) for achieving the scroll:
zoomIn() {
const elementRect = this.viewport.nativeElement.getBoundingClientRect();
const absoluteElementTop = elementRect.top + window.pageYOffset;
const middle = absoluteElementTop - (elementRect.height / 2);
window.scrollTo(0, middle); // have a window object reference in your component
}

Related

div follow mouse when scroll in javascript

I build a website with some sliders (Wordpress - Divi). I added some javascript to let the slider description follow the mousepointer when the mouse hovers over the slider. Works perfectly, BUT: when I scroll the mouse the pointer shifts downwards as expected, but the slider description does not shift accordingly; it remains where it was. So from that point on the link between the mouse and the description is gone.
Question: how can I enforce the description to follow the mouse when scrolling......
If you want to see the result until now, please check https://roel.famnabuurs.nl
Any help appreciated
The code as requested:
const sliderzelf = document.getElementById(slidernaam);
const titleblockslist = document.getElementById(slidernaam).getElementsByClassName('et_pb_slide_description');
for (let indslide of titleblockslist) {
indslide.style.setProperty('opacity',1, 'important');
var sliderOffset = sliderzelf.getBoundingClientRect();
let xpos = e.clientX - sliderOffset.left ;
// do not shift too far to the right
if (xpos > sliderzelf.clientWidth - indslide.clientWidth) {
xpos = sliderzelf.clientWidth - indslide.clientWidth;
}
if (xpos < 15) {
// do not shift too far to the left
xpos = 15;
}
indslide.style.left = (xpos) + 'px';
indslide.style.top = e.clientY + 'px';
}

Scroll to a specific point on a webpage given a percentage

I'm using the following function to calculate and save the percentage of how much of a web page I have scrolled:
const getPercentRead = () => {
const h = document.documentElement,
b = document.body,
st = "scrollTop",
sh = "scrollHeight";
return ((h[st] || b[st]) / ((h[sh] || b[sh]) - h.clientHeight)) * 100;
}
After refreshing the page, I want to use the output of this function and the current window height to scroll myself to the last saved location.
This is my current attempt at doing this:
const scrolled = (percentRead / windowHeight) * 100;
window.scrollTo(0, scrolled);
However, this does not take me to the correct scroll position. I'd appreciate any help you can give on this.
Thank you!
If you don't need to use percentages, this solution will work.
Save this value:
var x = window.pageYOffset;
Then scroll to it:
window.scrollTo(0, x);

Find rotated div top position from left

I want find top position of rotated div from element, I can able find top position of element but I want top(Y pos) position from left(x) position.
I am used this
var degree = degree;
if (degree < 0) {
var sign = -1;
} else {
var sign = 1;
}
var numY = Math.abs(myElem.position().top + sign * ((myElem.outerHeight() / 2) - Math.sin(degree)));
var numX = 0
var bottom = myElem.position().top + myElem.outerHeight(true);
y = numY;
Thanks in Advance
Slope:20 deg, height: 20px,width:400px, left 150px i want find top position
I want to re arrange dragged items after rotation for that I am finding top position.
Please find the jsbin link drop weights into plank.
I think it makes more sense to add the draggable image into the rotated div and to let everything rotate together, rather than worrying about the position of the draggable image. Here is a jsfiddle with your code updated (I only implemented dropping on the right side): http://jsfiddle.net/brendaz/17wwtffz/
drop:
// ...
var offset = ui.draggable.offset();
var rotateOffset = $('.rotatableAra').offset();
// Take the weight out of it's parent div and add it to the rotatable area
ui.draggable.remove();
ui.draggable.addClass("dropped");
ui.draggable.addClass("rightPlankDropped");
$('.rotatableAra').append(ui.draggable);
ui.draggable.css("top", ($('.rightPlank').position().top- ui.draggable.height()).toString() + "px");
ui.draggable.css("left", (offset.left - rotateOffset.left).toString() + "px");
rightArray[ind] = $textval * pos;
// ...

Zoom image to cursor breaks when mouse is moved

This is a followup question to How to zoom to mouse pointer while using my own mousewheel smoothscroll?
I am using css transforms to zoom an image to the mouse pointer. I am also using my own smooth scroll algorithm to interpolate and provide momentum to the mousewheel.
With Bali Balo's help in my previous question I have managed to get 90% of the way there.
You can now zoom the image all the way in to the mouse pointer while still having smooth scrolling as the following JSFiddle illustrates:
http://jsfiddle.net/qGGwx/7/
However, the functionality is broken when the mouse pointer is moved.
To further clarify, If I zoom in one notch on the mousewheel the image is zoomed around the correct position. This behavior continues for every notch I zoom in on the mousewheel, completely as intended. If however, after zooming part way in, I move the mouse to a different position, the functionality breaks and I have to zoom out completely in order to change the zoom position.
The intended behavior is for any changes in mouse position during the zooming process to be correctly reflected in the zoomed image.
The two main functions that control the current behavior are as follows:
self.container.on('mousewheel', function (e, delta) {
var offset = self.image.offset();
self.mouseLocation.x = (e.pageX - offset.left) / self.currentscale;
self.mouseLocation.y = (e.pageY - offset.top) / self.currentscale;
if (!self.running) {
self.running = true;
self.animateLoop();
}
self.delta = delta
self.smoothWheel(delta);
return false;
});
This function collects the current position of the mouse at the current scale of the zoomed image.
It then starts my smooth scroll algorithm which results in the next function being called for every interpolation:
zoom: function (scale) {
var self = this;
self.currentLocation.x += ((self.mouseLocation.x - self.currentLocation.x) / self.currentscale);
self.currentLocation.y += ((self.mouseLocation.y - self.currentLocation.y) / self.currentscale);
var compat = ['-moz-', '-webkit-', '-o-', '-ms-', ''];
var newCss = {};
for (var i = compat.length - 1; i; i--) {
newCss[compat[i] + 'transform'] = 'scale(' + scale + ')';
newCss[compat[i] + 'transform-origin'] = self.currentLocation.x + 'px ' + self.currentLocation.y + 'px';
}
self.image.css(newCss);
self.currentscale = scale;
},
This function takes the scale amount (1-10) and applies the css transforms, repositioning the image using transform-origin.
Although this works perfectly for a stationary mouse position chosen when the image is completely zoomed out; as stated above it breaks when the mouse cursor is moved after a partial zoom.
Huge thanks in advance to anyone who can help.
Actually, not too complicated. You just need to separate the mouse location updating logic from the zoom updating logic. Check out my fiddle:
http://jsfiddle.net/qGGwx/41/
All I have done here is add a 'mousemove' listener on the container, and put the self.mouseLocation updating logic in there. Since it is no longer required, I also took out the mouseLocation updating logic from the 'mousewheel' handler. The animation code stays the same, as does the decision of when to start/stop the animation loop.
here's the code:
self.container.on('mousewheel', function (e, delta) {
if (!self.running) {
self.running = true;
self.animateLoop();
}
self.delta = delta
self.smoothWheel(delta);
return false;
});
self.container.on('mousemove', function (e) {
var offset = self.image.offset();
self.mouseLocation.x = (e.pageX - offset.left) / self.currentscale;
self.mouseLocation.y = (e.pageY - offset.top) / self.currentscale;
});
Before you check this fiddle out; I should mention:
First of all, within your .zoom() method; you shouldn't divide by currentscale:
self.currentLocation.x += ((self.mouseLocation.x - self.currentLocation.x) / self.currentscale);
self.currentLocation.y += ((self.mouseLocation.y - self.currentLocation.y) / self.currentscale);
because; you already use that factor when calculating the mouseLocation inside the initmousewheel() method like this:
self.mouseLocation.x = (e.pageX - offset.left) / self.currentscale;
self.mouseLocation.y = (e.pageY - offset.top) / self.currentscale;
So instead; (in the .zoom() method), you should:
self.currentLocation.x += (self.mouseLocation.x - self.currentLocation.x);
self.currentLocation.y += (self.mouseLocation.y - self.currentLocation.y);
But (for example) a += b - a will always produce b so the code above equals to:
self.currentLocation.x = self.mouseLocation.x;
self.currentLocation.y = self.mouseLocation.y;
in short:
self.currentLocation = self.mouseLocation;
Then, it seems you don't even need self.currentLocation. (2 variables for the same value). So why not use mouseLocation variable in the line where you set the transform-origin instead and get rid of currentLocation variable?
newCss[compat[i] + 'transform-origin'] = self.mouseLocation.x + 'px ' + self.mouseLocation.y + 'px';
Secondly, you should include a mousemove event listener within the initmousewheel() method (just like other devs here suggest) but it should update the transform continuously, not just when the user wheels. Otherwise the tip of the pointer will never catch up while you're zooming out on "any" random point.
self.container.on('mousemove', function (e) {
var offset = self.image.offset();
self.mouseLocation.x = (e.pageX - offset.left) / self.currentscale;
self.mouseLocation.y = (e.pageY - offset.top) / self.currentscale;
self.zoom(self.currentscale);
});
So; you wouldn't need to calculate this anymore within the mousewheel event handler so, your initmousewheel() method would look like this:
initmousewheel: function () {
var self = this;
self.container.on('mousewheel', function (e, delta) {
if (!self.running) {
self.running = true;
self.animateLoop();
}
self.delta = delta;
self.smoothWheel(delta);
return false;
});
self.container.on('mousemove', function (e) {
var offset = self.image.offset();
self.mouseLocation.x = (e.pageX - offset.left) / self.currentscale;
self.mouseLocation.y = (e.pageY - offset.top) / self.currentscale;
self.zoom(self.currentscale); // <--- update transform origin dynamically
});
}
One Issue:
This solution works as expected but with a small issue. When the user moves the mouse in regular or fast speed; the mousemove event seems to miss the final position (tested in Chrome). So the zooming will be a little off the pointer location. Otherwise, when you move the mouse slowly, it gets the exact point. It should be easy to workaround this though.
Other Notes and Suggestions:
You have a duplicate property (prevscale).
I suggest you always use JSLint or JSHint (which is available on
jsFiddle too) to validate your code.
I highly suggest you to use closures (often refered to as Immediately Invoked Function Expression (IIFE)) to avoid the global scope when possible; and hide your internal/private properties and methods.
Add a mousemover method and call it in the init method:
mousemover: function() {
var self = this;
self.container.on('mousemove', function (e) {
var offset = self.image.offset();
self.mouseLocation.x = (e.pageX - offset.left) / self.currentscale;
self.mouseLocation.y = (e.pageY - offset.top) / self.currentscale;
self.zoom(self.currentscale);
});
},
Fiddle: http://jsfiddle.net/powtac/qGGwx/34/
Zoom point is not exactly right because of scaling of an image (0.9 in ratio). In fact mouse are pointing in particular point in container but we scale image. See this fiddle http://jsfiddle.net/qGGwx/99/ I add marker with position equal to transform-origin. As you can see if image size is equal to container size there is no issue. You need this scaling? Maybe you can add second container? In fiddle I also added condition in mousemove
if(self.running && self.currentscale>1 && self.currentscale != self.lastscale) return;
That is preventing from moving image during zooming but also create an issue. You can't change zooming point if zoom is still running.
Extending #jordancpaul's answer I have added a constant mouse_coord_weight which gets multiplied to delta of the mouse coordinates. This is aimed at making the zoom transition less responsive to the change in mouse coordinates. Check it out http://jsfiddle.net/7dWrw/
I have rewritten the onmousemove event hander as:
self.container.on('mousemove', function (e) {
var offset = self.image.offset();
console.log(offset);
var x = (e.pageX - offset.left) / self.currentscale,
y = (e.pageY - offset.top) / self.currentscale;
if(self.running) {
self.mouseLocation.x += (x - self.mouseLocation.x) * self.mouse_coord_weight;
self.mouseLocation.y += (y - self.mouseLocation.y) * self.mouse_coord_weight;
} else {
self.mouseLocation.x = x;
self.mouseLocation.y = y;
}
});

Javascript scrollIntoView() middle alignment?

Javascript .scrollIntoView(boolean) provide only two alignment option.
top
bottom
What if I want to scroll the view such that. I want to bring particular element somewhere in middle of the page?
try this :
document.getElementById('myID').scrollIntoView({
behavior: 'auto',
block: 'center',
inline: 'center'
});
refer here for more information and options : https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
It is possible to use getBoundingClientRect() to get all the information you need to achieve this. For example, you could do something like this:
const element = document.getElementById('middle');
const elementRect = element.getBoundingClientRect();
const absoluteElementTop = elementRect.top + window.pageYOffset;
const middle = absoluteElementTop - (window.innerHeight / 2);
window.scrollTo(0, middle);
Demo: http://jsfiddle.net/cxe73c22/
This solution is more efficient than walking up parent chain, as in the accepted answer, and doesn't involve polluting the global scope by extending prototype (generally considered bad practice in javascript).
The getBoundingClientRect() method is supported in all modern browsers.
Use window.scrollTo() for this. Get the top of the element you want to move to, and subtract one half the window height.
Demo: http://jsfiddle.net/ThinkingStiff/MJ69d/
Element.prototype.documentOffsetTop = function () {
return this.offsetTop + ( this.offsetParent ? this.offsetParent.documentOffsetTop() : 0 );
};
var top = document.getElementById( 'middle' ).documentOffsetTop() - ( window.innerHeight / 2 );
window.scrollTo( 0, top );
document.getElementById("id").scrollIntoView({block: "center"});
Scrolling to the middle of an element works well if its parent element has the css: overflow: scroll;
If it's a vertical list, you can use document.getElementById("id").scrollIntoView({block: "center"}); and it will scroll your selected element to the vertical middle of the parent element.
Cheers to Gregory R. and Hakuna for their good answers.
Further Reading:
https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
https://developer.mozilla.org/en-US/docs/Web/CSS/overflow
You can do it in two steps :
myElement.scrollIntoView(true);
var viewportH = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
window.scrollBy(0, -viewportH/2); // Adjust scrolling with a negative value here
You can add the height of the element if you want to center it globaly, and not center its top :
myElement.scrollIntoView(true);
var viewportH = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
window.scrollBy(0, (myElement.getBoundingClientRect().height-viewportH)/2);
With JQuery I use this:
function scrollToMiddle(id) {
var elem_position = $(id).offset().top;
var window_height = $(window).height();
var y = elem_position - window_height/2;
window.scrollTo(0,y);
}
Example:
<div id="elemento1">Contenido</div>
<script>
scrollToMiddle("#elemento1");
</script>
Improving the answer of #Rohan Orton to work for vertical and horizontal scroll.
The Element.getBoundingClientRect() method returns the size of an element and its position relative to the viewport.
var ele = $x("//a[.='Ask Question']");
console.log( ele );
scrollIntoView( ele[0] );
function scrollIntoView( element ) {
var innerHeight_Half = (window.innerHeight >> 1); // Int value
// = (window.innerHeight / 2); // Float value
console.log('innerHeight_Half : '+ innerHeight_Half);
var elementRect = element.getBoundingClientRect();
window.scrollBy( (elementRect.left >> 1), elementRect.top - innerHeight_Half);
}
Using Bitwise operator right shift to get int value after dividing.
console.log( 25 / 2 ); // 12.5
console.log( 25 >> 1 ); // 12
None of the solutions on this page work when a container other than the window/document is scrolled. The getBoundingClientRect approach fails with absolute positioned elements.
In that case we need to determine the scrollable parent first and scroll it instead of the window. Here is a solution that works in all current browser versions and should even work with IE8 and friends. The trick is to scroll the element to the top of the container, so that we know exactly where it is, and then subtract half of the screen's height.
function getScrollParent(element, includeHidden, documentObj) {
let style = getComputedStyle(element);
const excludeStaticParent = style.position === 'absolute';
const overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/;
if (style.position === 'fixed') {
return documentObj.body;
}
let parent = element.parentElement;
while (parent) {
style = getComputedStyle(parent);
if (excludeStaticParent && style.position === 'static') {
continue;
}
if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX)) {
return parent;
}
parent = parent.parentElement;
}
return documentObj.body;
}
function scrollIntoViewCentered(element, windowObj = window, documentObj = document) {
const parentElement = getScrollParent(element, false, documentObj);
const viewportHeight = windowObj.innerHeight || 0;
element.scrollIntoView(true);
parentElement.scrollTop = parentElement.scrollTop - viewportHeight / 2;
// some browsers (like FireFox) sometimes bounce back after scrolling
// re-apply before the user notices.
window.setTimeout(() => {
element.scrollIntoView(true);
parentElement.scrollTop = parentElement.scrollTop - viewportHeight / 2;
}, 0);
}
To support all options in scrollIntoViewOptions for all browsers it's better to use seamless-scroll-polyfill (https://www.npmjs.com/package/seamless-scroll-polyfill)
Worked for me.
Here is a link with explanation https://github.com/Financial-Times/polyfill-library/issues/657

Categories