Apply animation script to different Div/img - javascript

Need to apply the animateScript() function to different images/divs on mouseover. i think i just need to change my document.querySelector but im not sure how to do so so that it applies to one of the 4 different images.
let tID; //we will use this variable to clear the setInterval()
//let x = event.clientX, y = event.clientY, elementMouseIsOver = document.elementFromPoint(x, y);
function stopAnimate() {
clearInterval(tID);
} //end of stopAnimate()
function animateScript() {
let position = 80; //start position for the image slicer
let height = 0;
const interval = 190; //180 ms of interval for the setInterval()
const diff = 80; //diff as a variable for position offset
const next_row = 110;
let elements = document.querySelectorAll(':hover');
tID = setInterval(() => {
document.querySelector(".avatar").style.backgroundPosition = `-${position}px -${height}px`;
//we use the ES6 template literal to insert the variable "position"
if (height < 220)
{
if (position < 720) {
position = position + diff;
console.log(position);
}
else if (position == 720){
//console.log("height");
position = 80;
height = next_row + height;
console.log("height: ", height);
}
}
else {
if (position < 400) {
position = position + diff;
console.log(position);
}
else if (position == 480){
position = 80;
height = 0;
console.log("height: ", height);
}
}
//reset the position to 256px, once position exceeds 1536px
}, interval); //end of setInterval
} //end of animateScript()

You're hard-coding the .avatar selector, which will always get the first element with that class, so you're correct that that will need to change.
One easy solution would be to pass the hovered element into your function.
const handleMouseEnter = (e) => {
const el = e.target;
animateScript(el);
}
element.addEventListner("mouseenter", handleMouseEnter);
function animateScript(element) {
[...]
tID = setInterval(() => {
element.style.backgroundPosition = `whatever style`;
[...]
}, interval);
}

Related

allow native scroll in container during touchmove event like with drag and drop

Is there a way to allow native scrolling easily without heavy JS modifications when you reach the border of a div via custom drag and drop via touchmove listener?
When you drag the text in the div here you'll see the div inside is scrolling automatically
I provided an example with touchmove listeners but this one does not scroll, when you reach a border with your mouse
Is there an easy way to include a scrolling behavior to the 2nd example?
const element = document.body.querySelector('#draggable');
const isInContainer = (x,y) => {
const elements = document.elementsFromPoint(x, y)
return elements.find(el => el && el.classList && el.classList.contains('container')) || false;
}
const onMouseMove = (e) => {
if(isInContainer(e.pageX, e.pageY)){
element.style.top = e.pageY + 'px';
element.style.left = e.pageX + 'px';
}
}
const onMouseUp = () => {
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp)
}
element.addEventListener('mousedown', (e) => {
e.preventDefault();
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp)
});
In case someone has a native better solution I'm willing to accept that one... for the time being this would be my current way to solve the issue.
Note: I made a custom interval for scrolling and don't use the mousemove event so users don't have to move the mouse to trigger it. moving outside will start the interval moving inside will clear it.
// the container that should scroll
const scrollBody = document.getElementById('scrollContainer');
// parameter to check which directions should scroll
const scrollPositions = {
left: false,
right: false,
up: false,
down: false,
}
// how far should be scrolled
const nextScrollDistance = {
x: 0,
y: 0
}
// scroll interval
let scrollInterval= null;
const startScrolling = (scrollBody) => {
if (scrollInterval !== null) {
return true;
}
const intervalCallback = () => {
if (scrollInterval !== null && nextScrollDistance.x === 0 && nextScrollDistance.y === 0) {
window.clearInterval(scrollInterval);
scrollInterval = null;
} else {
scrollBody.scrollLeft += nextScrollDistance.x;
scrollBody.scrollTop += nextScrollDistance.y;
}
}
scrollInterval = window.setInterval(intervalCallback, 50);
}
const onMouseMove = (e) => {
if(isInContainer(e.pageX, e.pageY)){
element.style.top = e.pageY + 'px';
element.style.left = e.pageX + 'px';
}
const rects = scrollBody.getBoundingClientRect();
// check directions
// max x that can be scrolled
const maxX = scrollBody.scrollWidth - scrollBody.clientWidth;
// max y that can be scrolled
const maxY = scrollBody.scrollHeight - scrollBody.clientHeight;
// check all directions if it's even possible to scroll
const canScrollTop = Math.round(scrollBody.scrollTop) > 0;
const canScrollBottom = Math.round(scrollBody.scrollTop) < maxY;
const canScrollLeft = Math.round(scrollBody.scrollLeft) > 0;
const canScrollRight = Math.round(scrollBody.scrollLeft) < maxX;
// current x and y coordinates of the mouse
const x = e.pageX;
const y = e.pageY;
// dynamic value to decrease the speed.. otherwise it might scroll too fast
const minifier = 2;
// the modifiers for scrollTop and scrollLeft
nextScrollDistance.y = 0;
nextScrollDistance.x = 0;
if (canScrollBottom && y > rects.bottom) {
// distance between the right border and the mouse
const distance = Math.abs(y - rects.bottom);
// the next time it scrolls -> scroll distance / minifier
nextScrollDistance.y = Math.round(distance / minifier)
scrollPositions.down = true;
} else {
scrollPositions.down = false;
}
// all other directions...
if (canScrollTop && y < rects.top) {
const distance = Math.abs(y - rects.top);
nextScrollDistance.y = Math.round(distance / minifier) * -1;
scrollPositions.up = true;
} else {
scrollPositions.up = false;
}
if (canScrollRight && x > rects.right) {
const distance = Math.abs(x - rects.right);
nextScrollDistance.x = Math.round(distance / minifier)
scrollPositions.right = true;
} else {
scrollPositions.right = false;
}
if (canScrollLeft && x < rects.left) {
const distance = Math.abs(x - rects.left);
nextScrollDistance.x = Math.round(distance / minifier) * -1;
scrollPositions.left = true;
} else {
scrollPositions.left = false;
}
// in case one of those are set.. trigger scrolling
if (nextScrollDistance.x || nextScrollDistance.y) {
startScrolling();
}
}

