Javascript/CSS: How to start animation from cursor position - javascript

I've already asked this question but it did not solve my problem (JavaScript / CSS - How can I start an animation, creating new Divs from cursor position?).
I've found a solution but it doesn't seem to work properly on Mozilla and Opera. Also, when I click multiple times, the div starts flashing, which is not what I want. I need to have more than one div at the same time, so to have multiple animations simultaneously. Here's an example from an existing game: http://www.cram.com/flashcards/games/stellar-speller/gre-hit-parade-group-1-320949 Please don't use jQuery, just normal Javascript. Thank you!
<!DOCTYPE html>
<html>
<head>
<style>
#table{
height:300px;
width:900px;
background:red;
}
#ball {
width: 25px;
height: 25px;
position: absolute;
background-color: white;
border-radius:50px;
display:none;
z-index:11;
}
</style>
<script> function animation(){ var elem =
document.getElementById("ball");
document.getElementById("ball").style.display ='block';
document.getElementById("ball").disabled = true;
var x = event.clientX;
var y = event.clientY;
var id = setInterval(frame, 2);
function frame() {
if (x == 900) {
clearInterval(id);
}
else {
x++;
elem.style.top = y + 'px';
elem.style.left = x + 'px';
}
}
}
</script>
</head>
<body>
<div id="table" onclick="animation()">
<div id="ball"></div>
</body>
</html>

few issues in your code. First you have to clone or create a new ball each time you click (be aware that cloning an element won't copy attached events). Don't mix HTML and Javascript and use addEventListener, which let you have event as argument
document.getElementById('table').addEventListener("click",animation);
function animation(event){
var ball = document.getElementById("ball");
//clone element or create another one (you should set a class in you css)
var elem = ball.cloneNode(true);
document.getElementById("table").appendChild(elem);
elem.style.display ='block';
// document.getElementById("ball").disabled = true;
var x = event.clientX;
var y = event.clientY;
var id = setInterval(frame, 2);
function frame() {
if (x == 900) {
clearInterval(id);
}
else
{
x++;
elem.style.top = y + 'px';
elem.style.left = x + 'px';
}
}
}
#table{
height:300px;
width:900px;
background:red;
}
#ball {
width: 25px;
height: 25px;
position: absolute;
background-color: white;
border-radius:50px;
display:none;
z-index:11;
}
<div id="table">
<div id="ball"></div>
</div>

You're reusing the same #ball element for every animation, it can't be in two places at once. To create a new element each time, use document.createElement. Then you'll have to place that element somewhere with appendChild, and remove it with removeChild when the animation's finished.
And some smaller things:
If you will have more than one ball in the document, use a class instead of an ID - IDs are only supposed to be used once.
Your <div>s aren't closed with </div> in the HTML, which is probably Firefox's problem. Do people still use Opera?
You should use requestAnimationFrame here instead of setInterval.
And you should try to use addEventListener instead of onclick.
Good luck!

Related

Trying to get the element to appear once after clicking, but appears more than once

