Limiting an element from appearing out of the screen - javascript

I'm trying to make a square appear at random positions of the screen. I have set it's position property to be absolute and in javascript i'm running a random number between 0 to 100, this will then be assigned as a percentage to top and left property. however if the random number was ~100 or a bit less the square will appear out of the screen. How do I fix this problem?
var shape1 = document.getElementById("shape1");
//creating random number to 100
function randomNum() {
var r = Math.random();
var y = (r * (100 - 0 + 1)) + 0;
var x = Math.floor(y);
console.log(x);
return x;
}
//reappear at random position
function reappear(object) {
object.style.left = randomNum().toString() + "%";
object.style.top = randomNum().toString() + "%";
object.style.display = "block";
}
reappear(shape1);
code: https://jsfiddle.net/y3m4ygow/1/

You can call the getBoundingClientRect method (MDN reference) on the object and check to see if its bottom property is bigger than window.innerHeight (means it's falling outside the window height), or if its right property is bigger than window.innerWidth (means it's falling outside the window width), and if so, call the reappear function again:
function reappear(object) {
object.style.left = randomNum().toString() + "%";
object.style.top = randomNum().toString() + "%";
object.style.display = "block";
var rect = object.getBoundingClientRect();
if(rect.right > window.innerWidth || rect.bottom > window.innerHeight)
reappear(object);
}
Fiddle update: https://jsfiddle.net/y3m4ygow/2/

As you can see what's happening here is sometimes the object falling out of the document because (the width or height of it + the randomized percentage) is more than document width or height.
For example, say that document width is 1000px and the random number turned out to be 90% (=900px), since the box width is 200px, so you will have 100px out of the screen.
Now you have two solutions:
First: As #Sidd noted, you can check whether the box is in or out using getBoundingClientRect this will return a variable for you having two properties one is bottom which is the distance of the box from the upper edge of the document, the other property is height which is the distance of the box from the left border of the screen.
Now what you can do is compare those two values with the document width and height.
Now by adding those three lines you'll have your problem solved:
var rect = object.getBoundingClientRect(); // getting the properties
if(rect.right > window.innerWidth // comparing width
|| rect.bottom > window.innerHeight) // comparing height
{reappear(object);} // re-randomizing
https://jsfiddle.net/y3m4ygow/2/
this WILL work, but it might produce some flickering with some browsers, and i'm not very comfortable about calling a function inside itself.
Second Solution is: which is what I would prefer you to do, is by not using a percentage, but using a fixed height and width values.
you can get the current height and weight values from the window object and substract your box dimensions from it:
var cHeight = window.innerHeight - 200;
var cWidth = window.innerWidth - 200;
set those two as the maximum value for the top and the right.
function topRandomNum() {
var r = Math.random();
var y = (r * (cHeight - 0 + 1)) + 0;
var x = Math.floor(y);
return x;
}
function rightRandomNum() {
var r = Math.random();
var y = (r * (cWidth - 0 + 1)) + 0;
var x = Math.floor(y);
return x;
}
and here's the fiddle for the second solution: https://jsfiddle.net/uL24u0e4/

Related

Prevent subpixel rendering with svg

