Raphael - Hover Animation - javascript

Im trying to have a little animation when you hover over raphael elements.
See Example here:
http://jsfiddle.net/GVEqf/24/
When you hover over the square it scales up and when you stop hovering it scales down.
The problem is: When you stop hovering over the element while it's still scaling up, it'll start scaling down and it will end up smaller then it was originally.
You can see that when you hover over the element and then immediately move the mouse out of it.
Here are the functions I use for the hovering animations:
hoverFunc = function() {
rect.animate({transform: "...s1.6,1.6"}, 1000, 'bounce');
};
hideFunc = function() {
rect.animate({transform: "...s0.625,0.625"}, 1000, 'bounce');
};

I would save the transform to a var and transform to the origional state:
var canvas = Raphael(document.getElementById("canvas"), 250, 250);
var rect = canvas.rect(1, 1, 50, 50);
rect.attr({'fill': 'black'});
rect.transform("t20, 20");
var origionalForm = rect.transform(); // remember current transform
hoverFunc = function() {
rect.animate({transform: "...s1.6,1.6"}, 1000, 'bounce');
};
hideFunc = function() {
rect.animate({transform: origionalForm.toString()}, 1000, 'bounce'); // back to origional state
};
rect.hover(hoverFunc, hideFunc);
Demo: http://jsfiddle.net/GVEqf/27/

Instead of appending the transform to all the existing transforms (using “...”) in both animations, you could just reset the absolute transform (in at least one of the animations). This has the downside that you have to repeat the initial translation string but I guess it has the upside of being more explicit.
I’ve modified your jsFiddle:
hoverFunc = function() {
rect.animate({transform: "...S1.6,1.6"}, 1000, 'bounce');
};
hideFunc = function() {
rect.animate({transform: "t20, 20"}, 1000, 'bounce');
};

Related

Phaser 3: Change "Hitbox"/Interactive area of sprite without physics

The game I'm creating doesn't require any physics, however you are able to interact when hovering over/clicking on the sprite by using sprite.setInteractive({cursor: "pointer"});, sprite.on('pointermove', function(activePointer) {...}); and similar. However I noticed two issues with that:
The sprite has some area which are transparent. The interactive functions will still trigger when clicking on those transparent areas, which is unideal.
When playing a sprite animation, the interactive area doesn't seem to entirely (at all?) change, thus if the sprite ends on a frame bigger than the previous, there end up being small areas I can't interact with.
One option I thought of was to create a polygon over my sprite, which covers the area I want to be interactive. However before I do that, I simply wanted to ask if there are simpler ways to fix these issues.
Was trying to find an answer for this myself just now..
Think Make Pixel Perfect is what you're looking for.
this.add.sprite(x, y, key).setInteractive(this.input.makePixelPerfect());
https://newdocs.phaser.io/docs/3.54.0/focus/Phaser.Input.InputPlugin-makePixelPerfect
This might not be the best solution, but I would solve this problem like this. (If I don't want to use physics, and if it doesn't impact the performance too much)
I would check in the event-handler, if at the mouse-position the pixel is transparent or so, this is more exact and less work, than using bounding-boxes.
You would have to do some minor calculations, but it should work well.
btw.: if the origin is not 0, you would would have to compensate in the calculations for this. (in this example, the origin offset is implemented)
Here is a demo, for the click event:
let Scene = {
preload ()
{
this.load.spritesheet('brawler', 'https://labs.phaser.io/assets/animations/brawler48x48.png', { frameWidth: 48, frameHeight: 48 });
},
create ()
{
// Animation set
this.anims.create({
key: 'walk',
frames: this.anims.generateFrameNumbers('brawler', { frames: [ 0, 1, 2, 3 ] }),
frameRate: 8,
repeat: -1
});
// create sprite
const cody = this.add.sprite(200, 100).setOrigin(0);
cody.play('walk');
cody.setInteractive();
// just info text
this.mytext = this.add.text(10, 10, 'Click the Sprite, or close to it ...', { fontFamily: 'Arial' });
// event to watch
cody.on('pointerdown', function (pointer) {
// calculate x,y position of the sprite to check
let x = (pointer.x - cody.x) / (cody.displayWidth / cody.width)
let y = (pointer.y - cody.y) / (cody.displayHeight / cody.height);
// just checking if the properties are set
if(cody.anims && cody.anims.currentFrame){
let currentFrame = cody.anims.currentFrame;
let pixelColor = this.textures.getPixel(x, y, currentFrame.textureKey, currentFrame.textureFrame);
// alpha > 0 a visible pixel of the sprite, is clicked
if(pixelColor.a > 0) {
this.mytext.text = 'Hit';
} else {
this.mytext.text = 'No Hit';
}
// just reset the textmessage
setTimeout(_ => this.mytext.text = 'Click the Sprite, or close to it ...' , 1000);
}
}, this);
}
};
const config = {
type: Phaser.AUTO,
width: 400,
height: 200,
scene: Scene
};
const game = new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>