Im trying to get the shapes to appear once after clicking. After 1.2 seconds it appears, then when clicked it disappears, then the whole process repeats. The problem is after a few clicks two shapes appear.
This is the link to the program.
https://jsfiddle.net/EyedFox/w98naLjx/2/
function show() {
var randomColor = colors[Math.floor(colors.length * Math.random())]; // Random color chosen
var randomX = Math.floor(Math.random() * 60); // random x axis margin
var randomY = Math.floor(Math.random() * 80); // random y axis margin
square = document.getElementById("square");
circle = document.getElementById("circle");
var shapeArray = [square, circle];
var randShape = shapeArray[Math.floor(shapeArray.length * Math.random())];
randShape.style.margin = randomX + "% " + randomY + "%";
randShape.style.backgroundColor = randomColor;
randShape.style.display = "block";
randShape.addEventListener("click", click);
function click() {
randShape.style.display = "none";
setTimeout(show, 1200);
}
}
setTimeout(show, 1200);
Your code keep adding click event listener to randShape, but it never cleans them those event. As a result, they keep being added and click (and thus show) will get executed several times per click. Try to add console.log('clicked on', randShape); at the beginning of your click function to see it. If by chance all the show execution selects the same shape, you'll see only this one on screen, but otherwise you'll get both.
Your click function should look like this:
function click() {
console.log('clicked on', randShape);
randShape.style.display = "none";
setTimeout(show, 1200);
randShape.removeEventListener('click', click); // cleaning after myself ;-)
}
(btw (function() { ... })() is not executed on load, but as soon as it is encountered by the javascript vm)
Here is a fiddle that fixes the issue.
Edited my response based on #autra response
I would probably completely clean the canvas before redrawing:
https://jsfiddle.net/c0L54uk3/
Essentially made a reset function like so:
function reset(){
square = document.getElementById("square").style.display = "none";
circle = document.getElementById("circle").style.display = "none";
randShape.removeEventListener("click", click);
}
And called it at the beginning of the click() function to reset the canvas like so:
function click() {
reset();
setTimeout(show, 1200);
}
Hope that helps.
Delegate Events Instead of Registering Them Individually
addEventListener() should be outside the callback function (i.e. show()). There's an event listener being added to the clicked tag but none of them are being removed. So when the 4th to 7th click occurs, the browser starts slowing down and that setTimeout is never 1.2 seconds because it will be cued to fire off which means it'll wait however long then wait 1.2 seconds. As the cue accumulates, the clicks will out race the setTimeout.
Register the parent tag, .container to listen to the click event event for all of its children tags. This pattern is called Event Delegation. In doing so, you can add the click event to an unlimited number of tags within .container using only one event listener instead of one for each shape.
The #triangle was never styled so it's an extra invisible div that floats around. In the demo I have styled the #triangle and added a .blank <div> outlined in gold (you can remove the .blank).
Demo
Fiddle
Details commented in demo
// Reference parent tag
var box = document.querySelector('.container');
function show() {
// Collect all .shape into a NodeList and convert into an array
var shapes = Array.from(document.querySelectorAll('.shape'));
var colors = ["yellow", "red", "green", "purple", "aqua", "chartreuse", "coral", "dodgerBlue", "deepPink"];
var rColor = colors[Math.floor(colors.length * Math.random())];
var rX = Math.floor(Math.random() * 60);
var rY = Math.floor(Math.random() * 80);
var rShape = shapes[Math.floor(shapes.length * Math.random())];
rShape.style.margin = rX + "% " + rY + "%";
// if #triangle change the border-bottom-color
if (rShape.id === 'triangle') {
rShape.style.borderBottomColor = rColor;
// otherwise change background-color
} else {
rShape.style.backgroundColor = rColor;
}
rShape.style.display = "block";
}
/*
Run show()
A setTimeout nor an IIFE is needed to initially run show()
Simply call it.
*/
show();
// Register box to click event...
box.addEventListener('click', function(e) {
// ...reference the clicked tag (i.e. .shape)...
var tgt = e.target;
// ...reference the registered parent tag...
var cur = e.currentTarget;
/*
...if the clicked tag IS NOT the registered parent tag...
*/
if (tgt !== cur) {
// ...then hide clicked element...
tgt.style.display = "none";
// ...and run show() in about 1.2 seconds
setTimeout(show(), 1200);
}
});
.container {
overflow: hidden;
height: 800px;
width: 80%;
background-color: rgb(0, 34, 85);
margin: 0 auto;
margin-top: 5em;
}
#square {
width: 80px;
height: 80px;
background-color: red;
margin-left: 0%;
margin-top: 10%;
display: none
}
#circle {
width: 100px;
height: 100px;
background: red;
border-radius: 50px;
display: none
}
#triangle {
width: 0;
height: 0;
border-left: 45px solid transparent;
border-right: 45px solid transparent;
border-bottom: 90px solid cyan;
display: none
}
.shape {
cursor: pointer
}
.blank {
outline: 10px solid gold
}
<div class="container">
<div id="square" class='shape'></div>
<div id="triangle" class='shape'></div>
<!--
This .blank represents what #triangle was before it
had any style.
-->
<div class='blank'></div>
<div id="circle" class='shape'></div>
</div>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css" />