I'm working with SVGs currently and came to a dead end.
The SVG has lines, which should scale together with zooming (so that they stay in balance: 100% width 10px --> 10% width 1px for example)
i scale all stroke-widths with this code:
var svgPath = this._svgContainer.find('svg [class*="style"]');
for (var i = 0; i < svgPath.length; ++i) {
var newStrokeWidth = this._oldStrokeWidth[i] * (1 / (width / imgData.w));
$(svgPath[i]).css(
'stroke-width', newStrokeWidth
);
}
Where width is the new width after zoom and imgData.w is the original unscaled width.
The problem with this is, if i zoom in to far. The stroke with becomes to small and leads to sub-pixel rendering. And supposedly black lines get grey-ish.
My Idea was to clip the value at a certain point to prevent it.
But as far as I know, I have to consider the Device Pixel ratio too, because of different screens (desktop, mobile, 4K)
Would be nice If someone can help me with an idea to fix my problem
We finally found a solution for this, in case anyone has the same problems:
1) Because of the panning of this._$svgElement and the calculation of vpx in a completely different section of the code the element is 'between' pixels. ( 100.88945px for x for example). This causes lines to blur.
I fixed this part with a simple Math.round().
this._hammerCanvas.on('panmove', (event: any) => {
const translate3d = 'translate3d(' + Math.round(this._oldDeltaX + ((vpx === imgData.x) ? 0 : vpx) + event.deltaX) + 'px, ' + Math.round(this._oldDeltaY + ((vpy === imgData.y) ? 0 : vpy) + event.deltaY) + 'px, 0)';
this._$svgElement.css({
transform: translate3d
});
}
2) To fix the problem between the SVG viewport and the line strength, I had to implement a method to calculate the strokewidth equal to 1 'real' pixel regarding the svgs dimension.
the updated code looks like this: (This is the inital code, after the SVG was loaded from the server. Inside the zooming, the old code from above is still the same)
const pixelRatio = devicePixelRatio || 1;
const widthRatio = this._initSVGWidth / svgContainerWidth;
const heightRatio = this._initSVGHeight / svgContainerHeight;
this._svgZoomFactor = Math.max(widthRatio, heightRatio);
const strokeWidth1px = this.computeStrokeWidth1px(widthRatio, heightRatio);
for (let i = 0; i < svgPaths.length; ++i) {
this._initalStrokeWidth[i] = parseFloat($(svgPaths[i]).css('stroke-width'));
const newStrokeWidth = Math.max(strokeWidth1px / pixelRatio, this._svgZoomFactor * this._initalStrokeWidth[i]);
$(svgPaths[i])[0].setAttribute('style', 'stroke-width:' + newStrokeWidth);
this._oldStrokeWidth[i] = newStrokeWidth;
}
and the compute:
protected computeStrokeWidth1px (widthRatio: number, heightRatio: number): number {
const viewBox = this._$svgElement[0].getAttribute('viewBox').split(' ');
const viewBoxWidthRatio = parseFloat(viewBox[2]) / this._$svgElement.width();
const viewBoxHeightRatio = parseFloat(viewBox[3]) / this._$svgElement.height();
return widthRatio > heightRatio ? viewBoxWidthRatio : viewBoxHeightRatio;
}
var newStrokeWidth = this._oldStrokeWidth[i] * (1 / (width / imgData.w));
newStrokeWidth = (newStrokeWidth < 1) ? 1 : newStrokeWidth;
newStrokeWidth will always be 1 or greater

How to get the same scrollY height number on different screen sizes?