My JS animation resets its marginTop, but not its marginLeft

Im supernew to javascript animations. I just found out how to add the 2nd movement to my animation.
When running funk() function, the ball goes down to the right. When it triggers funktre() function, the ball goes to its starting marginTop position but keeps its marginLeft position.
Why does it return to the top? I want the 2nd animation, funktre(), to move the ball from the funk() ending position.
const ball = document.querySelector("#en");
ball.addEventListener("click", funk);
function funk() {
let id = null;
let position = 0;
clearInterval(id);
id = setInterval(funkto, 5);
function funkto() {
if (position == 450) {
clearInterval(id);
funktre();
// sett en ny funksjon her
}
else {
stop();
position++;
ball.style.marginLeft = position + "px";
ball.style.marginTop = position + "px";
}
}
};
function stop() {
ball.removeEventListener("click", funk);
};
function funktre() {
let idd = null;
let pos = 0;
clearInterval(idd);
idd = setInterval(funkfire, 5);
function funkfire() {
if (pos == 200) {
clearInterval(idd);
// ny funksjon her
}
else {
pos++;
ball.style.marginTop = pos + "px";
}
}
}
"I want the 2nd animation, funktre, to move the ball from the funk() ending position."
Then you should not initialize its starting position at zero, but at its current position.
function funktre() {
let idd = null;
let pos = parseInt(ball.style.marginTop); // set the initial pos to current pos
clearInterval(idd);
idd = setInterval(funkfire, 5);
function funkfire() {
if (pos == 200) {
clearInterval(idd);
// ny funksjon her
}
else {
pos++;
ball.style.marginTop = pos + "px";
}
}
}
If you intend to program more animations, or even a video game, I would suggest storing the ball top/left position in a global variable (which you could access from any scope).

ragne slider bubble fails to move with slider movement

