I'm scaling a div.parent by n% using transform : scale( 0.25 ). The width of div.parent is quite large and exceeds the viewport bounds. This container div has n children, each with a relative offset position, which I'm getting via jQuery like ( approximation )
parent = $ '.parent'
child = $ '.child'
posLeft = child.position().left
I'm then moving the div.parent container using the value posLeft.
parent.css
transform : "translate3d( #{posLeft}px, 0, 0 )"
I would like to expand the function that handles the above animation by allowing it to continue to move div.parent accurately ( as related to the child items posLeft ) when it's scaled down. Here's an image to help illuminate the problem.
The solution turned out to be really simple — after eliminating a few bugs in my code. For anyone else who is interested in doing something similar, I'll post an approximation of the code below ( which uses TweenMax to handle the transitions )
_state =
scale : 1
state: ( state, value )->
if value is undefined
return _state[state]
else
_state[state] = value
calculateDistance( distance, scale )->
newDistance = distance * scale
return newDistance
scaleDown = ( amount )->
if amount > 1 then amount = 1
props =
x : 0
scale : amount
transformOrigin : 'center left'
onComplete : Done
TweenMax.to container, 0.4, props
Done =->
num = this.vars.css.scale
state( 'scale', num )
and how to use calculateDistance()
location = calculateDistance( move.destination.attr( 'data-location' ), state('scale' ) )
This enables me to use 1 method for both the scaled and full versions of any transitions.
Related
So what I want to happen is that when viewing the Span the text is normal but as you scroll down it starts moving until it looks like such:
Before the effect:
While the effect occurs:
The header is represented by spans for each letter. In the initial state, the top pixel value for each is 0. But the idea as mentioned is that that changes alongside the scroll value.
I wanted to keep track of the scroll position through JS and jQuery and then change the pixel value as needed. But that's what I have been having trouble with. Also making it smooth has been another issue.
Use the mathematical functions sine and cosine, for characters at even and odd indices respectively, as the graphs of the functions move up and down like waves. This will create a smooth effect:
cos(x) == 1 - sin(x), so in a sense, each character will be the "opposite" of the next one to create that scattered look:
function makeContainerWiggleOnScroll(container, speed = 0.01, distance = 4) {
let wiggle = function() {
// y-axis scroll value
var y = window.pageYOffset || document.body.scrollTop;
// make div pseudo-(position:fixed), because setting the position to fixed makes the letters overlap
container.style.marginTop = y + 'px';
for (var i = 0; i < container.children.length; i++) {
var span = container.children[i];
// margin-top = { amplitude of the sine/cosine function (to make it always positive) } + { the sine/cosine function (to make it move up and down }
// cos(x) = 1 - sin(x)
var trigFunc = i % 2 ? Math.cos : Math.sin;
span.style.marginTop = distance + distance * trigFunc(speed * y)/2 + 'px';
}
};
window.addEventListener('scroll', wiggle);
wiggle(); // init
}
makeContainerWiggleOnScroll(document.querySelector('h2'));
body {
height: 500px;
margin-top: 0;
}
span {
display: inline-block;
vertical-align: top;
}
<h2>
<span>H</span><span>e</span><span>a</span><span>d</span><span>e</span><span>r</span>
</h2>
Important styling note: the spans' display must be set to inline-block, so that margin-top works.
Something like this will be the core of your JS functionality:
window.addEventListener('scroll', function(e) {
var scrl = window.scrollY
// Changing the position of elements that we want to go up
document.querySelectorAll('.up').forEach(function(el){
el.style.top = - scrl/30 +'px';
});
// Changing the position of elements that we want to go down
document.querySelectorAll('.down').forEach(function(el){
el.style.top = scrl/30 +'px';
});
});
We're basically listening in on the scroll event, checking how much has the user scrolled and then act upon it by offsetting our spans (which i've classed as up & down)
JSBin Example
Something you can improve on yourself would be making sure that the letters wont go off the page when the user scrolls a lot.
You can do this with simple math calculation, taking in consideration the window's total height and using the current scrollY as a multiplier.
- As RokoC has pointed out there is room for performance improvements.Implement some debouncing or other kinds of limiters
As per Jquery API documentation:
.position()Returns: Object
Description: Get the current coordinates of the first element in the set of matched elements, relative to the offset parent.
This method does not accept any arguments.Reference here
But somewhere i found using this:
$("#position1").position({
my: "center",
at: "center",
of: "#targetElement"
});
An object has been passed to position method .Isn't this against API documentation?It seems that the properties passed to an object above has some special meaning.What are those properties stating .What they do?I m a complete beginner to jquery.So may be i m wrong .
This variant of .position() is part of the jQuery UI position utility. It gives you an easy way to place an element relative to another one (or the mouse cursor) in a certain way.
You are totally right that the original position() method does not accept arguments... but:
This plugin extends jQuery's built-in .position() method. If jQuery UI is not loaded, calling the .position() method may not fail directly, as the method still exists. However, the expected behavior will not occur.
Check this out - http://docs.jquery.com/UI/API/1.8/Position
That feature is in jqueryUI position utility not in Core jQuery
Let's take it to the codez ! A quick glance into the jQuery 1.9.1 source reveals:
position: function() {
if ( !this[ 0 ] ) {
return;
}
var offsetParent, offset,
parentOffset = { top: 0, left: 0 },
elem = this[ 0 ];
// fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is it's only offset parent
if ( jQuery.css( elem, "position" ) === "fixed" ) {
// we assume that getBoundingClientRect is available when computed position is fixed
offset = elem.getBoundingClientRect();
} else {
// Get *real* offsetParent
offsetParent = this.offsetParent();
// Get correct offsets
offset = this.offset();
if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
parentOffset = offsetParent.offset();
}
// Add offsetParent borders
parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
}
// Subtract parent offsets and element margins
// note: when an element has margin: auto the offsetLeft and marginLeft
// are the same in Safari causing offset.left to incorrectly be 0
return {
top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true)
};
},
No arguments read, no arguments used. Wherever you saw that code, its not jQuery core. Most likely, its because the original author used the jQuery UI, which extends that method.
As per the jQuery API for the Position
my: Defines which position on the element being positioned to align
with the target element.
at: Defines which position on the target element to align the positioned element against,
of: Is for the element to position against. If you provide a selector, the first matching element will be used. Example: "#targetElement" in your case.
I've looked everywhere and so far have not found a non-jQuery js to handle this. I would like to avoid using a library for just this one simple task.
I would like to fix three navigation divs ("#header", "#tabs" and "#footer") to viewport left (or alternatively, to the x position of a div "#helper" with "position: fixed; left: 0; top: 0;") -- but not fix y. They can not be vertically fixed.
I've created a working js that forces the divs to reposition based on scrolling, but it's not smooth in the real page (too many dynamic and graphic elements) - I'd like it to either animate smoothly, or mimic fixed-left and not appear to reposition at all.
Anyone who can give pointers or a quick script, or review and modify the script I have made? I've noticed people tend to ask why an obvious solution is not used instead of answering the question... I will be glad to answer, but would prefer help with the actual problem.
Here is a jsFiddle with the problem: http://jsfiddle.net/BMZvt/6/
Thank you for any help!
Smooth animation example:
var box = document.getElementById('box');
var moveTo = function(obj, target) {
// start position
// you should obtain it from obj.style
var cpos = {
x: 0,
y: 0
}
var iv = setInterval(function(){
cpos.x += (target.x - cpos.x) * 0.3; // 0.3 is speed
cpos.y += (target.y - cpos.y) * 0.3; // 0.3 is speed
obj.style.left = Math.floor(cpos.x) + 'px';
obj.style.top = Math.floor(cpos.y) + 'px';
var dist = Math.abs(cpos.y - target.y); // distance (x+y) from destination
dist += Math.abs(cpos.x - target.x); // < 1 = object reached the destination
if(dist < 1) { // here we are checking is box get to the destination
clearInterval(iv);
}
}, 30); // this is also the speed
}
box.onclick = function(){
moveTo(box, {x: 90, y: 75}); // fire this function to move box to specified point
}
Demonstration: http://jsfiddle.net/Qwqf6/5/
Your script is your job, but this is a quick start how to solve animation problem
You can also do some fancy stuff with speed for example use sin(x) to set the speed
Demonstration #2 http://jsfiddle.net/Qwqf6/6/ (very smooth)
Full script here https://gist.github.com/3419179
I don't think there's a straight way to do this...
But here's a way.
First, You need to be able to detect the direction of the scrolling when window.onscroll event happens. You would do this by comparing the current page offsets with the newly acquired page offsets whenever the scroll event happens. (http://stackoverflow.com/questions/1222915/can-one-use-window-onscroll-method-to-include-detection-of-scroll-direction)
Now suppose you know the direction of the scroll, you want to change the styling for the divs depending on the direction of the scroll.
Let FixAtX be the value of the x coordinate that you want to fix your divs at.
Let OriginalY be the y coordinate of the divs.
Also whenever scrolling happens, despite of the direction, you want to remember the pageoffset X and Y. Let's call them OldX and OldY
If scrolling vertically:
Set position value for divs' style to be absolute.
Set top value for divs' style to be OriginalY
Set left value for divs' style to be OldX + FixAtX
If scrolling horizontally:
Set position value for divs' style to be fixed.
set top value for divs' style to be OriginalY - OldY (<- this may be different depending on how the browser computes pageOffset value,)
Set Left value for divs' style to be FixAtX
I think this should work...
Since you are just using browser's rendering for positioning, it should be very smooth!
hope I understood the question correctly.
This is for people who view this post - I wound up going with the solution I initially put together in the jsFiddle that used a simple javascript to mimic fixed x.
The javascript in the first answer was hefty and wound up buggy, and the second answer sounded good but did not work in practice. So, I'm recommending the javascript from the jsFiddle (below) as the best answer to fixed x and fluid y without a javascript library. It's not perfect and has a minimal delay but is the best answer I've found.
function fixLeft() {
function getScrollX() {
var x = 0, y = 0;
if( typeof( window.pageYOffset ) == 'number' ) {
x = window.pageXOffset;
} else if( document.body && ( document.body.scrollLeft) ) {
x = document.body.scrollLeft;
} else if( document.documentElement && ( document.documentElement.scrollLeft) ) {
x = document.documentElement.scrollLeft;
}
return [x];
}
var x = getScrollX();
var x = x[0];
// have to get and add horizontal scroll position px
document.getElementById('header').style.left = x + "px";
document.getElementById('tabs').style.left = x + "px";
document.getElementById('footer').style.left = x + "px";
}
window.onscroll = fixLeft;
I have a jQuery UI draggable element. It's extremely simple. It's just a div (container) with another div inside (draggable piece) set to a grid. The problem is after I move the element one time I can't go back to the first point. If i change the grid size it works, but I need it to work on this grid as it's matching some element below it
Relevant JS:
$('<div class="slider_wrap"><div class="slider"></div></div>').appendTo('#chart');
$('#chart .slider')
.draggable({
containment:'parent',
grid:[distanceBetweenPoints,0],
opacity: 0.25
})
.bind('mousedown', function(e, ui){
// bring target to front
$(e.target.parentElement).append( e.target );
})
.bind('drag', function(e, ui){
// update coordinates manually, since top/left style props don't work on SVG
e.target.setAttribute('x', ui.position.left);
})
.bind('dragstop',function(e, ui){
//a = true offset of slider piece
var a = ui.position.left + distanceBetweenPoints;
var b = containerWidth;
var c = thePoints.length;
var d = b / c;
var x = a / d;
//Since the points are in an array which starts at 0, not 1, we -1 from the currentPoint
console.log(x)
var currentPoint = Math.round(x)-1;
thisPointIndex = currentPoint;
chart.series[0].data[currentPoint].select(true);
});
Any ideas?
Example:
http://jsbin.com/ucebar
You're using a fractional grid size, for example 39.7 px. So, with each drag, the div gets offset a pixel to the left. This means that position zero quickly becomes unavailable:
That is: at point 1, ui.position.left will be 38 pixels or less.
Since moving the minimum jump (39.7px) -- towards point 0 -- will take the div outside the bounding rectangle, that move is not allowed.
And, using the nearest integer for grid size will quickly result in misalignment between the grid and the data points.
One way around all this is to:
Delete the grid:[distanceBetweenPoints,0], parameter.
Instead, snap the div upon drag stop, like so:
/*--- Snap to nearest grid.
*/
var gridPos = Math.round (
Math.round (ui.position.left / distanceBetweenPoints)
* distanceBetweenPoints
);
var delta = gridPos - ui.position.left;
var newOffset = $(this).offset ().left + delta;
$(this).offset ( {left: newOffset} );
See it in action at jsBin.
I didnt have time to work out a real solution, but I found that if you drag and drop the slider moves slightly more to the left each time. The reason it can't go back into first place is that after the first drop, there is not enough room anymore. Good luck!
I was able to solve it with the following:
.draggable({
...
drag : function(event, ui) {
ui.position.left = Math.round(ui.position.left / distance_between_points) * distance_between_points;
}
});
I have a site. I want to make 3 vertical divs with equal height. For this purposes I change the height of last block in each column/div.
For example, the naming of 3 columns are:
.leftCenter
.rightCenter
.right
Now I wrote a code which set the equal height for .leftCenter and .rightCenter:
var left = $('.leftCenter').height();
var center = $('.rightCenter').height();
var news = $('#newItemsList').height();
if (center < left)
$('.rightCenter').height(center + (left-center));
else if (center > left)
$('#newItemsList').height(news + (center-left));
news is the latest subblock in left column (there are 3 images in it). So, if central div is bigger than left div, I change the height of news to make them equal. This code works in Firefox, but doesn't work in Chrome. That's the first question. And the last is: how to make equal 3 divs (including right one).
I needed to make elements equal in height and width so I made the following function that allows you to define a height, or width, or really whatever at it. refType would be used if you sent a min-height and needed it to match the height of the tallest element.
elemsEqual = function (options) {
var defaults = {
'type' : 'height',
'refType' : '',
'elements' : [],
'maxLen' : 450
},
settings = $.extend({}, defaults, options),
max = 0;
$(settings.elements.join(",")).each(function () {
max = Math.max( max, parseInt( $(this).css(settings.type) ) );
if(settings.refType.length) max = Math.max( max, parseInt( $(this).css(settings.refType) ) );
});
max = ((max < settings.maxLen) ? max : settings.maxLen);
$(settings.elements.join(",")).css(settings.type, max + "px");
};
elemsEqual({elements : ['#selector1','#selector2', ... '#selectorN'], type : 'height'});
Well I have this so far:
//Get the height of the right column since it starts at a different Y position than the other two
var right=$('.right').outerHeight(1)-$('.left').children('header').outerHeight(1)-$('.left .innav').outerHeight(1);
//Get the max of the 3
var height_max=Math.max($('.leftCenter').outerHeight(1),$('.rightCenter').outerHeight(), right);
//Apply the max to all 3
$('.rightCenter').height(height_max-3); //-3 to accommodate for padding/margin
$('.right').height(height_max);
$('.leftCenter').height(height_max);
The only problem is that it does not make #newItemsList as tall as the parent, .leftCenter. It also assumes that the right div will be largest, so I don't know if it will still work if it isn't the biggest of the 3.