Phaser scrolling background

My goal is to make a sprite bigger than the screen and have the user scroll to see the different parts of it, so I wanted to ask if Phaser had any sprite eventListener-functions such as:
var canvas = window.document.getElementsByTagName('canvas')[0],
prevX = 0, prevY = 0, mouseDown = false;
where canvas can be used as
canvas.addEventListener('mousedown',function(e){
});
canvas.addEventListener('mousemove',function(e){
});
Here is how I did it.
In your update function:
if (this.game.input.activePointer.isDown) {
if (this.game.origDragPoint) {
// move the camera by the amount the mouse has moved since last update
this.game.camera.x += this.game.origDragPoint.x - this.game.input.activePointer.position.x;
this.game.camera.y += this.game.origDragPoint.y - this.game.input.activePointer.position.y;
}
// set new drag origin to current position
this.game.origDragPoint = this.game.input.activePointer.position.clone();
}
else {
this.game.origDragPoint = null;
}
If using the camera is OK for you, you can try this Phaser 2 plugin: https://jdnichollsc.github.io/Phaser-Kinetic-Scrolling-Plugin/
With this plugin you can enable vertical and horizontal scroll to simulate scrolling into the canvas element used by Phaser, also you can customize the movement before to initialize the plugin, example:
var game = new Phaser.Game(400, 400, Phaser.AUTO, '', {
init: function () {
//Load the plugin
this.game.kineticScrolling = this.game.plugins.add(Phaser.Plugin.KineticScrolling);
},
create: function () {
//Configure the plugin
this.game.kineticScrolling.configure({
kineticMovement: true,
timeConstantScroll: 325, //really mimic iOS
horizontalScroll: true,
verticalScroll: false,
horizontalWheel: true,
verticalWheel: false,
deltaWheel: 40
});
//Starts the plugin
this.game.kineticScrolling.start();
//Changing the world size
this.game.world.setBounds(0, 0, 800, 800);
},
stopScrolling: function () {
//Stop the plugin
this.game.kineticScrolling.stop();
}
});
Also there's a fork for Phaser 3, check the repo from GitHub.
Regards, Nicholls

How do you animate path morphs inside of loaded SVG with Snap.svg?