I have created a range slider where I am displaying a bubble with slider value within it.
( This example is based on https://css-tricks.com/value-bubbles-for-range-inputs/, I am simply attempted to recreate based on JS)
Here is a function that is suppose to make the bubble move along with the slider:
function displayValue(event) {
var rangeInput = document.getElementById('myRange');
var width = rangeInput.offsetWidth;
var min = rangeInput.getAttribute('min');
var max = rangeInput.getAttribute('max');
var newPoint = (event.target.value - min)/(max - min);
var offset = -1.3;
var newPlace;
if (newPoint < 0) {
newPlace = 0;
} else if (newPoint > 1 ) {
newPlace = width;
} else {
newPlace = width * newPoint + offset;
offset -= newPoint;
}
var outputElement = document.getElementById('myOutput');
outputElement.value = event.target.value;
outputElement.style.left = newPlace;
outputElement.style.marginLeft = offset = "%";
}
This function only partially works (as in the value insider the bubble updates but not the position)
Why is the sldier failing to update it's position ?
http://plnkr.co/edit/lt99U0uvMkPsibY54GAU
I found couple of anomalies in your code which might be typos.
Instead of:
outputElement.style.left = newPlace;
outputElement.style.marginLeft = offset = "%";
Try:
outputElement.style.left = newPlace + 'px';
outputElement.style.marginLeft = offset + "%";

Move absolute positioned divs off eachoter

I'm attempting to output on a page multiple 'labels' over an image using absolute positioned divs. Each of these divs has a unique number and are placed according to an x and y position on the map (these are percentage based so the image may be scaled).
As some of these labels may overlap, I need a way to either stop them from overlapping, or to essentially 'bump' them off eachother so they no longer overlap. (At this point, it doesn't matter if they are not in their correct position as long as they are near enough as there is a separate 'Pin' view).
They need to stay within the confines of their container and not overlap with eachother.
HTML:
<div id="labelzone">
<div class="label" style="left:0%;top:8%">001</div>
<div class="label" style="left:0%;top:11%">002</div>
<div class="label" style="left:1%;top:10%">003</div>
</div>
CSS:
#labelzone{
float:left;
width:500px;
height:500px;
border: 1px solid black;
position: relative;
}
.label{
position:absolute;
border:1px solid black;
background-color:white;
}
Jsfiddle: https://jsfiddle.net/79cco1oy/
There's a simple example of what I have as an output, these pins could be placed anywhere and there is no limit to how many is on the page, however there shouldn't be any occasion where there are too many to fit in the area.
I'm toying around with doing some form of collision detection and currently attempting to figure out an algorithm of some sort to get them to no longer overlap, and ensure they also don't overlap another item.
My solution is a bit more object oriented.
One object (LabelPool) will contain labels and will be in charge of storing and accomodating them so that they don't collide. You can customize the x/y values that you want to add/substract of the Label's positions in order to avoid their collision. The other object (Label) defines a Label and has some convenient methods. The collision algorithm that I used in LabelPool was taken from this post
var Label = function ($el) {
var position = $el.position(),
width = $el.outerWidth(true),
height = $el.outerHeight(true);
this.getRect = function () {
return {
x: position.left,
y: position.top,
width: width,
height: height
};
};
this.modifyPos = function (modX, modY) {
position.top += modY;
position.left += modX;
updatePos();
};
function updatePos() {
$el.css({
top: position.top,
left: position.left
});
}
};
var LabelPool = function () {
var labelPool = [];
function collides(a, b) {
return !(((a.y + a.height) < (b.y)) || (a.y > (b.y + b.height)) || ((a.x + a.width) < b.x) || (a.x > (b.x + b.width)));
}
function overlaps(label) {
var a = label.getRect();
return labelPool.some(function (other) {
return collides(a, other.getRect());
});
}
this.accomodate = function (label) {
while (labelPool.length && overlaps(label)) {
label.modifyPos(0, 1);// You can modify these values as you please.
}
labelPool.push(label);
};
};
var labelPool = new LabelPool;
$(".label").each(function (_, el) {
labelPool.accomodate(new Label($(el)));
});
Here's the fiddle.
Hope it helps.
Using js and jquery, you can find a basic collision engine based on left/top abs position and size of the label.
https://jsfiddle.net/Marcassin/79cco1oy/6/
Every time you want to add a Label, you check if the positionning is overlaping any existing div, in this case, you translate the new Label to position. This operation may not be the most beautiful you can find, there can be a long process time in case of lots of labels.
$(document).ready (function () {
addLabel (0, 8);
addLabel (0, 11);
addLabel (1, 10);
addLabel (2, 7);
});
function addLabel (newLeft, newTop)
{
var newLab = document.createElement ("div");
newLab.className = "label";
$(newLab).css({"left": newLeft+"%", "top": newTop + "%"});
var labels = $("#labelzone > div");
newLab.innerHTML = "00" + (labels.length + 1); // manage 0s
$("#labelzone").append (newLab);
var isCollision = false;
var cpt = 1;
do
{
isCollision = false;
$(labels).each (function () {
if (! isCollision && collision (this, newLab))
isCollision = true;
});
if (isCollision)
$(newLab).css({"left": (newLeft + cpt++) + "%",
"top": (newTop + cpt++) + "%"});
} while (isCollision);
}
function isInside (pt, div)
{
var x = parseInt($(div).css("left"));
var y = parseInt($(div).css("top"));
var w = $(div).width () + borderWidth;
var h = $(div).height ();
if (pt[0] >= x && pt[0] <= x + w &&
pt[1] >= y && pt[1] <= y + h)
return true;
return false;
}
function collision (div1, div2)
{
var x = parseInt($(div1).css("left"));
var y = parseInt($(div1).css("top"));
var w = $(div1).width () + borderWidth;
var h = $(div1).height ();
var pos = [x, y];
if (isInside (pos, div2))
return true;
pos = [x + w, y];
if (isInside (pos, div2))
return true;
pos = [x + w, y + h];
if (isInside (pos, div2))
return true;
pos = [x, y + h];
if (isInside (pos, div2))
return true;
return false;
}
Here's another implementation of collision detection close to what you asked for. The two main goals being:
move vertically more than horizontally (because boxes are wider than tall)
stay within a reasonable range from the origin
Here goes:
function yCollision($elem) {
var $result = null;
$('.label').each(function() {
var $candidate = $(this);
if (!$candidate.is($elem) &&
$candidate.position().top <= $elem.position().top + $elem.outerHeight() &&
$candidate.position().top + $candidate.outerHeight() >= $elem.position().top) {
$result = $candidate;
console.log("BUMP Y");
}
});
return $result;
}
function xCollision($elem) {
var $result = null;
$('.label').each(function() {
$candidate = $(this);
if (!$candidate.is($elem) &&
yCollision($elem) &&
yCollision($elem).is($candidate) &&
$candidate.position().left <= $elem.position().left + $elem.outerWidth() &&
$candidate.position().left + $candidate.outerWidth() >= $elem.position().left) {
$result = $candidate;
console.log("BUMP X");
}
});
return $result;
}
function fuzzyMoveY($elem, direction) {
var newTop = $elem.position().top + $elem.outerHeight() / 4 * direction;
// stay in the canvas - top border
newTop = (newTop < 0 ? 0 : newTop);
// stay in the canvas - bottom border
newTop = (newTop + $elem.outerHeight() > $("#labelzone").outerHeight() ? $("#labelzone").outerHeight() - $elem.outerHeight() : newTop);
// stay close to our origin
newTop = (Math.abs(newTop - $elem.attr("data-origin-top")) > $elem.outerHeight() ? $elem.attr("data-origin-top") : newTop);
$elem.css({'top': newTop});
}
function fuzzyMoveX($elem, direction) {
var newLeft = $elem.position().left + $elem.outerWidth() / 4 * direction;
// stay in the canvas - left border
newLeft = (newLeft < 0 ? 0 : newLeft);
// stay in the canvas - right border
newLeft = (newLeft + $elem.outerWidth() > $("#labelzone").outerWidth() ? $("#labelzone").outerWidth() - $elem.outerWidth() : newLeft);
// stay close to our origin
newLeft = (Math.abs(newLeft - $elem.attr("data-origin-left")) > $elem.outerWidth() ? $elem.attr("data-origin-left") : newLeft);
$elem.css({'left': newLeft});
}
function bumpY($above, $below) {
if ($above.position().top > $below.position().top) {
$buff = $above;
$above = $below;
$below = $buff;
}
fuzzyMoveY($above, -1);
fuzzyMoveY($below, 1);
}
function bumpX($left, $right) {
if ($left.position().left > $right.position().left) {
$buff = $right;
$right = $left;
$left = $buff;
}
fuzzyMoveX($left, 1);
fuzzyMoveX($right, -1);
}
$('.label').each(function() {
$(this).attr('data-origin-left', $(this).position().left);
$(this).attr('data-origin-top', $(this).position().top);
});
var yShallPass = true;
var loopCount = 0;
while (yShallPass && loopCount < 10) {
yShallPass = false;
$('.label').each(function() {
var $this = $(this);
$collider = yCollision($this);
if ($collider) {
bumpY($this, $collider);
yShallPass = true;
}
});
loopCount++;
}
console.log("y loops", loopCount);
var xShallPass = true;
var loopCount = 0;
while (xShallPass && loopCount < 10) {
xShallPass = false;
$('.label').each(function() {
var $this = $(this);
$collider = xCollision($this);
if ($collider) {
bumpX($this, $collider);
xShallPass = true;
}
});
loopCount++;
}
console.log("x loops", loopCount);
This is not production code obviously but please report back if it helps.

javascript toggle animation issue

I am doing a small javascript animation. this is my code :
window.onload = function () {
var heading = document.getElementsByTagName('h1')[0];
heading.onclick = function () {
var divHeight = 250;
var speed = 10;
var myInterval = 0;
alert(divHeight);
slide();
function slide() {
if (divHeight == 250) {
myInterval = setInterval(slideUp, 30);
} else {
myInterval = setInterval(slideDwn, 30);
alert('i am called as slide down')
}
}
function slideUp() {
var anima = document.getElementById('anima');
if (divHeight <= 0) {
divHeight = 0;
anima.style.height = '0px';
clearInterval(myInterval);
} else {
divHeight -= speed;
if (divHeight < 0) divHeight = 0;
anima.style.height = divHeight + 'px';
}
}
function slideDwn() {
var anima = document.getElementById('anima');
if (divHeight >= 250) {
divHeight = 250;
clearInterval(myInterval);
} else {
divHeight += speed;
anima.style.height = divHeight + 'px';
}
}
}
}
i am using above code for simple animation. i need to get the result 250 on the first click, as well second click i has to get 0 value. but it showing the 250 with unchanged. but i am assigning the value to set '0', once the div height reached to '0'.
what is the issue with my code? any one help me?
Everytime you click on the div the divHeight variable is reset to 250, thus your code never calls slideDwn. Moving the divHeight declaration outside the event handler should do the trick.
Also, your div wont have the correct size when any of the 2 animations end. You're setting the divHeight variable to 250 or 0 correctly, but never actually setting anima.style.height after that.
I've rewritten your code into something simpler and lighter. The main difference here is that we're using a single slide() function here, and that the height of the div in question is stored in a variable beforehand to ensure that the element slides into the correct position.
Note that this is a very simplistic implementation and assumes that the div carries no padding. (The code uses ele.clientHeight and ele.style.height interchangeably, which admittedly, is a pretty bad choice, but is done here to keep the code simple)
var heading = document.getElementsByTagName('h1')[0],
anima = document.getElementById('anima'),
divHeight = anima.clientHeight,
speed = 10,
myInterval = 0,
animating = false;
function slide(speed, goal) {
if(Math.abs(anima.clientHeight - goal) <= speed){
anima.style.height = goal + 'px';
animating = false;
clearInterval(myInterval);
} else if(anima.clientHeight - goal > 0){
anima.style.height = (anima.clientHeight - speed) + 'px';
} else {
anima.style.height = (anima.clientHeight + speed) + 'px';
}
}
heading.onclick = function() {
if(!animating) {
animating = true;
var goal = (anima.clientHeight >= divHeight) ? 0 : divHeight;
myInterval = setInterval(slide, 13, speed, goal);
}
}
See http://www.jsfiddle.net/yijiang/dWJgG/2/ for a simple demo.
I've corrected your code a bit (See working demo)
window.onload = function () {
var heading = document.getElementsByTagName('h1')[0];
var anima = document.getElementById('anima');
var divHeight = 250;
heading.onclick = function () {
var speed = 10;
var myInterval = 0;
function slideUp() {
divHeight -= speed;
if (divHeight <= 0) {
divHeight = 0;
clearInterval(myInterval);
}
anima.style.height = divHeight + 'px';
}
function slideDwn() {
divHeight += speed;
if (divHeight >= 250) {
divHeight = 250;
clearInterval(myInterval);
}
anima.style.height = divHeight + 'px';
}
function slide() {
console.log(divHeight )
if (divHeight == 250) {
myInterval = setInterval(slideUp, 30);
} else {
myInterval = setInterval(slideDwn, 30);
}
}
slide();
}
}​

Categories