Image move with mouse position - box issue? - javascript

I had originally taken some information from here and expanded on it: onextrapixel.com/examples/interactive-background/index4.html
I have instead incorporated the image to move with mouse position on the page, however there seems to be an issue with there being a top "box" that cuts off some of the hovered image. You can see it in action on a sample page here
My css:
.top-image {
background:url('http://i.imgur.com/wZRaMrB.png');
position:absolute ;
top:400px;
width:100%;
z-index:0;
height:100%;
background-repeat: no-repeat;
}
My js:
$(document).ready(function() {
var movementStrength = 25;
var height = movementStrength / $(window).height();
var width = movementStrength / $(window).width();
$("body").mousemove(function(e){
var pageX = e.pageX - ($(window).width() / 2);
var pageY = e.pageY - ($(window).height() / 2);
var newvalueX = width * pageX * -1 - 25;
var newvalueY = height * pageY * -1 - 50;
$('.top-image').css("background-position", newvalueX+"px "+newvalueY+"px");
});
});
I also hope to repeat this for the right side of the page.
After some suggesting in the comments here is the jsfiddle https://jsfiddle.net/yx1w8ysr/#&togetherjs=D4Q1xTfcaO

If you know the image's size beforehand, you can set the size of your div fixedly and don't need to use background-size:contain. Instead set it to some relative value (less than 100%) so that you have a padding around for the movement of the background image. However if you don't know the size of the image, you should use background-size:contain to ensure that your image sits right inside your div container. However with this approach we cannot control the size of the image anymore. That means you cannot use background-position to move the image around (because the size fits its parent, moving will cause the image be cut off).
So you need some another wrapper/container and move your inner div (.top-image) instead of changing the background-position.
Here is the detailed code:
var movementStrength = 25;
var w = $(window).width();
var h = $(window).height();
$(window).mousemove(function(e) {
var pageX = (e.pageX - w / 2) / w / 2;
var pageY = (e.pageY - h / 2) / h / 2;
var newvalueX = pageX * movementStrength;
var newvalueY = pageY * movementStrength;
$('.top-image').css({
left: newvalueX + 'px',
top: newvalueY + 'px'
});
});
.container {
padding: 25px;
width: 35%;
height: 35%;
position: absolute;
top: 400px;
}
.top-image {
background: url('http://i.imgur.com/wZRaMrB.png');
position: absolute;
background-size: contain;
width: 100%;
z-index: 0;
height: 100%;
background-repeat: no-repeat;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='container'>
<div class="top-image"></div>
</div>

Related

How to scroll an image over fixed content?

I'm building a Shopify site, and want an overlay image to cover the top content when you land on the website. Then when you scroll, this overlay image moves up and off the screen revealing the website.
I found some javascript that does this, but the main website scrolls with the overlay image. Is there a way to have it scroll on its own and not scroll the content behind?
Here's what I have so far:
var container = document.getElementById('overlay-container');
var windowHeight = window.innerHeight;
var windowWidth = window.innerWidth;
var scrollArea = 1000 - windowHeight;
var square1 = document.getElementsByClassName('overlay-background-image')[0];
// var square2 = document.getElementsByClassName('overlay-logo')[1];
// update position of square 1 and square 2 when scroll event fires.
window.addEventListener('scroll', function() {
var scrollTop = window.pageYOffset || window.scrollTop;
var scrollPercent = scrollTop / scrollArea || 0;
square1.style.bottom = -100 * (1 - scrollPercent * 1.5) + 'vh';
// square2.style.top = 800 - scrollPercent*window.innerHeight*0.6 + 'px';
});
// Global variable to control the scrolling behavior
const step = 30; // For each 30px, change an image
function trackScrollPosition() {
const y = window.scrollY;
const label = Math.min(Math.floor(y / 30) + 1, 20);
const imageToUse = fruitImages[label];
// Change the background image
$('.image-container').css('background-image', `url('${imageToUse}')`);
}
$(document).ready(() => {
$(window).scroll(() => {
trackScrollPosition();
})
})
.overlay-container {
position: relative;
max-width: 100%;
}
.overlay-background-image {
position: absolute;
z-index: 5;
left: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<div class="overlay-container">
<div class="overlay-background-image">
<img style="height: 100vh;" src="https://cdn.shopify.com/s/files/1/0608/2225/7886/files/01_-_Landing_Page.png?v=1636020266" />
</div>
</div>
Edit:
I misread the question.
Original answer:
This will automatically scroll the image offscreen once the page is loaded. (Not what the question wanted)
I'd recommend using css rather than JavaScript for this.
body {
margin: 0px;
}
.overlay-background-image {
animation-fill-mode: forwards;
animation-name: shop_reveal;
animation-duration: 3s;
position: absolute;
width: 100vw;
background-size: calc(100vh * 1440/767);
height: 100vh;
background-image: url('https://cdn.shopify.com/s/files/1/0608/2225/7886/files/01_-_Landing_Page.png?v=1636020266');
}
#keyframes shop_reveal {
from {
bottom: 0px
}
to {
bottom: 100vh
}
}
<div class="overlay-container">
<div class="overlay-background-image">
</div>
</div>
Edited answer:
This will scroll the image along with the scrollbar.
<div class="overlay-container">
<div class="overlay-background-image">
</div>
</div>```
<!-- end snippet -->
pagebox is the content that will be revealed when the user scrolls the page.
<!-- language: lang-css -->
```#pagebox {
position: absolute;
}```
<!-- end snippet -->
Give pagebox absolute positioning so that it's position can be set in JavaScript and it doesn't push the image.
<!-- language: lang-javascript -->
```var container = document.getElementById('overlay-container');
var windowHeight = window.innerHeight;
var windowWidth = window.innerWidth;
var scrollArea = 1000 - windowHeight;
var square1 = document.getElementsByClassName('overlay-background-image')[0];
// var square2 = document.getElementsByClassName('overlay-logo')[1];
var p = document.getElementById("pagebox");
// update position of square 1 and square 2 when scroll event fires.
window.addEventListener('scroll', function() {
var scrollTop = window.pageYOffset || window.scrollTop;
var scrollPercent = scrollTop / scrollArea || 0;
square1.style.bottom = p.style.marginTop = -100 * (1 - scrollPercent * 1.5) + 'vh';
// square2.style.top = 800 - scrollPercent*window.innerHeight*0.6 + 'px';
});```
<!-- end snippet -->
As the page is scrolled. The image overlay is moved up by this code. However the normal functioning of the scrolling is still in effect so the rest of the page moves up with it. In order to counteract this. The position of the content underneath the image is moved down by the amount the page is scrolled.
(simply using position:sticky would prevent the content underneath the page from exceeding the height of the window so can't be used)

Calculating child div top position - why does parent offsetHeight need to be subtracted?

I have created a magnifier in pure js. What I discovered in needing to translate the mouse position of a div relative to its parents is that in calculating the top for the overlaying magnifier div, the offsetTop works differently than the offsetLeft. After adjusting for what should be the top, I need to subtract the whole container div's offsetHeight.
The line in the code in question is this:
magnifier.style.top = yPosition - container.offsetHeight + "px";
Why do I need to subtract container.offsetHeight?
I know I've read something regarding this, but can't find it.
Disclaimers This code is working. I am asking so I (and those following) can understand how the box model works.
I know there are jQuery alternatives that are more cross browser reliable. I like to code it myself so that I can learn how it all works. If you see something which is not compatible for a modern browser, feel free to comment.
Lastly, For anyone using this, I removed code from this example to adjust for transforms. For example, if the wrapper has a transform: translate(-50%, 0); to center the wrapper horizontally, you will need to add the resulting amount of the translation (which translates to the wrapper's left position) back into the calculation.
I have created a jsfiddle here. I left more comments in the Fiddle as to methodology if anyone is interested.
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="../css/ms.js"></script>
<style type="text/css">
/********************/
body {
background-color: #FFF;
margin-left: 30px;
margin-top: 10px;
}
.wrapper {
position: absolute;
left: 100px;
}
#container {
width: 527px;
height: 450px;
border: 5px black solid;
overflow: hidden;
background-color: #F2F2F2;
cursor: pointer;
}
#image {
width: 527px;
height: 450px;
}
#magnifier {
width: 250px;
height: 250px;
overflow:hidden;
position:relative;
z-index: 1000;
border: solid 1px;
}
#magnifier img {
position: absolute;
}
</style>
</head>
<body>
<div class="wrapper" id="wrapper">
<div id="container">
<img id="image" src="../docs/grade-2/jpg/g2-bb-saints-francis.jpg">
<div id="magnifier" class="magnifier">
<img id="imagecopy">
</div>
<br>
</div>
<input type="button" value="Zoom" onClick="initmagnifier('magnifier', 'image', 'imagecopy');"><br>
</div>
<script>
function initmagnifier(magnifier, image, imagecopy){
var magnifier = document.getElementById("magnifier");
var container = document.getElementById("container");
var wrapper = document.getElementById("wrapper");
var img = document.getElementById(image);
var imgcopy = document.getElementById(imagecopy);
var zoom = 2;
container.addEventListener("mousemove",
function(e){
movemagnifier(e, img, imgcopy, magnifier, container, wrapper, zoom)
}, false);
var src = img.src;
imgcopy.src = src;
var src2 = imgcopy.src;
imgcopy.height = img.height * zoom;
imgcopy.width = img.width * zoom ;
}
function movemagnifier(e, img, imgcopy, magnifier, container, wrapper, zoom) {
// to get the left & top of the magnifier
// position needs to be adjusted for WRAPPER & CONTAINER top and left
// gets the top and left of the container
var containerPosition = getPosition(e.currentTarget);
// adjust out the CONTAINER's top / left
// Then takes 1/2 the hight of the MAGNIFIER and subtracts it from the MOUSE position to center MAGNIFIER around the MOUSE cursor
var xPosition = e.clientX - containerPosition.x - (magnifier.clientWidth / 2);
var yPosition = e.clientY - containerPosition.y - (magnifier.clientHeight / 2);
magnifier.style.left = xPosition + "px";
magnifier.style.top = yPosition - container.offsetHeight + "px";
// Adjust for zoom
// adjust the MAGNIFIER's top/left at an equal pace to the zoom amount
var yTravel = (e.clientY - containerPosition.y ) * (zoom - 1);
var yimgPosition = -(yPosition - container.clientTop + yTravel);
imgcopy.style.top = yimgPosition + "px";
var xTravel = (e.clientX - containerPosition.x) * (zoom - 1); // * 1.5
var ximgPosition = -(xPosition + xTravel);
imgcopy.style.left = ximgPosition + "px";
console.log('****');
console.log(e.clientY); // MOUSE POSTION
console.log(containerPosition.y);
console.log(wrapper.offsetTop);
console.log(wrapper.clientHeight);
console.log(container.offsetTop);
console.log(container.clientHeight);
console.log(yPosition);
console.log(container.offsetHeight);
console.log(magnifier.style.top);
}
function getPosition(element) {
var xPosition = 0;
var yPosition = 0;
// element is the CONTAINER
// This calculates the postion of the element (CONTAINER) TOP & LEFT relative to ALL parents
while (element) {
// if transform: translate in place for x and y,
// add it back as it skews the offsetLeft offsetTop values by the translate amount
xPosition += ((element.offsetLeft) - element.scrollLeft);
yPosition += ((element.offsetTop) - element.scrollTop);
element = element.offsetParent;
}
return { x: xPosition, y: yPosition };
}
</script>
</body>
</html>
It took me longer than I care to admit, but I have found the reason. In your fiddle you position the #magnifier-element relative, which means you have to move it from its 'natural' position, which is below the image inside the container.
So with every move you have to compensate for this, by pulling the #magnifier to the top/left position of the container, the left position already matches, but the 'natural' top position of the #magnifier is the full height of the container, as you calculate from the top/left position of the #container, you need to subtract the #container height.
A simple fix is to add position: relative to the #container and change position: relative on the #magnifier to position: absolute.
This will give you the expected coordinate system for the #magnifier as top: 0; left: 0 for the absolute positioned element is the top left corner of the its relative parent (the first positioned parent element, in this case #container).
a working example without the need to to subtract container.offsetHeight.
While I'm at it, you may want to look into the Element.getBoundingClientRect function, as you can get all information you need to determine the position in a single call.

Custom scroll bar flows out of screen

I'm trying to make my own scroll bar, and so far it's working fine, for this small exception.
When I reach the bottom of the page, the bar handle goes under the viewport.
Gif of what's happening:
I know it has to do with the CSS, but I'm unsure on how to set it correctly. Foundation's .off-canvas-content has a class added named .full-height, and the height property is added so that the scroll bar won't be tied to that element.
The scroll bar markup is added to div.content, which is where all the remaining content will be.
I'm trying to get the handle bar to stop at the bottom of the container, when the user has scrolled all the way of the bottom of the document, but haven't found a way to do this correctly.
CSS:
.scroll-container {
position: fixed;
right: 50px;
top: 0;
height: 100%;
width: 7.5px;
background-color: rgba(55,55,55,.3);
}
.scroll-bar {
position: relative;
top: 0;
height: 20%;
width: 100%;
background-color: #6A1B9A;
}
.full-height {
height: 100vh;
min-height: 100vh;
}
.content {
float: left;
width: 100%;
height: 100%;
display: block;
padding: 10px 20px;
overflow-y: scroll;
}
JS:
(function($) {
$.fn.scroller = function() {
var self = this,
scrollBarDrag = false,
docHeight = $(document).height();
var scrollContainer = document.createElement('div'),
scrollBar = document.createElement('div');
scrollContainer.className = 'scroll-container';
scrollBar.className = 'scroll-bar';
scrollContainer.appendChild(scrollBar);
self[0].appendChild(scrollContainer);
self.on('scroll', function() {
var top = $(this).scrollTop();
setScrollBarTop(top);
});
function setScrollBarTop (top) {
scrollBar.style.top = top + 'px';
}
};
})(jQuery);
I tried using plugins for this, but they don't simulate the scroll bar as intended (missing mouse wheel click and drag to scroll), so I decided to make my own, lightweight version of it. Any suggestions about using plugins, albeit appreciated, will be disregarded and not accepted as an answer.
With absolute positioning:
I think you forgot to account for the scrollbar's height. Lets say the scrollbar is 100px tall and your page is 500px tall, you are only able to move the scrollbar by 400px, not all 500.
Find out the difference between your scrollbar height and the document height, find the ratio of how they compare, and apply that to your new scrollbar position.
havent tested it, but something like;
var heightToWorkWith = docHeight - scrollBarHeight;
var ratio = heightToWorkWith / docHeight;
function setScrollBarTop (top) {
scrollBar.style.top = (top * ratio) + 'px';
}
Have found a solution regarding this, was quite a bit of trial and error, but managed to find it in the end. Hope it can be of use to some of you.
Edited it to a more revised version.
self.on('scroll', function() {
elHeight = self.height();
docHeight = $(document).height();
var sTop = self[0].scrollTop;
var sHeight = self[0].scrollHeight;
var sBHeight = $(scrollBar).height();
var ratio = (elHeight - $(scrollBar).height()) / elHeight;
var currentPosY = (sTop / (sHeight - docHeight)) * 100;
scrollBar.style.top = (currentPosY * ratio) + '%';
});
You can get scroll ratio by doing this:
(thumbHeight / containerHeight) + 1
containerHeight is not the scroll area height, but the actual overflow: hidden container.
When you get the scrollTop value just multiply it with your ratio. Like this:
thumbPosition.top = el.scrollTop * ratio + 'px';

mousemove parallax only move slightly instead of mouse position

I'm wanting the plane and rocket to only move approx 5% from their original place when the mouse hits the hero-panel area.
this current code makes both images follow and offset where the mouse position is.
Please assist.
$(document).ready(function () {
$('#hero-panel').mousemove(function (e) {
parallax(e, document.getElementById('plane'), 1);
parallax(e, document.getElementById('rocket'), 2);
});
});
function parallax(e, target, layer) {
var layer_coeff = 10 / layer;
var x = ($(window).width() - target.offsetWidth) / 4 - (e.pageX - ($(window).width() / 4)) / layer_coeff;
var y = ($(window).height() - target.offsetHeight) / 4 - (e.pageY - ($(window).height() / 4)) / layer_coeff;
$(target).offset({ top: y ,left : x });
};
https://jsfiddle.net/jc0807/c5yke2on/
Thanks
Ok, I think I understand what you're looking for:
fiddle
$(document).ready(function () {
var plane = document.getElementById('plane');
var rocket = document.getElementById('rocket');
plane.homePos = { x: plane.offsetLeft, y: plane.offsetTop };
rocket.homePos = { x: rocket.offsetLeft, y: rocket.offsetTop };
$('#hero-panel').mousemove(function (e) {
parallax(e, document.getElementById('plane'), 10);
parallax(e, document.getElementById('rocket'), 20);
});
});
function parallax(e, target, layer) {
var x = target.homePos.x - (e.pageX - target.homePos.x) / layer;
var y = target.homePos.y - (e.pageY - target.homePos.y) / layer;
$(target).offset({ top: y ,left : x });
};
What we're doing here is recording the starting position of the plane and the rocket as a new property 'homePos' on the plane and rocket objects. This makes it easy to apply the parallax effect as an offset from the original positions based on the mouse distance from the object homePos.
If you modify the layer value passed to parallax, the amount of movement will change (we're dividing the mouse offset from the middle of the object's starting position by it, to calculate the new object offset amount).
I guess my question is somehow related to the one above and I didn't want to create a duplicate.
I am using the code bellow to "navigate" into an image on mousemove. The problem is that I cannot manage to make the image fill all the viewable screen area. I've added a red background to the container to show what I mean. The desired result would be to no red background visible.
HTML
<div class="m-scene" id="main">
<div class="scene_element">
<img class="body" src="http://www.planwallpaper.com/static/images/nature-background-images2.jpg" />
</div>
</div>
JS
$(document).ready(function() {
$('img.body').mousemove(function(e) {
parallax(e, this, 1);
});
});
function parallax(e, target, layer) {
var layer_coeff = 20 / layer;
var x = ($(window).width() - target.offsetWidth) / 2 - (e.pageX - ($(window).width() / 2)) / layer_coeff;
var y = ($(window).height() - target.offsetHeight) / 2 - (e.pageY - ($(window).height() / 2)) / layer_coeff;
$(target).offset({
top: y,
left: x
});
};
CSS
.m-scene {
background-color: red;
overflow: hidden;
width: 100%;
}
.scene_element {
margin-left: auto;
margin-right: auto;
overflow: hidden;
}
.scene_element img {
width: 100%;
height: auto;
margin-left: auto;
margin-right: auto;
}
My jsFiddle is: fiddle
Thank you!

fullscreen & centred background image script

I have made a simple function that adapts the size of an image according to the size of the window. I can't determine when exactly, but sometimes the img does not fill the width of the screen, but continues to stick to the height. Any idea why this could be?
If i console log (iRatio <= wRatio) anything seems to fit, but the shown result is incorrect.
The img is set as postion: absolute; with: 100%; top:0; left:0; in the css.
$win contains $(window) and $img the background image
function autoImageSize($img, $win){
var wHeight = $win.height(),
wWidth = $win.width(),
iHeight = $img.height(),
iWidth = $img.width(),
iRatio = iWidth / iHeight,
wRatio = wWidth / wHeight;
if(iRatio <= wRatio){
$img.css({
width: "100%",
height: "auto",
top: "-" + ((iHeight - wHeight)/2) + "px",
left: 0
});
}else{
$img.css({
width: "auto",
height: "100%",
top: 0,
left: "-" + ((iWidth - wWidth)/2) + "px"
});
}
return [$img.width(), $img.height()];
};
the problem was:
left: "-" + ((iWidth - wWidth)/2) + "px"
and
top: "-" + ((iHeight - wHeight)/2) + "px"
this is a stupid way to do a negation, sometimes the result was --somenumber px. i fixed the problem by doing this operation only wen the iHeight is smaller then the wHeight oder the iWidth is smaller then wWidth and by calculating the negation with a multiplication by -1.
When you are setting the image's width to 100% it will fill up to its parent's width. As is the case with the height of 100%, but there the parent also needs a fixed height in pixels (as far as I know).
Instead of setting the height to 100% you should calculate the width matching the height of the window using the image's ratio:
var toWidth = $win.height() * iRatio;
$img.width(toWidth);

Categories