I know you can animate path morhs with snap.svg (How to animate path morphs using snap.svg)
Is it possible to 'load' a path and then animate path morph? Or do you HAVE to define the path inside of Snap.svg?
(function($) {
'use strict';
// grab the empty svg element in the html
var s = Snap(800,600);
// load the svg and grab the #arm and its inner elemmnts
Snap.load('body.svg', function(f) {
var arm = f.select('#arm'),
forearm = f.select('#forearm'),
bicep = f.select('#bicep');
s.append(arm);
s.append(forearm);
s.append(bicep);
anim1();
});
// animate rotate the forearm graphic
var anim1 = function() {
forearm.animate({
'transform': 'r45,320,120'
}, 1000, mina.linear, anim2);
};
// animate morph from the svg images current path coordinates top the ones below
var anim2 = function() {
bicep.animate({
d: 'M337.889,188c-12.064-11.708-28.073-93.482-89.777-92.889c-62.222,3.333-93,104.555-93,104.555l1.667,49.445 c29.608,30.553,96.669,99.406,178.333,3.333L337.889,188z'
}, 1000, mina.bounce, anim3);
};
// animate morph from the svg images current path coordinates top the ones below
var anim3 = function() {
forearm.animate({
d: 'M174.333,250.938c0,0,19.659-36.111,17.816-98.333c-25.316-59.032-31.731-75.007-24.267-84.445l-27.338,1.667 c-35.496,57.849-82.325,139.528-1.843,178.334L174.333,250.938z'
}, 1000, mina.bounce);
};
})(jQuery);
jsfiddle here - http://jsfiddle.net/1wqewzs3/2/
Thanks
You can animate following a load or whatever really, the main thing is that the variables are defined somewhere accessible, and the animation is only run after the load.
In this particular case, the main error is that you are defining the arm, forearm, bicep variables within the Snap.load function, and then trying to use them in your animation function (which isn't aware of those variables).
Couple of options...
make arm, forearm, bicep all global variables ( use 'var' at the beginning of your script, if all the relevant bits are in an immediate mode function, it should limit scope which maybe better ). This is probably the simplest.
Write your animation with a select...
s.select('#forearm').animate({
'transform': 'r45,320,120'
}, 1000, mina.linear, anim2);

Calling js functions at same time yui

currently this is causing the (image) fadeout function to end, and then the fade in function fires. i need the images to crossfade and the opacity of each image to overlap. im having trouble getting this work. thoughts?
_initFade: function () {
this._timer = Y.later(this._intervalDuration, this, this._startPeriod, [], false);
},
_startPeriod: function () {
this._timer = Y.later(this._intervalDuration, this, this._fadeOut, [], true);
this._fadeOut();
},
_fadeOut: function(){
var host = this.get('host');
this._animOut.set('node', host._getCurrentBlock());
this._animOut.once('end', this._fadeIn, this);
this._animOut.run();
},
_fadeIn: function(){
var host = this.get('host'),
blocks = host.get('blocks'),
index = host.get('index');
index = host._moveIndex(host._getNextIndex());
var nextBlock = blocks.item(index);
this._transparent(nextBlock);
host.syncUI();
this._animIn.set('node', nextBlock);
this._animIn.run();
},
YUI doesn't support multiple animations which run in sync. But have a look at the 'tween' event of Y.Anim. It is called for every frame of the animation. So you can fade one image using the animation and adjust the opacity of the second image during the tween event.
For example, I use the tween event to animate multiple items simultaneously:
var someNode = Y.Node.create("<div></div>"); // invisible div to animate
Y.one(document.body).appendChild(someNode);
var anim = new Y.Anim({
node: someNode,
duration: 0.25,
from: { color: 'red' },
to: { color: 'white' }
});
anim.on('tween', function(event){
Y.StyleSheet().set('input.text.error', { backgroundColor: someNode.getStyle('color') });
// other animations
});

Stop Spinner.js

I'm using spin.js ( http://fgnass.github.com/spin.js/) while a large full width/height image loads. The problem is I'm having trouble stopping the spinner. The stop() method doesn't seem to work. There's what I've got:
$(document).ready(function($) {
var img = new Image();
img.src = $('#top-bg').css('background-image').replace(/url\(|\)/g, '');
img.onload = function(){
$("#top-bg").spinner.stop();
$(".top-bar").delay(1500).fadeIn(5000);
$("#arrow, #arrowrt, #top-icons").delay(5000).fadeIn(5000);
};
});
I also tried
.spin(false)
and
.data('spinner').stop()
This is the spinner settings:
$(document).ready(function($) {
var opts = {
lines: 9, // The number of lines to draw
length: 11, // The length of each line
width: 4, // The line thickness
radius: 10, // The radius of the inner circle
rotate: 0, // The rotation offset
color: '#fff', // #rgb or #rrggbb
speed: 0.9, // Rounds per second
trail: 54, // Afterglow percentage
shadow: false, // Whether to render a shadow
hwaccel: false, // Whether to use hardware acceleration
className: 'spinner', // The CSS class to assign to the spinner
zIndex: 2e9, // The z-index (defaults to 2000000000)
top: 'auto', // Top position relative to parent in px
left: 'auto' // Left position relative to parent in px
};
var target = document.getElementById('top-bg');
var spinner = new Spinner(opts).spin(target);
});
You need to store the spinner instance somewhere - in a way that you can access it when you need it to stop the spinning:
var spinner = new Spinner(opts).spin(target);
$(target).data('spinner', spinner);
And now you're able to stop it like this:
$('#top-bg').data('spinner').stop();
You can stop spinner without instance:
$(target).spin(false);
This seems to work across browsers and uses less code:
$(".spinner").remove();
Remove all DOM of containter, example:
<style type="text/css">.loader{ display: none; }</style>
<div>
<span class="loader"></span>
<span>Foo</span>
</div>
If need apply sping append it in
$('.loader').show().spin();
And destroy:
$('.loader').hide().empty();
Remember, jQuery.empty() automaticaly remove all DOM of object before destroy.

Categories

Resources