I'm working on a pen to move the body through the viewport and simulate pages outside of the window.
I have written a script that will detect the "right" css declaration and add or subtract 100% to simulate the viewport moving around the content. I would like to do this with the top using
var = vertical.
I'm not very good at Javascript, so please let me know if there are easier ways to work through my variables. After realizing that .css pulls a string, I had to use parseInt.
$('.right.arrow-link').click(function () {
var vertical = parseInt($('body').css('top'), 10);
var horizontal = parseInt($('body').css('right'), 10);
var newhorizontal = horizontal + 100;
var newhorizontal = newhorizontal.toString() + '%';
$('body').css('right', newhorizontal);
});
$('.left.arrow-link').click(function () {
var horizontal = parseInt($('body').css('right'), 10);
alert(horizontal);
var newhorizontal = horizontal - 100;
var newhorizontal = newhorizontal.toString() + '%';
$('body').css('right', newhorizontal);
});
Edit it Here:
Use CSS transitions, and just track horizontal and vertical numbers per click. You need the CSS transition on the .wrapper to make it all animate nicely.
.wrapper {
transition: all 0.3s ease-out;
-webkit-transition: all 0.3s ease-out;
}
However, you're redeclaring your vertical and horizontal variables on every click, which resets them, and will make it impossible to add more boxes and grids to the left and right of subsequent pages. This is causing stuff to fly around. [EDIT: added up and down as well] Try this:
$(document).ready(function() {
var vertical = 0;
var horizontal = 0;
$('.right.arrow-link').click(function(){
horizontal -= 100;
$('body').css('left', horizontal + '%');
});
$('.left.arrow-link').click(function(){
horizontal += 100;
$('body').css('left', horizontal + '%');
});
$('.up.arrow-link').click(function() {
vertical -=100;
$('body').css('top', vertical + '%');
});
$('.down.arrow-link').click(function() {
vertical +=100;
$('body').css('top', vertical + '%');
});
});
The .wrapper style with the transitions assumes that all of your extra pages will use that class. This will set up the CSS transition animation for all of them in one fell swoop.
Also, please note that in your CSS you've only specified -webkit-transform in a couple places, which makes this break in FireFox. You can fix that by just adding the transform version as well.
http://codepen.io/anon/pen/nsJDr
On you left arrow click, you don't want it to move 100% of the width of the screen, you just want it to move back to the start of the page. If there is going to be multiple sections, you will have to adjust this to move the width of the section your moving from though:
$('.left.arrow-link').click(function() {
var horizontal = parseInt($('body').css('right'), 10);
var newhorizontal = 0;
newhorizontal = newhorizontal.toString() + '%';
$('body').css('right', newhorizontal);
});
Here is a solution that keeps track of the wrapper and moves it the width of the current wrapper:
$(document).ready(function() {
var currentWrapper = 1;
$('.right.arrow-link').click(function() {
var vertical = parseInt($('body').css('top'), 10);
var horizontal = parseInt($('body').css('right'), 10);
var newhorizontal = horizontal + 100;
newhorizontal = newhorizontal.toString() + '%';
$('body').css('right', newhorizontal);
currentWrapper++;
});
$('.left.arrow-link').click(function() {
var horizontal = parseInt($('body').css('right'), 10);
var newhorizontal = horizontal - $(".wrapper.page-" + currentWrapper).width;
newhorizontal = newhorizontal.toString() + '%';
$('body').css('right', newhorizontal);
if(currentWrapper > 1)
currentWrapper--
});
});
Related
I have two DIV's of different widths on top of each other. The top DIV displayDIV is wider than the bottom DIV captureDIV.
In the displayDIV I'm drawing a dot who's X position is proportionate to the mouse position within captureDIV.
As you move the mouse in captureDIV the dot moves proportionately in DisplayDIV.
It makes much more sense if you look at this fiddle
My code is as follows...
let capture = document.getElementById('captureDIV');
let display = document.getElementById('displayDIV');
let circle = document.getElementById('circle');
capture.addEventListener('mousemove', handleMouseMove);
function handleMouseMove(event) {
const captureRect = capture.getBoundingClientRect();
const captureWidth = captureRect.right - captureRect.left;
const relativeX = event.x - captureRect.left;
let percent = (relativeX / captureWidth) * 100;
let roundedPercent = parseFloat(Math.round(percent * 100) / 100).toFixed(2);
moveDotTo(roundedPercent);
}
function moveDotTo(percentage) {
const displayRect = display.getBoundingClientRect();
const displayWidth = displayRect.right - displayRect.left;
const circleX = displayRect.left + displayWidth * (percentage / 100);
const circleY = displayRect.top + (displayRect.height / 2);
const style = `top:${circleY}px;left:${circleX}px;`;
circle.setAttribute('style', style);
}
I also have a number of buttons that can set the position of the dot within DisplayDIV such as...
let move20 = document.getElementById('move20');
move20.addEventListener('click', function(event) {
moveDotTo(20);
});
Using Vanilla JS not CSS tricks, how can I create a function to animate (rather than move) the dot from its existing position to the new position.
function animateDotTo(percentage) {
// clever code here
}
I need to be able to call the animateDotTo(percentage) function from either a button or from the mousemove event handler.
The dot should always animate to its new position regardless of how the move is triggered. For instance if the mouse is moved out of the left side of the captureDIV round the bottom and then into the right side of the captureDIV the dot should animate across the DisplayDIV not jump as it does now. Equally pressing one of the move to x% buttons should animate the dot from its current position to the new one.
If you are drawing a circle and moving it around, I would suggest drawing to a <canvas> element instead of moving a <div> by setting its top and left properties. Even using transform: translate(x, y) might be better.
In order to smoothly transition your dot from one location to another, using JavaScript, you will want:
The dot's current position as x and y coordinates,
The dot's target position as x and y coordinates, and
The speed at which the dot moves as a scalar.
Updating the current position is done at every animation frame with window.requestAnimationFrame. With these in hand, and a way of applying the resulting calculated position to the dot, you can use a method like this one: How to move an object using X and Y coordinates in JavaScript to move your dot (the example moves a canvas, but if you know the x and y, then you can set them to top and bottom).
Answering my own question, with thanks to Billy Brown for pointing me in the right direction. Using window.requestAnimationFrame is the way to go.
var currentPercentage;
var startPercentage;
var targetPercentage;
function animateDotTo(percentage) {
targetPercentage = percentage;
startPercentage = currentPercentage;
window.requestAnimationFrame(step);
}
function step(timestamp) {
var fps = 7;
var maxStep = 30;
var distStartToTarget = Math.abs(startPercentage - targetPercentage);
var stepSize = Math.min(distStartToTarget / fps, maxStep);
if (targetPercentage < startPercentage) {
currentPercentage -= stepSize,0;
if (currentPercentage > targetPercentage) {
window.requestAnimationFrame(step);
}
} else if (targetPercentage > startPercentage) {
currentPercentage += stepSize,100;
if (currentPercentage < targetPercentage) {
window.requestAnimationFrame(step);
}
} else {
return;
}
if (currentPercentage > 100 ) { currentPercentage = 100; }
if (currentPercentage < 0 ) { currentPercentage = 0; }
moveDotTo(currentPercentage);
}
Updated fiddle
A simple trick in css transition will fix this.
Of course. You don't want it to animate when you're actually moving the mouse. So what I did is that I separate the transition css property on another class and then remove that class on mouse move, re-attaching it when we click the move buttons.
CSS
#circle {
position: absolute;
left: -100px;
top: -100px;
width: 10px;
height: 10px;
border-radius: 50%;
background-color: #000;
transition: none;
}
#circle.animate{
transition: 500ms ease;
}
JS
move20.addEventListener('click', function(event) {
moveDotTo(20); animateDotTo();
});
move60.addEventListener('click', function(event) {
moveDotTo(60);animateDotTo();
});
move80.addEventListener('click', function(event) {
moveDotTo(80);animateDotTo();
});
function moveDotTo(percentage) {
circle.classList.remove("animate");
const displayRect = display.getBoundingClientRect();
const displayWidth = displayRect.right - displayRect.left;
const circleX = displayRect.left + displayWidth * (percentage / 100);
const circleY = displayRect.top + (displayRect.height / 2);
const style = `top:${circleY}px;left:${circleX}px;`;
circle.setAttribute('style', style);
}
function animateDotTo(percentage) {
circle.classList.add("animate");
}
http://jsfiddle.net/8pm2grjd/
If you want it to animate even if you're triggering the movement using mousemove, you can disregard the class approach and just slap the transition property on the css. But this will simulate the annoying mouse delay effect similar to input delay on video games due to V-Sync.
I would like to add left margin and right margin to the body to hide the width change when I hide the vertical scrollbar.
I have this code that finds the width of the vertical scrollbar:
var $outer = $('<div>').css({visibility: 'hidden', width: 100, overflow: 'scroll'}).appendTo('body'),
widthWithScroll = $('<div>').css({width: '100%'}).appendTo($outer).outerWidth();
$outer.remove();
var scrollbarwidth = 100 - widthWithScroll;
It gives the value "17" (in pixels) for IE11, Chrome 45, and Firefox 39 (desktop).
When I hide the vertical scrollbar, all elements, such as images, jump exactly 17 pixels to the right, which I want to hide.
I have tried:
document.body.style.marginRight = scrollbarwidth + "px";
$('body').css('margin-right', scrollbarwidth);
$(body).css("marginRight", scrollbarwidth + "px");
The last one might be faulty in some way, since other parts of the function stops working when it's enabled. The two others don't seem to work either, as I don't see any margin changes.
EDIT 1: For easier understanding of how I am going to use it, I wanted to mention that it's supposed to trigger on a on scroll function, like this:
var check1 = false;
$(document).bind('scroll', function() {
if(check1 === false && $(window).scrollTop() >= $('#divscrolltester').offset().top + $('#divscrolltester').outerHeight() - window.innerHeight) {
check1 = true;
unloadScrollBars();
disableScroll();
var $outer = $('<div>').css({visibility: 'hidden', width: 100, overflow: 'scroll'}).appendTo('body'),
widthWithScroll = $('<div>').css({width: '100%'}).appendTo($outer).outerWidth();
$outer.remove();
var scrollbarwidth = 100 - widthWithScroll;
//document.body.style.paddingRight = scrollbarwidth + "px"; Temporary disabled.
//$('body').css('padding-right', scrollbarwidth); Temporary disabled.
//$(body).css("marginRight", scrollbarwidth + "px"); Temporary disabled.
setTimeout(function() {
enableScroll();
reloadScrollBars();
//document.body.style.paddingLeft = scrollbarwidth + "px"; Temporary disabled.
//$('body').css('padding-left', scrollbarwidth); Temporary disabled.
//$(body).css("marginLeft", scrollbarwidth + "px"); Temporary disabled.
}, 500);
}
});
EDIT 2:
Here is a Fiddle to show most of the js, html and css: https://jsfiddle.net/tfnwj7dj/10/.
I haven't added the change of css through code yet, as I'm still trying to solve the issue. Also, the scrolling and scrollbar are supposed to be re-enabled in a second, but there seems to be an error in there somewhere, sorry.
EDIT 3:
For your information at this moment, these lines work:
document.body.style.paddingLeft = (scrollbarwidth) + "px";
$('body').css('padding-left', scrollbarwidth);
document.body.style.paddingRight = (scrollbarwidth) + "px";
$('body').css('padding-right', scrollbarwidth);
document.body.style.marginLeft = (scrollbarwidth) + "px";
$('body').css('margin-left', scrollbarwidth);
document.body.style.marginRight = (scrollbarwidth) + "px";
$('body').css('margin-right', scrollbarwidth);
Maybe you have enough information to solve it, if you have the same issue, but unfortunately, this wasn't enough for me. It might be important info to know that I have my content centered with a width / max-width of just 500px, and that I don't actually have a body class. Maybe on designs with width="100%", or elements with absolute positioning, the lines might be enough.
Both javascript and jquery solutions are welcomed.
EDIT 4:
I finally solved it for my own circumstances - feel free to read the answer below. It works for preventing elements to jump when hiding the vertical scrollbar, and with some tinkering, it could probably do for a body class, or other situations.
Is your scrollbarwidth integer? Try this
var scrollbarwidth = 100;
$('body').css('margin-right', scrollbarwidth);
Maybe you have wrong value at scrollbarwidth ? In my ff this code works.
I managed to solve it - I'd like to clarify that my css actually don't contain a body class, and that I just centered all elements with a width / max-width of 500px and margin-left/right auto.
For my and other, similar cases, here is the answer:
/* First 5 lines for finding the scrollbar width. */
var $outer = $('<div>').css({visibility: 'hidden', width: 100, overflow: 'scroll'}).appendTo('body'),
widthWithScroll = $('<div>').css({width: '100%'}).appendTo($outer).outerWidth();
$outer.remove();
var scrollbarwidth = 100 - widthWithScroll;
var scrollbarwidthadjustment = scrollbarwidth / 2; /* For centered elements, divide the scrollbar width by 2. */
var element = document.getElementById('element');
element.style.right = (scrollbarwidthadjustment) + "px";
And when you re-enable the vertical scrollbar, simply add:
element.style.right = "0px";
Also, the element must have a css position stated, otherwise it won't trigger. Here is an example of a css style that works:
.examplestyle {
color: white;
position: relative;
margin-left: auto;
margin-right: auto;
max-width: 100%;
display: block;
}
EDIT 1:
To prevent some unsightly css errors on mobile devices, add these lines:
/* ... */
var scrollbarwidthadjustment = scrollbarwidth / 2;
var windowWidth = $(window).width(); /* Get current window width on click/scroll etc. */
var window1 = windowWidth + scrollbarwidth; /* Window width + scrollbar width. */
var element = document.getElementById('element');
if(window1 >= widthofelement) {element.style.right = (scrollbarwidthadjustment) + "px";}
else {}
EDIT 2:
Fix for image resized smaller than its original size:
var offsetwidth = element.offsetWidth;
var widthadjustment = offsetwidth - scrollbarwidth; /* Get full width of image when scrollbar hidden, and then remove the scrollbar width. */
if(window1 < widthofelement && scrollbarwidth > 0) {
element.style.width = widthadjustment + "px";
element.style.right = (scrollbarwidthadjustment) + "px";
}
And then this code when showing the Y-scrollbar again:
if(window1 < widthofelement && scrollbarwidth > 0) {
element.style.width = "OriginalSizepx";
element.style.right = "0px";
}
If you want to use every edit that I have added, here is the full code:
/* First 5 lines for finding the scrollbar width. */
var $outer = $('<div>').css({visibility: 'hidden', width: 100, overflow: 'scroll'}).appendTo('body'),
widthWithScroll = $('<div>').css({width: '100%'}).appendTo($outer).outerWidth();
$outer.remove();
var scrollbarwidth = 100 - widthWithScroll;
var scrollbarwidthadjustment = scrollbarwidth / 2; /* For centered elements, divide the scrollbar width by 2. */
var element = document.getElementById('element'); /* Put element ID into a variable for easier use, and consecutive uses without re-identifying it. */
var window1 = windowWidth + scrollbarwidth; /* Window width + scrollbar width. */
var offsetwidth = element.offsetWidth; /* Get exact element size in current window. Shows shown dimensions when the window is resized. */
var widthadjustment = offsetwidth - scrollbarwidth; /* Get full width of image when scrollbar hidden, and then remove the scrollbar width. */
if(window1 >= widthofelement) {element.style.right = (scrollbarwidthadjustment) + "px";} /* If current window is equal to or greater than element width... */
if(window1 < widthofelement && scrollbarwidth > 0) { /* If current windows is smaller than the element width, and the window has a scrollbar greater than 0 pixels in width. */
element.style.width = widthadjustment + "px";
element.style.right = (scrollbarwidthadjustment) + "px";
}
/* When re-enabling the Y-scrollbar again; */
if(window1 >= widthofelement) {element.style.right = "0px";}
if(window1 < widthofelement && scrollbarwidth > 0) {
element.style.width = "OriginalSizepx";
element.style.right = "0px";
}
For further clarification, this code will prevent elements from jumping to the right when you hide the vertical scrollbar.
padding is your answer, as Shikkediel said. Just change margin to that and it'll work.
The items move because you change the default margin body has, so the whole body moves to the left (in case we are modifying margin-right).
If you remove the scroll bar, the default margin will go right behind it, and then you need to "buffer" the rest, left of the margin, and that's what padding does.
I really enjoy working with the Inspecting tool Chrome supplies (Ctrl + Shift + I) and then in the Styles tab on the right scorll down until you see the measurements. It really helps understand the CSS box model.
Did you add 'px' here..
$('body').css('margin-right', scrollbarwidth+'px')??
Just nowI tried in w3schools. If you add 'px' to above syntax, it is working for me.
Got a strange issue, my tag has a greater width than my monitor, which it shouldn't. I have some JavaScript which gets the scroll offset and adjusts my background, to give it a parallax effect, but as you can see, once the background gets given an 100% width, it snaps and stretches out. You can see this by zooming out of the page, the background is larger.
Here is the website
Any idea what is going wrong with it? Here is my JavaScript, and view the CSS by inspecting the element. It has also gone a bit slow as well to be honest, was working nice and smooth.
var ismobile = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
if (!ismobile){
window.onresize = function(event) {
//Detect window size and make new padding
if (window.innerWidth > 835) {
var newPadding = parseInt(window.innerHeight)/2.8;
newPadding = newPadding.toFixed(0);
var limitPadding = 221;
//Apply new padding value to header
if (newPadding > limitPadding) {
doc("header").style.padding = newPadding + "px 0px";
}
}
}
window.onscroll = function() {
var speed = 0.7;
var newPos = "100% " + (window.pageYOffset * speed) + "px";
document.body.style.backgroundPosition = newPos;
}
}
Add the overflow property to your body tag...
body {overflow-X: hidden;}
I am trying to add to the width of the element everytime the setInterval function is invoked
function setProgress(){
var bar = document.getElementById('progress-bar');
var width = 20;
bar.style.width += width + 'px';
//console.log(bar.style.width);
}
window.onload = function(){
setInterval(setProgress, 10);
}
I tried using parseInt(), but everytime I console.log() to the window i see the same width. My end goal is for the width to increase by 20
You need to remove px part from width style and then cast string to number before incrementing it:
function setProgress(){
var bar = document.getElementById('progress-bar');
var width = 20;
bar.style.width = Number(bar.style.width.replace('px', '')) + width + 'px';
//console.log(bar.style.width);
}
Make width a global var, like shown below:
var width = 0;
function setProgress(){
var bar = document.getElementById('progress-bar');
width+= 20;
bar.style.width += width + 'px';
//console.log(bar.style.width);
}
window.onload = function(){setInterval(setProgress, 10);}
Also, you should specify the max width to prevent the progress bar moving outside the working area (for example, modifying the increment line: if(width<500) {width+= 20;} else {return;}).
Alternatively, you can use your original solution by adding couple more statements, namely: removing the "px" unit from style property bar.style.width, then parsing it (converting to Number), then incrementing it and then adding "px" (otherwise, "+" operator will cause a concatenation of strings with output like: 20px20px, 20px20px20px, etc). Such alternative solution will slow down the process and put additional load on CPU (thus, it's not recommended).
Hope this may help. Best regards,
The problem is that width returns a string with units.
Instead, consider storing the number of pixels in a variable:
var bar = document.getElementById('progress-bar'),
width = parseFloat(getComputedStyle(bar).width);
setInterval(function() {
width += 20;
bar.style.width = width + 'px';
}, 10);
var bar = document.getElementById('progress-bar'),
width = parseFloat(getComputedStyle(bar).width);
setInterval(function() {
width += 20;
bar.style.width = width + 'px';
}, 200);
#progress-bar {
background: #0f0;
display: inline-block;
}
<div id='progress-bar'>Progress bar</div>
var width = 0;
function setProgress(){
var bar = document.getElementById('bar');
width+= 20;
bar.style.width = width + 'px';
console.log(bar.style.width);
if(width==200){
width=0;
}
}
window.onload = function(){
setInterval(setProgress, 1000);
}
I have 2 full height divs. When you scroll down the page the one div scrolls up and the other scrolls in an opposite direction. This works great.
I'm trying to keep this effect but put normal full width content underneath it whilst trying to maintain natural scrolling. So I'd like to keep the alternate scrolling effect but when I get to the bottom of the last div that uses this effect I would like to continue scrolling normally to see normal content underneath it.
Here's my jsFiddle, currently its floating over the effect I refer to: http://jsfiddle.net/u9apC/116/ and the JS is pasted below for reference:
(function ($) {
var top = 0;
$(document).ready(function () {
var contentHeight = $('.right').height(),
contents = $('.right > .content').length;
top = (0 - (contentHeight * (contents - 1)));
$('.right').css('top', top + 'px');
});
$(window).resize(function () {
var contentHeight = $('.right').height(),
contents = $('.right > .content').length;
top = (0 - (contentHeight * (contents - 1)));
$('.right').css('top', (top + $(window).scrollTop()) + 'px');
});
$(window).scroll(function () {
$('.right').css('top', (top + $(window).scrollTop()) + 'px');
});
})(jQuery);
EDIT
Here's a illustration of what I want:
I hope this is what you're after - it's a little hard to visualise from the description.
There are a couple of tricks to get this working:
Reverse the scroll direction when the right col top goes positive
Ensure the .row div has a top margin sufficient to push it down to the bottom of the left col.
(function ($) {
var top = 0;
var contentHeight = 0;
$(document).ready(function () {
calcContentHeight();
});
$(window).resize(function () {
calcContentHeight();
});
$(window).scroll(function () {
setRightTop();
});
function calcContentHeight() {
var contents = $('.right > .content').length - 1;
contentHeight = $('.right').height() * contents;
top = 0 - contentHeight;
setRightTop();
}
function setRightTop() {
var rightTop = top + $(window).scrollTop();
//1. don't allow right col top to go positive
if(rightTop > 0) rightTop = 0 - rightTop;
$('.right').css('top', rightTop + 'px');
//2. Ensure .row has sufficient top margin
$('.row').css('margin-top', contentHeight + 'px');
}
})(jQuery);
See updated JSFiddle here: http://jsfiddle.net/u9apC/126/
I've also refactored your code a little to reduce duplication.
You just need to make height of the div.body equal to total height of elements within it. Either by js or css.