I have created a random fishes animation but at some point (about 10sec) from the beginning, my fishes Cling to the right, i want them to keep moving in random all over the area, any idea?
here is the fiddle link: http://jsfiddle.net/832Fx/1/
jquery code:
$.fn.rotate = function (degrees) {
var self = $(this);
self.transition({
rotateY: degrees + 'deg'
}, 5000);
};
var animeVars = {
maxPixelsPerSecond: 50,
minPixelsPerSecond: 10,
topMargin: 0,
bottomMargin: 400,
leftMargin: 0,
rightMargin: 400
};
function topFlip(obj) {
var speed = $(obj).data('speed') ? (1 / parseFloat($(obj).data('speed'))) * 1000000 : 300;
$(obj).find('.top_fin, .top_fins').transition({
rotate: '+=25deg',
x: '+=10'
}, speed, function () {
$(obj).find('.top_fin, .top_fins').transition({
rotate: '-=25deg',
x: '-=10'
}, speed, function () {
topFlip(obj);
});
});
}
function tailFlip(obj) {
var speed = $(obj).data('speed') ? (1 / parseFloat($(obj).data('speed'))) * 1000000 : 300;
$(obj).find('.tail_fin, .tail_fins').transition({
rotateX: '-=25deg'
}, speed, function () {
$(obj).find('.tail_fin, .tail_fins').transition({
rotateX: '+=25deg'
}, speed, function () {
tailFlip(obj);
});
});
}
function animateFish(obj) {
var heading = $(obj).data('heading');
if (!heading) heading = 'left';
var rotation = 0;
var currentCoords = {
top: parseInt($(obj).css('top').replace(/[^-\d\.]/g, ''), 10),
left: parseInt($(obj).css('left').replace(/[^-\d\.]/g, ''), 10)
};
var newCoords = {
top: Math.random() * (animeVars.topMargin - animeVars.bottomMargin + 1) + animeVars.bottomMargin,
left: Math.random() * (animeVars.leftMargin - animeVars.rightMargin + 1) + animeVars.rightMargin
};
if (currentCoords.left < newCoords.left && heading != 'right') {
$(obj).rotate(180);
$(obj).data('heading', 'right');
console.log('swimming right');
} else if (currentCoords.left > newCoords.left && heading != 'left') {
$(obj).rotate(0);
$(obj).data('heading', 'left');
console.log('swimming left');
}
var totalMovement = Math.sqrt(Math.pow(currentCoords.left - newCoords.left, 2) + Math.pow(currentCoords.top - newCoords.top, 2));
console.log('Total pixels to move: ' + totalMovement);
var pps = Math.floor(Math.random() * (animeVars.maxPixelsPerSecond - animeVars.maxPixelsPerSecond)) + animeVars.maxPixelsPerSecond;
var speed = totalMovement / pps * 1000;
$(obj).data('speed', speed);
$(obj).animate({
top: newCoords.top,
left: newCoords.left
}, speed, function () {
animateFish(obj);
});
}
$(document).ready(function () {
$('.loop ').each(function () {
animateFish(this);
topFlip(this);
tailFlip(this);
});
});
Here is how I would fix this: http://jsfiddle.net/BaliBalo/832Fx/2/
I just added
var $wnd = $(window);
$wnd.resize(function() {
animeVars.rightMargin = $wnd.width();
animeVars.bottomMargin = $wnd.height();
}).resize();
at the end of your code, and changed the algorithm a bit to take in account fishes size:
var w = $(obj).width();
var h = $(obj).height();
var newCoords = {
top: Math.random() * (animeVars.topMargin - animeVars.bottomMargin + h + 1) + animeVars.bottomMargin - h,
left: Math.random() * (animeVars.leftMargin - animeVars.rightMargin + w + 1) + animeVars.rightMargin - w
};
However, please note that many optimizations could be done, like caching jQuery objects in variables rather than calling the $ function every time.
Related
I'm new to this community so excuse me if the answer to my question may come be stupid-simple. I've created this site on webflow: https://flumes-fantastic-site.webflow.io/ with the help of the jquery ripples library and I'm trying to make the automatic drops stop when I switch tabs or when the window is not on focus. This is because whenever I switch tabs and come back to it, the animation seems to have "loaded" and shoots all drops at once which looks kinda funny.
I'm new to coding so I'm probably messing up here with the code. This is what the developer of the library has in the documentation https://github.com/sirxemic/jquery.ripples/blob/master/demo/index.html:
<script src="../dist/jquery.ripples.js"></script>
<script>
$(document).ready(function() {
try {
$('body').ripples({
resolution: 512,
dropRadius: 20, //px
perturbance: 0.04,
});
$('main').ripples({
resolution: 128,
dropRadius: 10, //px
perturbance: 0.04,
interactive: false
});
}
catch (e) {
$('.error').show().text(e);
}
$('.js-ripples-disable').on('click', function() {
$('body, main').ripples('destroy');
$(this).hide();
});
$('.js-ripples-play').on('click', function() {
$('body, main').ripples('play');
});
$('.js-ripples-pause').on('click', function() {
$('body, main').ripples('pause');
});
// Automatic drops
setInterval(function() {
var $el = $('main');
var x = Math.random() * $el.outerWidth();
var y = Math.random() * $el.outerHeight();
var dropRadius = 20;
var strength = 0.04 + Math.random() * 0.04;
$el.ripples('drop', x, y, dropRadius, strength);
}, 400);
});
</script>
And this is what I've come up with looking for answers in the community but isn't working:
<script src="https://cdn.jsdelivr.net/npm/jquery.ripples#0.6.3/dist/jquery.ripples.min.js"></script>
<script>
$('#ripple').ripples({
resolution: 512,
dropRadius: 30,
perturbance: 0.04,
});
setInterval(function() {
var $el = $('#ripple');
var x = Math.random() * $el.outerWidth();
var y = Math.random() * $el.outerHeight();
var dropRadius = 30;
var strength = 0.04 + Math.random() * 0.04;
$el.ripples('drop', x, y, dropRadius, strength);
}, 2500);
</script>
<script>
var window_focus;
$(window).focus(function() {
window_focus = true;
})
.blur(function() {
window_focus = false;
});
$(document).one('click',function() {
$('#ripple').ripples('play');
});
$(document).one('click',function() {
$('#ripple').ripples('pause');
});
setInterval(function() { $('body').append('has focus? ' + window_focus + '<br>'); }, 1000);
});
</script>
I'm sorry if the answer to this is super simple and I'm just wasting your time but i appreciate any help you can give me.
Thank you.
The key is to enable the timer when the window gets the focus, and disable it when the focus is lost:
function randomDrop() {
var $el = $('#ripple');
var x = Math.random() * $el.outerWidth();
var y = Math.random() * $el.outerHeight();
var dropRadius = 30;
var strength = 0.04 + Math.random() * 0.04;
$el.ripples('drop', x, y, dropRadius, strength);
}
$(function() {
$('#ripple').ripples({
resolution: 512,
dropRadius: 30,
perturbance: 0.04
});
var timer = null;
$(window).focus(function() {
if (timer === null) {
//console.log('on');
timer = setInterval(randomDrop, 2500);
}
})
.blur(function() {
if (timer !== null) {
clearInterval(timer);
timer = null;
//console.log('off');
}
})
.focus();
});
<div class="container w-container">
<section class="hero wf-section">
<div class="w-embed"><div id="ripple" style="height:100%; Width: 100%; position: absolute; left 0%; top: 0%; right: 0%; bottom: 0%; background: url(https://i.ibb.co/kBgPMYY/wellness-hd.jpg) left top / cover;"></div>
</div>
</section>
</div>
<script src="https://d3e54v103j8qbb.cloudfront.net/js/jquery-3.5.1.min.dc5e7f18c8.js?site=62f2d2e2cbc47f8cda0a15b9" type="text/javascript" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery.ripples#0.6.3/dist/jquery.ripples.min.js"></script>
JSFiddle: https://jsfiddle.net/bd7kc24a/20/
A coworker and I have hit a snag with our attempt to use canvas and we are looking to have a Rect on the canvas to be the actual work area. We are trying to add a grid and/or dot background to that Rect, but we need that section to not scale when zooming occurs. If there is a simple way to do this, we'd love to know about it, but this is where we are currently.
The example jsfiddle above kinda does what we need it to do. But we are having issues:
Persisting that to the database as well as the redux store.
Making sure the grid stays the same pixel distance instead of zooming with objects
So I'm trying to create a custom subclass to re-apply that static canvas background when serialization occurs, however once I've added this base class it will not longer do the loadFromJSON call or hit the callback.
Any help would be appreciated! Especially if there is a simple way to do this that we simply missed in the documentation.
JSFiddle: https://jsfiddle.net/bd7kc24a/20/
'use strict';
debugger;
fabric.CanvasMap = fabric.util.createClass(fabric.Rect, {
type: 'canvasMap',
initialize: function(element, options) {
this.callSuper('initialize', element, options);
options && this.set('name', options.name);
options && this.set('imageRef', options.imageRef);
this.rescaleBackground(10);
},
rescaleBackground: function(scale) {
const padding = 0;
this.imageRef.scaleToWidth(scale);
var patternSourceCanvas = new fabric.StaticCanvas();
patternSourceCanvas.add(this.imageRef);
patternSourceCanvas.setDimensions({
width: this.imageRef.getWidth() + padding,
height: this.imageRef.getHeight() + padding
});
let pattern = new fabric.Pattern({
source: patternSourceCanvas.getElement(),
repeat: 'repeat'
});
this.setPatternFill(pattern);
},
toObject: function() {
return fabric.util.object.extend(this.callSuper('toObject'), {
name: this.name
});
}
});
fabric.CanvasMap.fromObject = function (object, callback) {
var _enlivenedObjects;
object.imageRef = squareImg;
fabric.util.enlivenObjects(object.objects, function (enlivenedObjects) {
delete object.objects;
_enlivenedObjects = enlivenedObjects;
});
const newCanvasMap = new fabric.CanvasMap(_enlivenedObjects, object);
return newCanvasMap;
};
let floorPlan = new fabric.Canvas('floorPlan', {
hoverCursor: 'pointer',
backgroundColor: '#cccccc'
});
function reloadFromJSON() {
let jsonMap = floorPlan.toJSON();
let jsonMapString = JSON.stringify(jsonMap);
floorPlan.loadFromJSON(jsonMapString, function() {
floorPlan.renderAll();
});
}
//let mapBackground = new fabric.Canvas();
function addShape() {
let coordinates = floorPlan.getVpCenter();
const circle = new fabric.Circle({
name: 'some circle',
radius: 20,
fill: 'blue',
left: coordinates.x - 20,
top: coordinates.y - 20
});
floorPlan.add(circle);
}
let defaultScale = 10;
let squareSVG = '<svg width="10" height="10" xmlns="http://www.w3.org/2000/svg"><g> <title>background</title><rect fill="#fff" id="canvas_background" height="12" width="12" y="-1" x="-1"/></g><g> <title>Layer 1</title> <rect id="svg_1" height="9.87496" width="9.99996" y="0.062519" x="0.031269" stroke-width="1.5" stroke="#000000" fill="none"/> </g></svg>'
let squareImg;
fabric.loadSVGFromString(squareSVG, function(objects, options) {
squareImg = fabric.util.groupSVGElements(objects, options);
const map = new fabric.CanvasMap({
imageRef: squareImg,
height: 300, width: 300,
left: 20, top: 20,
strokeWidth: 2,
stroke: 'red',
selectable: false,
evented: false, hasBorders: true, hasControls: false
});
floorPlan.add(map);
floorPlan.renderAll();
});
function setBackground(scale) {
floorPlan._objects[0].rescaleBackground(scale);
}
//logic for binding objects to MAP
floorPlan.observe('object:moving', function(e) {
let obj = e.target;
if (obj.getHeight() > map.height || obj.getWidth() > map.width) {
obj.setScaleY(obj.originalState.scaleY);
obj.setScaleX(obj.originalState.scaleX);
}
obj.setCoords();
if (obj.top - (obj.cornerSize / 2) < map.top ||
obj.left - (obj.cornerSize / 2) < map.left) {
console.log(map.top, map.left);
obj.top = Math.max(obj.top, obj.top - obj.top + (obj.cornerSize / 2) + map.top);
obj.left = Math.max(obj.left, obj.left - obj.left + (obj.cornerSize / 2) + map.left);
}
if (obj.top + obj.height + obj.cornerSize > map.height ||
obj.left + obj.width + obj.cornerSize > map.width) {
obj.top = Math.min(obj.top, (map.height + map.top) - obj.height + obj.top - obj.top - obj.cornerSize / 2);
obj.left = Math.min(obj.left, (map.width + map.left) - obj.width + obj.left - obj.left - obj.cornerSize / 2);
}
});
//zoom
let clicks = 0;
floorPlan.on('mouse:down', function(event) {
clicks += 1;
setTimeout(function() {
if (clicks > 1) {
console.log(event.e.clientX);
floorPlan.zoomToPoint(new fabric.Point(event.e.clientX, event.e.clientY), floorPlan.getZoom() + 1);
}
clicks = 0;
}, 300);
});
function zoomIn() {
floorPlan.setZoom(floorPlan.getZoom() + 0.5);
console.log(floorPlan.getZoom(), defaultScale * floorPlan.getZoom());
setBackground(defaultScale / floorPlan.getZoom());
floorPlan.renderAll();
}
function zoomOut() {
floorPlan.setZoom(floorPlan.getZoom() - 0.5); ;
console.log(floorPlan.getZoom(), defaultScale * floorPlan.getZoom());
setBackground(defaultScale / floorPlan.getZoom());
floorPlan.renderAll();
}
function renderAll() {
floorPlan.renderAll();
}
//pan
function pan(event) {
event.e.preventDefault();
let panAmount = {
x: event.e.deltaX,
y: event.e.deltaY
};
//logic for binding the map to the screen during pan
/*
* We need to use the following points and check for negative or overflow values
* canvas.oCoords.tl - top left
* canvas.oCoords.bl - bottom left
* canvas.oCoords.tr - top right
* canvas.oCoords.br - bottom right
*/
//pan left
if (panAmount.x > 0) {
if (map.oCoords.tr.x < floorPlan.width - 40) {
panAmount.x = 0;
}
}
//pan right
if (panAmount.x < 0) {
if (map.oCoords.tl.x > 40) {
panAmount.x = 0;
}
}
//pan up
if (panAmount.y > 0) {
if (map.oCoords.bl.y < floorPlan.height - 40) {
panAmount.y = 0;
}
}
//pan down
if (panAmount.y < 0) {
if (map.oCoords.tl.y > 40) {
panAmount.y = 0;
}
}
// assuming the logic above allows the scroll, get the relative scroll values and create a new fabric point and then scroll to it
const delta = new fabric.Point(-panAmount.x, -panAmount.y);
floorPlan.relativePan(delta);
}
floorPlan.on('mouse:wheel', pan);
I have this piece of code:
var delta = _window.width() / 30, // resize hexagons by delta px
window_height = _window.height(); // .box-1/window height
$('.hexagon').each(function () { // get animated boxes
var $this = $(this),
width_1 = $this.width(),
height_1 = $this.height(),
multi = 0, // adjust resize to scroll speed (depends on delta)
ratio = 1.167; // to calculate height
// animation properties
var enlarge_obj = {
width: function() {return width_1 += (delta * multi)},
height: function() {return width_1 * ratio}
},
shrink_obj = {
width: function() {return width_1 -= (delta * multi)},
height: function() {return width_1 * ratio}
};
....
function scrollHandler() {
...
$this.animate(enlarge_obj, 0, 'linear');
...
$this.animate(shrink_obj, 0, 'linear');
}
}
I want to pass enlarge_obj and shrink_obj to jQuery animate function.
But it doesn't work.
If I write code like this:
$this.animate({width: width_1 += (delta * multi), height: width_1 * ratio},0,'linear');
it works fine.
What am I doing wrong? Thx.
I have an image of a wheel of fortune wheel and I am trying to make so that when it spins it displays the correct amount for what it was spun to.
I have the following code: http://jsfiddle.net/maniator/rR67s/
Many times it is correct, and other time is is wrong.
For example I spun this:
And it alerted 300, which is wrong.
How can I fix my algorithm so that it is correct 99% of the time (or 100% if it is possible)?
HTML:
<div id="game">
<div id="tick">⇩</div>
<img id="wheel" src="http://i.imgur.com/R7JYazp.png" data-rotation="0">
</div>
Javascript:
var Wheel = (function () {
var wheel = document.getElementById('wheel'),
wheelValues = [5000, 600, 500, 300, 500, 800, 550, 400, 300, 900, 500, 300, 900, 0, 600, 400, 300, -2, 800, 350, 450, 700, 300, 600],
spinTimeout = false,
spinModifier = function () {
return Math.random() * 10 + 20;
},
modifier = spinModifier(),
slowdownSpeed = 0.5,
prefix = (function () {
if (document.body.style.MozTransform !== undefined) {
return "MozTransform";
} else if (document.body.style.WebkitTransform !== undefined) {
return "WebkitTransform";
} else if (document.body.style.OTransform !== undefined) {
return "OTransform";
} else {
return "";
}
}()),
degreeToRadian = function (deg) {
return deg / (Math.PI * 180);
};
function Wheel() {};
Wheel.prototype.rotate = function (degrees) {
var val = "rotate(-" + degrees + "deg)";
if (wheel.style[prefix] != undefined) wheel.style[prefix] = val;
var rad = degreeToRadian(degrees % 360),
filter = "progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand', M11=" + rad + ", M12=-" + rad + ", M21=" + rad + ", M22=" + rad + ")";
if (wheel.style["filter"] != undefined) wheel.style["filter"] = filter;
wheel.setAttribute("data-rotation", degrees);
};
Wheel.prototype.spin = function (callback, amount) {
var _this = this;
clearTimeout(spinTimeout);
modifier -= slowdownSpeed;
if (amount === undefined) {
amount = parseInt(wheel.getAttribute('data-rotation'));
}
this.rotate(amount);
if (modifier > 0) {
spinTimeout = setTimeout(function () {
_this.spin(callback, amount + modifier);
}, 1000 / 5);
} else {
var dataRotation = parseInt(wheel.getAttribute('data-rotation'));
modifier = spinModifier();
var divider = 360 / wheelValues.length;
var wheelValue = wheelValues[Math.floor(Math.round(dataRotation % 360) / divider)];
switch (wheelValue) {
case 0:
return callback(0);
case -1:
return callback("Free Spin");
case -2:
return callback("Lose a turn");
default:
return callback(wheelValue);
}
}
};
return Wheel;
})();
var wheel = new Wheel;
wheel.spin(function(spinVal){
alert(spinVal)
});
Full game for those who want to try it out: http://jsfiddle.net/maniator/XP9Qv/ (<-- this was updated using accepted answer)
The fun continues here.
I think the problem is that the arrow in the starting position is in the middle of a zone, not at the start of it. So you have a starting offset of (360 / wheelValues.length)/2
var divider = 360 / wheelValues.length;
var offset=divider/2; //half division
var wheelValue = wheelValues[Math.floor(Math.ceil((dataRotation+offset) % 360) / divider)];
This seems to work: when the wheel stops either at the beginning (first half) or at the end (last half) of a zone the showed value is the expected one (just about a dozen tests done)
var wheelValue = wheelValues[Math.floor(Math.ceil(dataRotation % 360) / 15)];
switch (wheelValue) {
I changed this line from round to ceil and got 14 out of 14 correct results..
I have created a function to rotate images, and now what I would like to do is to stop the images from rotating when I use the mouseover command. this is the js coding I have to get the images to rotate
var m = {
Z : 100,
xm : 0,
xmm : .25,
ymm : 0,
ym : 0,
mx : 0,
nx : 0,
ny : 0,
nw : 0,
nh : 0,
xR : 0,
nI : 0,
scr : 0,
img : 0,
run : function () {
m.xm += (m.xmm - m.xm) * .1;
if (m.ym < m.nw * .15) m.ym++;
m.xR += m.xm;
for (var i = 0; i < m.nI; i++){
var A = (i * 360 / m.nI) + m.xR;
var x = Math.cos(A * (Math.PI / 180));
var y = Math.sin(A * (Math.PI / 180));
var a = m.img[i];
a.style.width = ''.concat(Math.round(Math.abs(y * m.ym) + y * m.Z), 'px');
a.style.left = ''.concat(Math.round((m.nw * .5) + x * ((m.nw * .5) - (m.nw * .05)) - ((Math.abs(y * m.ym) + y * m.Z) * .5)), 'px');
a.style.height = ''.concat(Math.round(m.ym + y * m.Z), 'px');
a.style.top = ''.concat(Math.round((m.nh * .5) - (m.ym * .5) - x * (m.Z * .5) - (m.ymm * y)), 'px');
a.style.zIndex = 600 + Math.round(y);
m.setOpacity(a, (y * 50) + 100);
}
setTimeout(m.run, 30);
},
I really did not read your code in detail, but what you could probably do is set a parameter outside the function, maybe a global parameter to the function that rotates your image, call it "rotate" and set it to TRUE
Then, before you make the actual rotation, you check if this "rotate" param set to TRUE, if yes then rotate.
Now, onmouseover, all you have to do is set the "rotate" param to FALSE, and then when it its FALSE when the setTimeout trigger is expires and the function starts again, the imagee would not rotate becaues it failed your test.
Another approach is to set your setTimeout to trigger only when not on mousenotover, so if on mouse over, do not set the timeout, othersie, set the time out.
Those are just two ideas that just came to my mind reading your code, I think you can think about it and decide if those are solutions you like, and if not then I would be interested to know what you decided.
Cheers.
The next code is just an approximation, and is more like a "pseudo code" than a production code. Anyways feel free to modify as you need.
var m = {
run: function() {
this.isRunning = true;
// when complete the cycle
this.cycle = true;
},
play: function() {
this.timeout = setTimeout((function($this){
if($this.animCheck !== undefined) clearInterval($this.animCheck);
$this.run();
})(this), this.frames);
},
pause: function() {
this.animCheck = setInterval((function($this) {
// Obviously, you've to pause the animation when the cycle is finished.
if($this.cycle) clearTimeout($this.timeout);
})(this), this.frames);
return !!this.animCheck;
},
init: function() {
this.frames = 30;
this.isRunning = true;
[the element].addEventListener('mouseover', function() {
if(this.pause()) this.isRunning = false;
})
this.play();
}
};
m.init();