What my blue fish should do: Move around to random positions on the screen and when you touch it with the mouse, it should flee to a random location fast, then continue swimming.
What my blue fish does: After fleeing, it warps to a random location.
How can I fix the warping, so that the fish continues swimming from its current position after fleeing?
/*globals $:false, window:false*/
$("#blueFishId").animate({}, function() {
blueFish(this)
});
function blueFish(IdRef) {
var x = Math.floor(Math.random() * ($(window).width() - $(IdRef).width()))
var y = Math.floor(Math.random() * ($(window).height() - $(IdRef).height()))
$(IdRef).delay(500).animate({
top: y,
left: x
}, 5000, function() {
blueFish(IdRef);
});
}
$("#blueFishId").on("mouseenter", function() {
var x = Math.floor(Math.random() * ($(window).width() - $("#blueFishId").width()))
var y = Math.floor(Math.random() * ($(window).height() - $("#blueFishId").height()))
$("#blueFishId").animate({
top: y,
left: x
}, 1000);
});
img {
position: relative;
}
#blueFishId {
height: auto;
width: auto;
position: relative;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<img class="blueFish" id="blueFishId" src="images/fish2.png">
To fix this you need to clear the queued animation on mouseover and you also need to stop the current animation that's executing. You can achieve this by calling stop() and also putting the delay() call after the animate(). Also note that you can DRY up the logic by just calling your function from within mouseenter after you stop() the current animation. Try this:
var $fish = $('#blueFishId');
function moveFish($fish) {
var x = Math.floor(Math.random() * ($(window).width() - $fish.width()))
var y = Math.floor(Math.random() * ($(window).height() - $fish.height()))
$fish.animate({
top: y,
left: x
}, 5000, function() {
moveFish($fish);
}).delay(500);
}
$fish.on("mouseenter", function() {
$fish.stop(true);
moveFish($fish);
});
moveFish($fish);
img {
position: relative;
}
#blueFishId {
height: auto;
width: auto;
position: relative;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<img class="blueFish" id="blueFishId" src="images/fish2.png">
You may also want to consider making the fish move a greater distance on mouseenter to make it look more like it's 'fleeing' the mouse.
Related
I could find similar questions involving jQuery UI lib, or only css with no handle to drag, but nothing with pure maths.
What I try to perform is to have a resizable and rotatable div. So far so easy and I could do it.
But it gets more complicate when rotated, the resize handle does calculation in opposite way: it decreases the size instead of increasing when dragging away from shape.
Apart from the calculation, I would like to be able to change the cursor of the resize handle according to the rotation to always make sense.
For that I was thinking to detect which quadrant is the resize handle in and apply a class to change cursor via css.
I don't want to reinvent the wheel, but I want to have a lightweight code and simple UI. So my requirement is jQuery but nothing else. no jQuery UI.
I could develop until achieving this but it's getting too mathematical for me now.. I am quite stuck that's why I need your help to detect when the rotation is enough to have the calculation reversed.
Eventually I am looking for UX improvement if anyone has an idea or better examples to show me!
Here is my code and a Codepen to try: http://codepen.io/anon/pen/rrAWJA
<html>
<head>
<style>
html, body {height: 100%;}
#square {
width: 100px;
height: 100px;
margin: 20% auto;
background: orange;
position: relative;
}
.handle * {
position: absolute;
width: 20px;
height: 20px;
background: turquoise;
border-radius: 20px;
}
.resize {
bottom: -10px;
right: -10px;
cursor: nwse-resize;
}
.rotate {
top: -10px;
right: -10px;
cursor: alias;
}
</style>
<script type="text/javascript" src="js/jquery.js"></script>
<script>
$(document).ready(function()
{
new resizeRotate('#square');
});
var resizeRotate = function(targetElement)
{
var self = this;
self.target = $(targetElement);
self.handles = $('<div class="handle"><div class="resize" data-position="bottom-right"></div><div class="rotate"></div></div>');
self.currentRotation = 0;
self.positions = ['bottom-right', 'bottom-left', 'top-left', 'top-right'];
self.bindEvents = function()
{
self.handles
//=============================== Resize ==============================//
.on('mousedown', '.resize', function(e)
{
// Attach mouse move event only when first clicked.
$(document).on('mousemove', function(e)
{
var topLeft = self.target.offset(),
bottomRight = {x: topLeft.left + self.target.width(), y: topLeft.top + self.target.height()},
delta = {x: e.pageX - bottomRight.x, y: e.pageY - bottomRight.y};
self.target.css({width: '+=' + delta.x, height: '+=' + delta.y});
})
.one('mouseup', function(e)
{
// When releasing handle, round up width and height values :)
self.target.css({width: parseInt(self.target.width()), height: parseInt(self.target.height())});
$(document).off('mousemove');
});
})
//============================== Rotate ===============================//
.on('mousedown', '.rotate', function(e)
{
// Attach mouse move event only when first clicked.
$(document).on('mousemove', function(e)
{
var topLeft = self.target.offset(),
center = {x: topLeft.left + self.target.width() / 2, y: topLeft.top + self.target.height() / 2},
rad = Math.atan2(e.pageX - center.x, e.pageY - center.y),
deg = (rad * (180 / Math.PI) * -1) + 135;
self.currentRotation = deg;
// console.log(rad, deg);
self.target.css({transform: 'rotate(' + (deg)+ 'deg)'});
})
.one('mouseup', function(e)
{
$(document).off('mousemove');
// console.log(self.positions[parseInt(self.currentRotation/90-45)]);
$('.handle.resize').attr('data-position', self.positions[parseInt(self.currentRotation/90-45)]);
});
});
};
self.init = function()
{
self.bindEvents();
self.target.append(self.handles.clone(true));
}();
}
</script>
</head>
<body>
<div id="all">
<div id="square"></div>
</div>
</body>
</html>
Thanks for the help!
Here is a modification of your code that achieves what you want:
$(document).ready(function() {
new resizeRotate('#square');
});
var resizeRotate = function(targetElement) {
var self = this;
self.target = $(targetElement);
self.handles = $('<div class="handle"><div class="resize" data-position="bottom-right"></div><div class="rotate"></div></div>');
self.currentRotation = 0;
self.w = parseInt(self.target.width());
self.h = parseInt(self.target.height());
self.positions = ['bottom-right', 'bottom-left', 'top-left', 'top-right'];
self.bindEvents = function() {
self.handles
//=============================== Resize ==============================//
.on('mousedown', '.resize', function(e) {
// Attach mouse move event only when first clicked.
$(document).on('mousemove', function(e) {
var topLeft = self.target.offset();
var centerX = topLeft.left + self.target.width() / 2;
var centerY = topLeft.top + self.target.height() / 2;
var mouseRelativeX = e.pageX - centerX;
var mouseRelativeY = e.pageY - centerY;
//reverse rotation
var rad = self.currentRotation * Math.PI / 180;
var s = Math.sin(rad);
var c = Math.cos(rad);
var mouseLocalX = c * mouseRelativeX + s * mouseRelativeY;
var mouseLocalY = -s * mouseRelativeX + c * mouseRelativeY;
self.w = 2 * mouseLocalX;
self.h = 2 * mouseLocalY;
self.target.css({
width: self.w,
height: self.h
});
})
.one('mouseup', function(e) {
$(document).off('mousemove');
});
})
//============================== Rotate ===============================//
.on('mousedown', '.rotate', function(e) {
// Attach mouse move event only when first clicked.
$(document).on('mousemove', function(e) {
var topLeft = self.target.offset(),
center = {
x: topLeft.left + self.target.width() / 2,
y: topLeft.top + self.target.height() / 2
},
rad = Math.atan2(e.pageX - center.x, center.y - e.pageY) - Math.atan(self.w / self.h),
deg = rad * 180 / Math.PI;
self.currentRotation = deg;
self.target.css({
transform: 'rotate(' + (deg) + 'deg)'
});
})
.one('mouseup', function(e) {
$(document).off('mousemove');
$('.handle.resize').attr('data-position', self.positions[parseInt(self.currentRotation / 90 - 45)]);
});
});
};
self.init = function() {
self.bindEvents();
self.target.append(self.handles.clone(true));
}();
}
The major changes are the following:
In the resize event, the mouse position is transformed to the local coordinate system based on the current rotation. The size is then determined by the position of the mouse in the local system.
The rotate event accounts for the aspect ratio of the box (the - Math.atan(self.w / self.h) part).
If you want to change the cursor based on the current rotation, check the angle of the handle (i.e. self.currentRotation + Math.atan(self.w / self.h) * 180 / Math.PI). E.g. if you have a cursor per quadrant, just check if this value is between 0..90, 90..180 and so on. You may want to check the documentation if and when negative numbers are returned by atan2.
Note: the occasional flickering is caused by the box not being vertically centered.
Nico's answer is mostly correct, but the final result calculation is incorrect -> which is causing the flickering BTW. What is happening is the increase or decrease in size is being doubled by the 2x multiplication. The original half-width should be added to the new half-width to calculate the correct new width and height.
Instead of this:
self.w = 2 * mouseLocalX;
self.h = 2 * mouseLocalY;
It should be this:
self.w = (self.target.width() / 2) + mouseLocalX;
self.h = (self.target.height() / 2) + mouseLocalY;
I don't know how to search for this in google or at least here at stackoverflow. Maybe you guys have a word for this animation to search for or something how to call it.
Anyway, my idea is the following. You have a simple div and when you click on it, from the center of the div there should grow 4 other divs which have a little distance to the clicked div and are placed on a half circle. Maybe the image can help your imagination to follow my idea:
Does anyone of you have an idea how to code this?
Edit: Added Code from Comment, while .squadplayerinfo are the spread divs, .playercard the outer div and .field the outer outer div
function calculateDistance(elem, mouseX, mouseY) {
return Math.floor(Math.sqrt(Math.pow(mouseX - (elem.offset().left + (elem.width() / 2)), 2) + Math.pow(mouseY - (elem.offset().top + (elem.height() / 2)), 2)));
}
$(document).on("mousemove", ".field", function(e) {
var mX, mY, distance,
$element = $(".playercard");
mX = e.pageX;
mY = e.pageY;
distance = calculateDistance($element, mX, mY);
if(distance > 100) {
$(".squadplayerinfo").each(function(index) {
(index == 1 || index == 2) ? radius = 100 : radius = 75;
angle = toRadians(360 / $(".squadplayerinfo").length * (index + 0.5) / 2);
$(this).animate({
opacity: 0,
right: "+=" + calcX(angle),
top: "+=" + calcY(angle)
}, 750, function() {
// done animating
});
});
}
});
I played with the idea for a few minutes and I think this is what you are looking for:
// javascript
var amount = $(".expand").length;
var radius = 200;
$("#trigger").click(function() {
var x, y, angle;
$(".expand").each(function(index) {
angle = toRadians(360 / amount * index);
x = calcX(angle);
y = calcY(angle);
$(this).animate({
right: "-=" + x,
top: "-=" + y
}, 2000, function() {
// done animating
});
});
});
function calcX(angleRadians) {
return Math.floor(radius * Math.cos(angleRadians));
}
function calcY(angleRadians) {
return Math.floor(radius * Math.sin(angleRadians));
}
function toRadians(angleDegrees) {
return angleDegrees * Math.PI / 180;
}
/* css */
.expand {
position: absolute;
top: 50%;
right: 50%;
}
#trigger {
position: absolute;
top: 200px;
right: 200px;
}
<!-- html -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
<button id="trigger">click me</button>
It is dynamically computes the angles based on the amount of divs and the radius can be controlled with ease :)
Hope this helped... Good luck
EDIT
I have noticed you only want your divs to spread on the top half of the screen, so pls change this line:
angle = toRadians(360 / amount * index);
to this one:
angle = toRadians(360 / amount * (index + 0.5) / 2);
EDIT#2
Since HansMartin has edited his post, this is a snippet which will move the div when the mouse will come near the divs (or any other div specified). This snippet is using this answer to check proximity to the div.
// javascript
var amount = $(".expand").length;
var radius = 200;
var first_animation = true;
$('#check-mouse-area').mousemove(function(event) {
if (first_animation) {
if (isNear($(".expand"), 200, event)) {
first_animation = false;
var x, y, angle;
$(".expand").each(function(index) {
angle = toRadians(360 / amount * (index + 0.5) / 2);
x = calcX(angle);
y = calcY(angle);
$(this).animate({
right: "-=" + x,
top: "-=" + y
}, 2000, function() {
// done animating
});
});
}
}
});
function calcX(angleRadians) {
return Math.floor(radius * Math.cos(angleRadians));
}
function calcY(angleRadians) {
return Math.floor(radius * Math.sin(angleRadians));
}
function toRadians(angleDegrees) {
return angleDegrees * Math.PI / 180;
}
function isNear(element, distance, event) {
var left = element.offset().left - distance,
top = element.offset().top - distance,
right = left + element.width() + 2 * distance,
bottom = top + element.height() + 2 * distance,
x = event.pageX,
y = event.pageY;
return (x > left && x < right && y > top && y < bottom);
}
.expand {
position: absolute;
top: 50%;
right: 50%;
}
#check-mouse-area {
position: absolute;
top: 0;
right: 0;
width: 100%;
height: 100%;
}
<!-- html -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="check-mouse-area"></div>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
The main change is from
$("#trigger").click(function () {
to
$('#check-mouse-area').mousemove(function (event) {
...
if (isNear($(".expand"), 200, event)) {
Since the mouse mousemove event is called a lot I added the first animation boolean so that the animation will only fire once.
I'm wanting the plane and rocket to only move approx 5% from their original place when the mouse hits the hero-panel area.
this current code makes both images follow and offset where the mouse position is.
Please assist.
$(document).ready(function () {
$('#hero-panel').mousemove(function (e) {
parallax(e, document.getElementById('plane'), 1);
parallax(e, document.getElementById('rocket'), 2);
});
});
function parallax(e, target, layer) {
var layer_coeff = 10 / layer;
var x = ($(window).width() - target.offsetWidth) / 4 - (e.pageX - ($(window).width() / 4)) / layer_coeff;
var y = ($(window).height() - target.offsetHeight) / 4 - (e.pageY - ($(window).height() / 4)) / layer_coeff;
$(target).offset({ top: y ,left : x });
};
https://jsfiddle.net/jc0807/c5yke2on/
Thanks
Ok, I think I understand what you're looking for:
fiddle
$(document).ready(function () {
var plane = document.getElementById('plane');
var rocket = document.getElementById('rocket');
plane.homePos = { x: plane.offsetLeft, y: plane.offsetTop };
rocket.homePos = { x: rocket.offsetLeft, y: rocket.offsetTop };
$('#hero-panel').mousemove(function (e) {
parallax(e, document.getElementById('plane'), 10);
parallax(e, document.getElementById('rocket'), 20);
});
});
function parallax(e, target, layer) {
var x = target.homePos.x - (e.pageX - target.homePos.x) / layer;
var y = target.homePos.y - (e.pageY - target.homePos.y) / layer;
$(target).offset({ top: y ,left : x });
};
What we're doing here is recording the starting position of the plane and the rocket as a new property 'homePos' on the plane and rocket objects. This makes it easy to apply the parallax effect as an offset from the original positions based on the mouse distance from the object homePos.
If you modify the layer value passed to parallax, the amount of movement will change (we're dividing the mouse offset from the middle of the object's starting position by it, to calculate the new object offset amount).
I guess my question is somehow related to the one above and I didn't want to create a duplicate.
I am using the code bellow to "navigate" into an image on mousemove. The problem is that I cannot manage to make the image fill all the viewable screen area. I've added a red background to the container to show what I mean. The desired result would be to no red background visible.
HTML
<div class="m-scene" id="main">
<div class="scene_element">
<img class="body" src="http://www.planwallpaper.com/static/images/nature-background-images2.jpg" />
</div>
</div>
JS
$(document).ready(function() {
$('img.body').mousemove(function(e) {
parallax(e, this, 1);
});
});
function parallax(e, target, layer) {
var layer_coeff = 20 / layer;
var x = ($(window).width() - target.offsetWidth) / 2 - (e.pageX - ($(window).width() / 2)) / layer_coeff;
var y = ($(window).height() - target.offsetHeight) / 2 - (e.pageY - ($(window).height() / 2)) / layer_coeff;
$(target).offset({
top: y,
left: x
});
};
CSS
.m-scene {
background-color: red;
overflow: hidden;
width: 100%;
}
.scene_element {
margin-left: auto;
margin-right: auto;
overflow: hidden;
}
.scene_element img {
width: 100%;
height: auto;
margin-left: auto;
margin-right: auto;
}
My jsFiddle is: fiddle
Thank you!
I'm creating a slider that displays a panoramic image by moving it horizontally inside a container.
Features or Intention (it's under development):
By default the animation happens automatically, changing the transformX value, in each step;
the user's able to touch the element and drag it left or right;
when touchend event's triggered, the slider resumes the animation from the user's dragged x position;
The problem is that when a user touch the element and starts dragging, the input value on transformX makes the element jumpy, apparently because the anchor point is different every time the user touches the draggable element.
What I'd like to know is, how to calculate the anchor point or position, where the user is dragging from? Or what's the best practice to always calculate from the same point, to avoid having this jumps.
I've done quite a lot of research at the moment without success. You can find a live example in the following link (http://jsbin.com/quhabi/1/edit). Please turn on the touch emulator on your brower because at the moment I'm not yet supporting mouse dragging.
You can also have a look into the code below (depends on jQuery):
sass:
html {
width: 100%;
height: 100%;
body {
width: 100%;
height: 100%;
background-color: #999;
.panorama {
width: 80%;
height: 80%;
margin: 0 auto;
overflow: hidden;
img {
height: 100%;
position: relative;
transition: opacity 0.6s ease;
transform-origin:left top;
}
}
}
}
html:
<div class="panorama">
<img src="images/panorama.jpg" alt="">
</div>
Javascript:
/*globals $, window */
'use strict';
$('document').ready(function () {
var panorama = {
// properties
$panorama: $('.panorama'),
$moveElement: $('.panorama img'),
timestart: 0,
seconds: 12,
msTotal: 0,
direction: -1,
positionX: 0,
percentage: 0,
animationFrameID: false,
myRequestAnimationFrame: (function () {
return function (callback) {
return window.setTimeout(callback, 1000 / 60);
};
})(),
touchPlayTimeout: 3000,
moveTimeoutID: null,
rightBoundary: null,
// methods
step: function (timestart) {
var self = this,
timestamp,
positionX;
timestamp = Date.now();
self.progress = timestamp - timestart;
self.percentage = (self.progress * (100 / self.msTotal));
positionX = self.direction * self.percentage;
positionX = self.positionBounderies(positionX);
positionX += '%';
self.position(positionX);
if (self.progress < self.msTotal) {
timestamp += 10;
self.animationFrameID = self.myRequestAnimationFrame(function () {
self.step.call(self, timestart);
});
}
},
positionBounderies: function (positionX) {
// move the next line to init method, after image preload done!
this.rightBoundary = 100 - (100 * (this.$panorama.width() / this.$moveElement.width()));
positionX = positionX > 0 ? 0 : positionX;
positionX = (positionX < 0 && Math.abs(positionX) > this.rightBoundary) ? this.direction * this.rightBoundary : positionX;
return positionX;
},
progressByPercentage: function (percentage) {
return percentage * (this.msTotal / 100);
},
dragIt: function (touchX) {
var positionX,
percentage = (this.progress * (100 / this.msTotal));
positionX = this.direction * percentage;
positionX = positionX + (touchX / 100);
positionX = this.positionBounderies(positionX);
positionX += '%';
// update percentage
this.percentage = Math.abs(parseFloat(positionX));
this.position(positionX);
},
position: function (posX) {
this.$moveElement.css('transform', 'translateX(' + posX + ')');
},
init: function () {
var self = this;
// set initial values
this.msTotal = this.seconds * 1000;
// set listeners
this.$moveElement.on('touchstart mousedown', function (e) {
// on mousedown prevent browser default `img` drag
e.preventDefault();
clearTimeout(self.animationFrameID);
clearTimeout(self.moveTimeoutID);
});
this.$moveElement.on('touchend mouseup', function (e) {
// on mousedown prevent browser default `img` drag
e.preventDefault();
// calculate where to play from using current progress
var playFrom = null;
self.progress = self.progressByPercentage(self.percentage);
self.moveTimeoutID = setTimeout(function () {
clearTimeout(self.moveTimeoutID);
playFrom = Date.now();
playFrom = playFrom - self.progress;
self.step(playFrom);
}, self.touchPlayTimeout);
});
this.$moveElement.on('touchmove', function (e) {
console.log(e);
var touch = e.originalEvent.touches[0],
touchPosition = touch.pageX - self.$panorama.width();
self.dragIt(touchPosition);
});
this.step(Date.now());
}
};
panorama.init();
});
I found that my problem is related with how I'm calculating the drag distance. The code above clearly shows that I'm using solely the position of pageX to calculate the transformX, which is absolutely wrong. The correct way is to store the start point from where the drag is happening from and the current dragging x value should be used to calculate the distance.
For example,
this.$moveElement.on('touchstart mousedown', function (e) {
var touch = e.originalEvent.touches[0];
self.touchDistance.start = touch.pageX;
});
this.$moveElement.on('touchmove', function (e) {
var touch = e.originalEvent.touches[0],
distance = 0;
self.touchDistance.end = touch.pageX;
distance = self.touchDistance.end - self.touchDistance.start;
self.dragIt(distance);
});
With this, I've got a working solution, as we can see here http://jsbin.com/quhabi/2/edit
Thanks for looking, hope this is useful for someone else in the future!
I have this code for moving the box using .scrollTop. Using this i code i was able to move the box from bottom to top, is there any other way to control the speed of scrolling every 100 pixel?
Here's my jquery:
$(document).ready(function () {
$(window).scroll(function () {
var x = $(document).scrollTop();
var dh = $(window).innerHeight();
var move = x / 100 * 100;
console.log(x);
$('.box').css('bottom', move);
}
});
});
and the css:
body {
height: 2000px;
}
.box {
width: 50px;
height: 50px;
background: #f00;
bottom: 0;
position: fixed;
}
Is there a way to do this? Thanks in advance guys. Have a nice day.
Not sure if this is exactly what you are getting at, if not, please be more specific as to what you are asking for. That said....
var move = x / 100 * 100;
Just change the numbers in the denominator. It alters the rate of scroll.