So I want to add a class on a specific screen height. So I've done:
window.onscroll = function() {scrollPost()};
function scrollPost () {
var x = window.screen.width;
var i = window.scrollY;
var a = i / x;
animator (a);
slide(a);
}
function slide(x){
var a = (x * 100);
var i = Math.round(a);
console.log(i);
var slideIn = document.querySelector(".slide-in-right");
var slideOut = document.querySelector(".slide-out-left");
if (i >= 90){
slideOut.classList.add("animate");
slideIn.classList.add("animate");
slideIn.style.display = ("block");
}
The problem however. Is that on different screens, the value will be different. Thus therefore on different screens it will toggle at a different time. I could probably make ifstatements, based on screen width. But then I'd need a whole lot of them.
I want the animation to be added only after the container is 100% in the screen height. How would you do this?
You are going to have to get users viewport (window height).
Divide it by half
Add it towards your IF position.
I wrote a simular answer, how ever it was written jQuery, if you are considering it would be a better output.
How to trigger a class that is only in view with wapoints
However to implement that to plain js:
Get user screen height:
var w = window,
d = document,
e = d.documentElement,
g = d.getElementsByTagName('body')[0],
x = w.innerWidth || e.clientWidth || g.clientWidth,
y = w.innerHeight|| e.clientHeight|| g.clientHeight;
alert(x + ' × ' + y);
divide y by half, and then add to your current position that checks if element is scrolled.
I will add this just in case. To get element position.
function getPosition(element) {
var xPosition = 0;
var yPosition = 0;
while(element) {
xPosition += (element.offsetLeft - element.scrollLeft + element.clientLeft);
yPosition += (element.offsetTop - element.scrollTop + element.clientTop);
element = element.offsetParent;
}
return { x: xPosition, y: yPosition };
}
Source: How to get the distance from the top for an element?

scroll parallax using margin-top

The below works with background-position: but it does not work when trying to use the same effect on a different element using margin-top: the second element does not have a background - but is div with content. Any suggestions?
function parallax() {
setpos(".filter-page");
setpos(".filter");
// setpos(".filter", 4);
// setpos("#pb3");
// setpos("#pb4");
}
function setpos(element, factor) {
factor = factor || 2;
var offset = $(".filter-page").offset();
var w = $(window);
var posx = (offset.left - w.scrollLeft()) / factor;
var posy = (offset.top - w.scrollTop()) / factor;
$(".filter-page").css('background-position', '10% '+posy+'px');
// $(".filter").css('background-position', '15% '+posy+'px');
$(".filter").css('margin-top', '10% '+posy+'px');
}
$(document).ready(function () {
parallax();
}).scroll(function () {
parallax();
});
When you use
$(".filter-page").css('background-position', '10% '+posy+'px');
it means you're setting the values x (10%) and y(posy pixels) of the background-position, wich is a valid value to this property (http://www.w3schools.com/cssref/pr_background-position.asp) - because you can set both X and Y at the same time.
However the attribute margin-top does not expect this kind of value (http://www.w3schools.com/cssref/pr_margin-top.asp). There is no way to set X and X values to "margin-top". This attribute has only one value to be setted. So, if you remove both 10% or posy px this will work.

Equation for image mouseover pan?

i have a simple jQ script:
a set width/height container
a landscape img (can be bigger or
smaller than container)
when a user mouses over the image, it pans
(no click/drag) until it reaches the end
The equation to move the img to the left is this:
-1(relative mouse-position)*(img width)/(container width)
This works fine, but it leaves a space one the mouse reaches the end of the img.
Fiddle
$("figure img").mousemove( function (e) {
var a = $(this).closest("figure"),
b = $(this).width(),
c = a.width(),
d = (e.clientX - a.offset().left);
$(this).css({
left: -1*(d*b/c)
}, 100);
});
can someone help? I want the img to be completely aligned to the right of the container once the mouse reaches the end.
The correct formula is: -1 * (d/c) * (b - c)
Or, more clearly: -1 * (mouseX / figureWidth) * (imgWidth - figureWidth)
(mouseX / figureWidth) represents the percent of the width of the figure that the mouse is positioned at. It will be a number between 0 and 1.
(imgWidth - figureWidth) represents the biggest X value you want to use to position the image at the opposite side.
Multiplying the percent by the total range of movement gives you the movement amount for the current mouse position!
Updated Fiddle
I suggest using more descriptive variable names such as figureWidth, imgWidth, mouseX etc. Not only will it be easier for you to understand, but it will be easier for people to answer.
This should work: http://jsfiddle.net/0zd5t1wf/4/
i just get the limit value for the left propriety of image (the image width - the figure box)
$("figure img").each( function () {
if($(this).width() >= ($(this).height() * 2.5)) {
$(this)
.attr("class", "panorama")
.mousemove( function (e) {
var a = $(this).closest("figure"),
b = $(this).width(),
c = a.width(),
d = (e.clientX - a.offset().left),
newLeft = -1*(d*b/c),
limitValue = parseInt($(this).width()) - parseInt($("figure").width());
if ( newLeft < 0 && (newLeft *-1) < limitValue ){
$(this).css({
left: newLeft
}, 100);
}
$("#hello").html('why');
});
}
});

Adjust position of a group of adjacent divs in non-linear fashion

This isn't so much a jQuery question as it is an overall conceptual question.
In my example I can populate a container with divs that have a top value set in a nonlinear fashion.
The top value of each one is calculated based on a formula that takes into account the top position of the one to its left as well as the height of the container (line 33 of fiddle).
//this formula sets the top value for each new child added to the container
//height is 100% of its parent which is 20% of the body
//newOne:last is the most recently added child and will have an initial top value of 10%
parseInt($(this).next().css('top'), 10) / $('#queue').height()) * 75 + (parseInt($('.newOne:last').css('top'), 10) * 2) + '%'
I more of less stumbled upon this by chance and it seems to work 'ok', but if an optimization is obvious to you, please point it out :)
What I'm having trouble coming up with is an elegant formula for how to adjust the children smoothly during a drag event. I'm thinking the top value needs to be adjusted based on some manipulation of the left offset, but after hours of experimenting, I haven't found anything that keeps the original position intact when I start dragging and continues adjusting the values smoothly during my drag. The children should gradually approach a minimum top value of 10% as I drag left (child with left offset of 0 will have a top value of 10%), and gradually move away from that top value back toward their initial position as I drag right.
$('#queue').draggable({
axis: "x",
scroll: false,
drag: function(){
//adjust values of each child
$('.newOne').each(function(){
var percentLeft = $(this).offset().left / $('footer').width() * 100
var thisLeft = parseInt($(this).css('left'), 10) / $(window).width() * 100;
var thisTop = parseInt($(this).css('top'), 10) / $('#queue').height() * 100;
if (percentLeft >= 0){
//top value of each one gradually decreases...
//as it gets closer to an offset value of 0 and minimum top value of 10%
//non-linear attempt but not even close
//$(this).css('top', $(this).css('top', 10 + (thisTop - 10 / thisLeft) + '%'));
//linear step
$(this).css({'top': 8 + (percentLeft/2) + '%'});
}
});
}
});
http://jsfiddle.net/5RRCS/17/
P.S. I know I'm asking a lot here, but hopefully someone is up to the challenge :)
Update:
Stumbled onto exp method and did something like this:
adjustTop = function(offset){
return 100 * (1.0-Math.min(0.98,(0.83 + ( 0.17/ (Math.exp(0.007*offset))) )) ) + '%';
};
$(this).css('top', adjustTop($(this).offset().left) );
Here's a version that I believe does what you are looking for.
The first thing I did was to refactor the top calculation so that both the initialization and the drag handlers would get the same results.
Rather than calculate the positions of the child divs based on their offset to the document, I changed the logic to use position relative to their container.
I also remove z-index as the child divs already being added the parent with the correct stacking order - the left most child is the last element in the container.
Calculating the height of each child depended on whether #queue's current position was to the left or right of its origin.
I also change the iteration logic to behave the same to simplify calculating the current elements starting offset:
$($('.newOne').get().reverse()).each(function (index) {
$(this).css({
'background': 'rgba(255,255,255,.80)',
'top': calcTop($(this), index)
});
});
Code for positioning the child elements:
function calcTop($ele, index) {
var elePositionLeft = $ele.position().left;
var queuePositionLeft = $('#queue').position().left;
var footerWidth = $('footer').width();
var queueHeight = $('#queue').height();
var distanceToTravel = queuePositionLeft < 0 ? elePositionLeft : footerWidth - elePositionLeft;
var percentTraveled = Math.abs(queuePositionLeft) / distanceToTravel;
var thisPercentLeft = (elePositionLeft + queuePositionLeft) / footerWidth;
var queuePercentLeft = queuePositionLeft / footerWidth;
var newTop;
var myStartOffset = (index + 1) * startOffset;
var topTravel = queuePositionLeft < 0 ? -myStartOffset + startOffset : (queueHeight - startOffset);
var linear = false;
if (linear) {
newTop = myStartOffset + (topTravel * percentTraveled);
newTop = newTop > startOffset ? Math.round(newTop) : startOffset;
return newTop;
} else {
if (queuePositionLeft >= 0) {
newTop = myStartOffset + (topTravel * thisPercentLeft * percentTraveled);
newTop = newTop > startOffset ? Math.round(newTop) : startOffset;
} else {
newTop = myStartOffset + (topTravel * (1+thisPercentLeft) * percentTraveled);
newTop = newTop < startOffset ? startOffset : Math.round(newTop);
}
return newTop;
}
}
There was also a minor bug in the reset function - it wasn't setting childCount back to zero:
$('#reset').click(function () {
$('#queue').empty().css('left', 0);
childCount = 0;
});
Demo Fiddle

Categories

Resources