Using JavaScript Animation to Move Rectangle With Arrow Keys

I'm trying to move a rectangle in a canvas element, and haven't had any luck. This is my code so far. I want to do it in PURE JavaScript, not jQuery or any other library.
My JavaScript:
window.onload = beginningMovement; // load beginning movement
function beginningMovement() {
var elem = document.getElementById("rectangle");
var pos = 0;
var id = setInterval(frame, 8);
function frame() {
if (pos == 203) {
clearInterval(id);
//Color funcs
setTimeout(black, 0);
setTimeout(lightgray, 500);
setTimeout(black, 1000);
setTimeout(lightgray, 1500);
setTimeout(black, 2000);
setTimeout(lightgray, 2500);
function black() {
var color = document.getElementById('container').style.backgroundColor = "black";
}
function lightgray() {
var color = document.getElementById('container').style.backgroundColor = "lightgray";
}
//End of color funcs
} else {
pos++;
elem.style.top = pos + 'px';
elem.style.middle = pos + 'px';
}
}
}
//Arrow switching
var X = 40;
var Y = 20;
function switchKey(event) {
Key = event.keyCode;
myCanvas.fillstyle = "white";
myCanvas.filRect(X, Y, 50, 50);
}
switch(key) {
case 37: X -=5;
break;
case 37: Y -=5;
break;
case 37: X +=5;
break;
case 37: Y +=5;
}
myCanvas.fillstyle = "blue";
myCanvas.filRect(X, Y, 50, 50);
HTML:
<!DOCTYPE html>
<html>
<head>
<title></title>
<style type="text/css">
#container {
width: 450px;
height: 400px;
border: 5px solid black;
background: lightgray;
}
#rectangle {
height: 50px;
width: 150px;
background: cyan;
margin-left: 150px;
margin-top: 160px;
position: absolute;
}
</style>
</head>
<body onkeyup="switchKey(event)">
<div id="container">
<div id="rectangle">
</div>
</div>
</body>
</html>
I've been at this for hours and haven't had any luck. I would appreciate it if someone could help me, and get this done soon.
There were a lot of issues with you code and I eventually thought it would be easier to remake a simple version for you to look at. If you really want to stick with your version, please check any browser console for errors and you'll see the issues I'm seeing. Some functions aren't yet available onload, there is no key variable, yet there is a Key variable. There were other issues, too, regarding variable and function scope.
Here's a new version for you to look at: http://codepen.io/antibland/pen/ONwEBE
It's not my finest work, but it's a step in the right direction for you. Instead of setInterval, you should be using requestAnimationFrame to do the redraw. It's much more performant (more on that here if you're interested).
In my demo, a real <canvas> element is used. The smaller rectangle is added to it. I figured you intended to use the real canvas since you included a myCanvas variable in your code. If not, apologies. Either way, you'll probably want to add bounds checking in there, to keep the small rectangle from moving beyond the walls.
Good luck!

Draggable Columns With Pure JavaScript

