Building on my previous inquiries, I have an image, cat.jpg, that is being cloned, resized (halved, to be precise), then sent to a random position on the page via jQuery's .css() and .animate(). Now, when a cat is clicked, I'd like it to generate a new image/element in its place temporarily before removing it. So the order of events would be:
Click cat.
Cat explodes into 3 smaller kittens.
Clicked cat element is removed and a new image temporarily takes its place.
Cat replacement image is removed after set amount of time (say, one second).
Here's where I'm at thus far. Everything is up to snuff save for keeping the new kittens within the document boundaries (which I'll solve on my own) and the replacement image. Here's the Fiddle, and here's the code:
var explode;
$('img.cat').click(explode=function() {
var contW = $(document).width();
var contH = $(document).height();
for (var i = 1; i <= 3; i++){
var source = $(this).position();
var posNeg = Math.random() < 0.5 ? -1 : 1;
var newTop = Math.floor(Math.random() * (contH / 2 + (posNeg * $(this).height()))) * posNeg;
var posNeg = Math.random() < 0.5 ? -1 : 1;
var newLeft = Math.floor(Math.random() * (contW / 2 + (posNeg * $(this).width()))) * posNeg;
var $kitty = $(this).clone().css({
width: $(this).width() / 2,
height: $(this).height() / 2
});
$('#container').append($kitty);
$kitty.css({ top: source.top, left: source.left })
.animate({ top: newTop+'px', left: newLeft+'px' }, 300)
.click(explode);
}
$(this).remove();
});
I know I have to get the current position of the $kitty (or rather img.cat) being clicked on and set the new object to that via CSS, but my implementation is flawed.
var $lion = $('#replacement').clone().css({
top: newTop,
left: newLeft,
});
$('#container').append($lion);
<img id="replacement" src="http://images1.wikia.nocookie.net/__cb20130326142633/warriors/images/4/47/Lion.png" />
How can I get the lion to show up for only a second when any img.cat is clicked, and show up in that cat's exact position?
instead of $(this).remove() try to replace the cat with lion
var $lion = $('<img id="replacement" src="http://images1.wikia.nocookie.net/__cb20130326142633/warriors/images/4/47/Lion.png" />')
$(this).replaceWith($lion);
setTimeout(function () {
$lion.remove()
}, 1000)
Demo: Fiddle, Make sure the image is displayed for 1 sec
Related
I'm trying to make a random slideshow of images for a gallery. I have two images on the page – the "front" one and the "back" one, and I have a timer set up to, every few seconds, move the back image to the front and load a new back image. I'm basically doing this by swapping the image objects in the code.
As the back image becomes the front image, I have it fade in gradually from an opacity of 0 to an opacity of 1, while I have the front image do the same in reverse. I implemented this as follows:
var fadeOutCt = 0;
var fadeOutInterval;
// Decrements the opacity of element by amt, until cap
function decrementOpacity( element, amt, cap ) {
var currentOpacity = Number( window.getComputedStyle( element ).getPropertyValue( "opacity" ) );
currentOpacity = currentOpacity - amt;
element.setAttribute( "style", "opacity:" + currentOpacity );
fadeOutCt = fadeOutCt + 1;
if ( fadeOutCt >= cap ) {
element.setAttribute( "style", "opacity:0" );
clearInterval( fadeOutInterval );
}
}
// Calls decrementOpacity to fill the specified interval.
function fadeOut( element, interval ) {
var currentOpacity = Number( window.getComputedStyle( element ).getPropertyValue( "opacity" ) );
fadeOutCt = 0;
if ( currentOpacity > 0 ) {
cap = interval / 10.0;
amt = 1.0 / cap;
fadeOutInterval = setInterval( decrementOpacity, 10, element, amt, cap );
}
}
Separately, I have another routine that resizes the image I load to conform to the user's screen (it also centers the image). I run this immediately before the fade-in or fade-out operation.
function resizeForSlideshow( imgToResize ) {
// Get size of usable area for slideshow
var usableWidth = window.innerWidth;
var titleTable = document.getElementById( "descTable" );
var windowHeight = window.innerHeight;
var tableHeight = titleTable.offsetHeight;
var usableHeight = windowHeight - tableHeight;
// Resize containing div
var slideDiv = document.getElementById( "slideDiv" );
slideDiv.setAttribute( "style", "height:" + usableHeight + "px" );
// Get size of native image to be displayed
var nativeWidth = imgToResize.naturalWidth;
var nativeHeight = imgToResize.naturalHeight;
// Determine width-to-height ratios of those two
var windowRatio = usableWidth / usableHeight;
var imageRatio = nativeWidth / nativeHeight;
if ( imageRatio > windowRatio ) {
// image's width-to-height is greater than window
// image should be set to 100% width, less height
imgToResize.width = usableWidth;
imgToResize.height = usableWidth * ( nativeHeight / nativeWidth );
// relocate image accordingly
var newTop = ( usableHeight - imgToResize.height ) / 2;
imgToResize.style.left = 0;
imgToResize.style.top = newTop + "px";
}
else {
// image's width-to-height is less than window
// image should be set to 100% height, less width
imgToResize.height = usableHeight;
imgToResize.width = usableHeight * ( nativeWidth / nativeHeight );
// relocate image accordingly
var newLeft = ( usableWidth - imgToResize.width ) / 2;
imgToResize.style.top = 0;
imgToResize.style.left = newLeft + "px";
}
}
The problem is, when I fade in or fade out, it breaks the positioning of my images. Instead of being centered, they revert to being on the top left of the page (though their size remains what it should be). I've tried a few things but I'm out of ideas, and I was hoping someone would be able to shed some light on what's going wrong here and how I could fix it.
(If seeing the code in action would help: http://artmonitors.com/slideshow/full-slideshow.html
EDIT: I later figured out the problem.
The problem is that using setAttribute to set opacity also removed the positional settings I had given. Manually setting element.style.opacity made things work.
How about centering it with css?
.slide {
display: block;
margin: auto;
position: relative;
}
You should close the style with ;
element.setAttribute( "style", "opacity:" + currentOpacity +";");
I later figured out the problem. The problem is that using setAttribute to set opacity also removed the positional settings I had given. Manually setting element.style.opacity made things work.
How do I use jQuery through a Drupal 8 module?
I'm trying to get some jquery code running on a drupal 8 website. It's a div animation that randomly moves the div within a wrapper.
I've added the JS and CSS files and they're both showing in the source. I've then added the Div ID of 'container' to html.html.twig(yes I copied it into my theme folder first) -
{%
set body_classes = [
logged_in ? 'user-logged-in',
not root_path ? 'path-frontpage' : 'path-' ~ root_path|clean_class,
node_type ? 'page-node-type-' ~ node_type|clean_class,
db_offline ? 'db-offline',
theme.settings.navbar_position ? 'navbar-is-' ~ theme.settings.navbar_position,
theme.has_glyphicons ? 'has-glyphicons',
'container',
]
%}
Also added a div to use for this. Again in html.html.twig
<js-bottom-placeholder token="{{ placeholder_token|raw }}"> //**Not part of my code just adding it for comparison.
<div class='fly'>TEST DIV, IF I MOVE I'M WORKING!</div>
So the files show up in the source and the source also shows that the div ID of container has been added.
Here's the jQuery code, keep in mind that I've adjusted it to work with Drupal 8 so there's probably a syntax error or two.
$.extend(Drupal.theme, {
animateDiv: (function() {
($('.fly')); }),
* Almost certain the problem is here ^^^
makeNewPosition: function ($container) {
// Get viewport dimensions (remove the dimension of the div)
var h = $container.height() - 10;
var w = $container.width() - 10;
var nh = Math.floor(Math.random() * h);
var nw = Math.floor(Math.random() * w);
return [nh, nw];
},
animateDiv: function ($target) {
var newq = makeNewPosition($target.parent());
var oldq = $target.offset();
var speed = calcSpeed([oldq.top, oldq.left], newq);
$target.animate({
top: newq[0],
left: newq[1]
}, speed, function() {
animateDiv($target);
});
},
calcSpeed: function (prev, next) {
var x = Math.abs(prev[1] - next[1]);
var y = Math.abs(prev[0] - next[0]);
var greatest = x > y ? x : y;
var speedModifier = 0.23;
var speed = Math.ceil(greatest / speedModifier);
return speed;
}
});
Here's the CSS code
.fly {
width: 50px;
height:50px;
background-color: red;
position:fixed;
}
That's what I've tried and yet there's still no jQuery activity, I've also tried using different types for class names(.div instead of #div etc) which I think is where the problem is. The flying div shows but isn't being styled and there's no jQuery. What am I doing wrong?
Update: After playing with the code for awhile I've managed to at least get the style to show on the div. jQuery still a no show though..
Got the answer here https://drupal.stackexchange.com/questions/214483/how-do-i-use-jquery-code-through-a-module-theme/214485?noredirect=1#comment262213_214485
Apparently my Drupal/jQuery syntax wasn't correct.
I wrote a basic program that moves a button inside a DIV element. The button is not staying within the area I had intended. On the HTML side I have code that defines the DIV with an id = "page". Here is the js code. Why is the button not staying within the DIV element?
var buttonState = document.getElementById("clickMe");
var maxWidth = document.getElementById("page");
var maxHeight = document.getElementById("page");
var pageWidth = maxWidth.clientWidth;
var pageHeight = maxHeight.clientHeight;
var screenWidth = 0;
var screenHeight = 0;
function moveButton() {
"use strict";
// Find max width and height of screen and set variables to random number within parameters
screenWidth = Math.floor(Math.random() * (pageWidth)) + 1;
screenHeight = Math.floor(Math.random() * (pageHeight)) + 1;
console.log(screenWidth);
console.log(screenHeight);
// Button position
buttonState.style.left = (screenWidth) + "px";
buttonState.style.top = (screenHeight) + "px";
// Button size
buttonState.style.width = buttonSize + "em";
buttonState.style.height = buttonSize + "em";
In your equations for screenWidth and screenHeight you need to take into account the size of the button.
The div is positioned based on the top left pixel so if you end up with Math.random() returning 1 or something very close to it, the button will fall out of the page unless you subtract the button sizes from the maxes.
var buttonWidthPx = buttonState.offsetWidth;
var buttonHeightPx = buttonState.offsetHeight;
screenWidth = Math.floor(Math.random() * (pageWidth - buttonWidthPx)) + 1;
screenHeight = Math.floor(Math.random() * (pageHeight - buttonHeightPx)) + 1;
Also make sure to set the positioning of the button to relative so it positions inside the div and not absolute to the document.
First thought, it is probably more of a css layout issue than javascript or html.
The first clue to this is
buttonState.style.left
buttonState.syyle.top
If you check the buttonState in Chrome DevTools you might find the layout to be: absolute and that would be one good reason why it does not layout as intended. Another would be if layout is set to static.
Here is a link that gives good depth of information on css layout settings: http://alistapart.com/article/css-positioning-101
The first thing I would try would be to open DevTools and deselect (remove) all the styles that buttonState has until you find the layout that is causing the problem.
I don't know the exact problem that you are facing because you didn't provide any HTML/CSS, but check out this fiddle for a working example.
<div id="page">
<button id="clickMe">Click Me</button>
</div>
<button id="move">Move Button</button>
#page {
width:300px;
height: 300px;
background-color: whitesmoke;
}
#clickMe {
position: relative;
top: 0;
left: 0;
}
var page = document.getElementById("page");
var pageWidth = page.clientWidth;
var pageHeight = page.clientHeight;
var button = document.getElementById("clickMe");
var buttonWidth = button.clientWidth;
var buttonHeight = button.clientHeight;
function moveButton() {
var x = Math.floor(Math.random() * (pageWidth - buttonWidth));
var y = Math.floor(Math.random() * (pageHeight - buttonHeight));
button.style.left = x + "px";
button.style.top = y + "px";
}
document.getElementById("move").onclick = moveButton;
Using this - random position of divs in javascript as a starting point I am trying to randomly position divs containing text from an array.
Current code (predominately copied Ken Redler's answer in the linked post):
(function makeDiv(){
var divsize = 200;
var color = '#'+ Math.round(0xffffff * Math.random()).toString(16);
//set up the array to hold the random div content
var boastsText = [];
var i = 0;
$(".boast ul li").each(function() { boastsText.push($(this).text()) });
$newdiv = $('<div/>').css({
'width':divsize+'px',
'height':divsize+'px',
'color': color
});
// make position sensitive to size and document's width
var posx = (Math.random() * ($(document).width() - divsize)).toFixed();
var posy = (Math.random() * ($(document).height() - divsize)).toFixed();
$newdiv.css({
'position':'absolute',
'left':posx+'px',
'top':posy+'px',
'display':'none'
}).html(boastsText[i]).appendTo( 'body' ).fadeIn(100).delay(1000).fadeOut(500, function(){
$(this).remove();
makeDiv();
});
})();
The only thing I have really added is the setting up of the array var boastsText = [] and the each function that populates that array.
The problem I have is I need to iterate over the array so each new div created uses the next item as it's content i.e. div 1 uses array item 0, div 2 uses item 1 etc... until it reaches the end of the array and then starts the process again.
I've modified your script a little bit to use a recursive function in order to iterate over the array and maintain the delay and fadeIn/Out you have:
(function() {
//set up the array to hold the random div content
var boastsText = [];
$(".boast ul li").each(function () {
boastsText.push($(this).text());
});
function makeDiv(i){
var divsize = 200;
var color = '#'+ Math.round(0xffffff * Math.random()).toString(16);
var $newdiv = $('<div/>').css({
'width':divsize+'px',
'height':divsize+'px',
'color': color
});
// make position sensitive to size and document's width
var posx = (Math.random() * ($(document).width() - divsize)).toFixed();
var posy = (Math.random() * ($(document).height() - divsize)).toFixed();
$newdiv.css({
'position':'absolute',
'left':posx+'px',
'top':posy+'px',
'display':'none'
}).html(boastsText[i]).appendTo('body').fadeIn(100).fadeOut(500, function() {
$(this).remove();
if (i === boastsText.length) return;
makeDiv(++i);
});
}
//Start the recursion
makeDiv(0);
}());
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