I'm trying to build a draggable column based layout in JavaScript and having a bit of hard time with it.
The layout comprises of 3 columns (divs), with two dragable divs splitting each. The idea is that they are positioned absolutely and as you drag the draggers, the columns' respective widths, and left values are updated.
The three columns should always span the full width of the browser (the right most column is 100% width), but the other two should remain static by default when the browser is resized (which is why i'm using px, not %).
My code isn't working as of yet, I'm relatively new to JavaScript (which is why I don't want to use jQuery).
Having said that, there must be a more efficient (and cleaner) way of achieving this with less code that works (without reaching for the $ key).
If anyone with some awesome JS skills can help me out on this I'd be super-appreciative.
Here's the fiddle I'm working on http://jsfiddle.net/ZFwz5/3/
And here's the code:
HTML
<!-- colums -->
<div class="col colA"></div>
<div class="col colB"></div>
<div class="col colC"></div>
<!-- draggers -->
<div class="drag dragA" style="position: absolute; width: 0px; height: 100%; cursor: col-resize; left:100px;"><div></div></div>
<div class="drag dragB" style="position: absolute; width: 0px; height: 100%; cursor: col-resize; left: 300px;"><div></div></div>
CSS:
body {
overflow:hidden;
}
.col {
position: absolute;
height:100%;
left: 0;
top: 0;
overflow: hidden;
}
.colA {background:red;width:100px;}
.colB {background:green; width:200px; left:100px;}
.colC {background:blue; width:100%; left:300px;}
.drag > div {
background: 0 0;
position: absolute;
width: 10px;
height: 100%;
cursor: col-resize;
left: -5px;
}
and my terrible JavaScript:
//variabe columns
var colA = document.querySelector('.colA');
var colB = document.querySelector('.colB');
var colC = document.querySelector('.colC');
//variable draggers
var draggers = document.querySelectorAll('.drag');
var dragA = document.querySelector(".dragA");
var dragB = document.querySelector(".dragB");
var dragging = false;
function drag() {
var dragLoop;
var t = this;
var max;
var min;
if (dragging = true) {
if (this == dragA) {
min = 0;
max = dragB.style.left;
} else {
min = dragA.style.left;
max = window.innerWidth;
}
dragLoop = setInterval(function () {
var mouseX = event.clientX;
var mouseY = event.clientY;
if (mouseX >= max) {
mouseX = max;
}
if (mouseY <= min) {
mouseY = min;
}
t.style.left = mouseX;
updateLayout();
}, 200);
}
}
function updateLayout() {
var posA = dragA.style.left;
var posB = dragB.style.left;
colB.style.paddingRight = 0;
colA.style.width = posA;
colB.style.left = posA;
colB.style.width = posB - posA;
colC.style.left = posB;
colC.style.width = window.innerWidth - posB;
}
for (var i = 0; i < draggers.length; i++) {
draggers[i].addEventListener('mousedown', function () {
dragging = true;
});
draggers[i].addEventListener('mouseup', function () {
clearInterval(dragLoop);
dragging = false;
});
draggers[i].addEventListener('mouseMove', function () {
updateLayout();
drag();
});
}
I see a couple of things wrong here. First of all, the mousemove event only fires on an element when the mouse is over that element. You might have better luck registering a mousemove listener on the parent of your div.drag elements, then calculating the mouse's position inside that parent whenever a mouse event happens, then using that position to resize your columns and your draggers.
Second, I'm not quite sure what you're trying to do by registering a function with setInterval. You're doing pretty well with registering event listeners; why not continue to use them to change the state of your DOM? Why switch to a polling-based mechanism? (and the function you pass to setInterval won't work anyway - it refers to a variable named event, which in that context is undefined.)
This is just a little example... I hope it can help you :)
window.onload = function() {
var myDiv = document.getElementById('myDiv');
function show_coords(){
var monitor = document.getElementById('monitor');
var x = event.clientX - myDiv.clientWidth / 2;
var y = event.clientY - myDiv.clientWidth / 2;
monitor.innerText = "X: " + x + "\n" + "Y: " + y;
myDiv.style.left = x + "px";
myDiv.style.top = y + "px";
}
document.onmousemove = function(){
if(myDiv.innerText == "YES"){show_coords();}
}
myDiv.onmousedown = function(){
myDiv.innerText = "YES";
}
myDiv.onmouseup = function(){
myDiv.innerText = "NO";
}
}

Making a 'div' go in a circle

I am working on a bit of code to have a dot circle around your cursor on a webpage, but I am having trouble getting the 'div' to follow the path I want; in fact, the object is not moving at all and I cannot figure out why my code does not work. Here is the code that is causing me trouble from what I've narrowed down:
<!DOCTYPE HTML>
<head>
<title>TEST SPACE</title>
<script src="jquery-1.10.2.min.js"></script>
</head>
<body>
<div id="test"></div>
<style>
#test {
background-color: black;
height: 10px;
width: 10px;
border-radius: 100%;
position: absolute;
top: 50%;
left: 50%;
}
</style>
<script>
const omega = Math.PI / 2000;
function dotRotation() {
var time = 0;
var x = 20*(Math.sin(omega*time));
var y = 20*(Math.cos(omega*time));
document.getElementById("test").style.marginLeft = x;
document.getElementById("test").style.marginTop = y;
time += 25;
};
$(document).ready(function() {
setInterval('dotRotation()',25);
});
</script>
</body>
JSFiddle
Two things are wrong
You need to move your time variable outside of the function
You need to give a unit to the value you pass to the margins so add +'px' after the variables .marginTop = y + 'px';
So altogether
const omega = Math.PI / 2000;
var time = 0;
function dotRotation() {
var x = 20*(Math.sin(omega*time));
var y = 20*(Math.cos(omega*time));
var style = document.getElementById("test").style;
style.marginLeft = x +'px';
style.marginTop = y +'px';
time += 25;
};
Demo at http://jsfiddle.net/mWC63/
Also you can cache references to the dom to avoid the searching
Your fiddle is set to run onload, it should be set up to run in the head. [drop down on left under jQuery selection.]
time is declared inside of the function so it is reset back to zero every single time it is called. Move it outside of dotRotation
var time = 0;
function dotRotation() {
var x = 20*(Math.sin(omega*time));
var y = 20*(Math.cos(omega*time));
$("#test").css({"marginLeft" : x + "px", "marginTop" : y + "px"});
time += 25;
};
$(function() {
setInterval(dotRotation,25);
});

How to compare positions of two elements in javascript

I have been trying to make this work since the beginning of this week but couldn't figure it out. I have two elements in my HTML file. They are on the same vertical lane, element1 is moving 60ems to the left horizontally with each button click. I have to compare the positions of these two elements and detect whether element1 has passed the element2 with an if/else statement. When it passes I want to move the element1 to its initial position and start over. But if statement never shoots, it always jumps to else statement.
I have tried comparing following properties:
$('#elemet1').position();
$('#elemet1').position().left;
$('#elemet1').offset();
$('#elemet1').offsetLeft;
$('#elemet1').css("position");
$('#elemet1').css("left");
$('#elemet1').css("margin");
$('#elemet1').left;
I tried to compare the positions like below:
$(function () {
$("button").click(function () {
var position1 = $('#element1').css("left");
var position2 = $('#element2').css("left");
if (position1 <= position2) {
$("#element1").animate({left:'+=240em'}, 600);
}
else {
$("#element1").animate({left:'-=60em'}, 600);
}
});
});
If you can give me an answer or direct me to another post I will appreciate it. Thank you in advance.
EDIT: The style I use is below:
.element2 {
position: relative;
width: 60em;
height: 40em;
bottom: 8em;
background: rgba(242, 210, 139, 0.6);
border-radius: 1.2em;
z-index: 1;
overflow: hidden;
#element1 {
position: absolute;
margin-left: 180em;
width: 60em;
height: 40em;
z-index: 1;
}
And I should have mentioned this at the beginning: element1 is inside element2:
<div class="element2">
<img id="element1" src="image/bg1.png" />
</div>
This will give you the current position of an element:
$('#element').position().left;
If you need position relative to the document (instead of relative to a parent element) you can use:
$('#element').offset().left;
You should just be able to compare the return values of these for the different elements directly. Here's some sample code that works for me, note there is no need to parse since they always return pixel values.
var pos = Math.max($(w).scrollTop(), 38),
top = $(ballon).position().top,
dist = Math.abs(pos-top),
time = dist > 1000 ? 2000 : dist / 0.15;
if (pos + $(w).height() >= pHeight) {
pos = pHeight - bHeight - gHeight;
$(ballon).animate({'top': pos}, time, 'easeInOutSine');
}
else if (dist > 150 || pos < 100) {
$(ballon).animate({'top': pos}, time, 'easeInOutBack');
}

Categories