Raphael JS - .animate .text and .path at the same time - javascript

I've been working on scaling different areas of a SVG chart using Raphael js. I have all of the elements (.paths) scaling the way I want but I need to scale text in conjunction these scalable elements and also need to add .attr to the text. Can I scale a .path and .text simultaneously upon hover? Can someone look at my jfiddle and let me know how I could achieve this? Thank you!!
var w = 600;
var h = 600;
var paper1 = Raphael("box");
paper1.setViewBox(0,0,w,h,false);
paper1.setSize('100%', '100%');
var ONEgrp = paper1.set();
var ONE = paper1.path("M340.1064,218.2627l97.8281-75.5117c-39.5898-48.9438-100.123-80.2485-167.9785-80.2485V186.019C298.0166,186.019,323.1533,198.5244,340.1064,218.2627z");
ONE.attr({parent: 'ONEgrp',fill: "#EFA35A",stroke:"#fff",'stroke-width':1, cursor: 'pointer'}).data('id', 'ONE');
var ONEtxt = paper1.text(340, 145, "Total Rewards\nManagement\nT1/GR1");
ONEtxt.attr('fill', '#000');
ONEtxt.attr('font-size', '12px');
ONE.mouseover(function(){
if(!ONE.data("over")) {
ONE.attr('opacity',
ONE.attr('opacity')*0.90);
ONE.toFront();
ONE.data("over",true);
ONEtxt.toFront();
}
});
ONE.mouseout(function(){
ONE.attr('opacity',1);
ONE.data("over",false);
ONEtxt.toFront();
});
ONEtxt.mouseover(function(){
if(!ONEtxt.data("over")) {
ONEtxt.attr('opacity',
ONEtxt.attr('opacity')*0.90);
ONEtxt.toFront();
ONEtxt.data("over",true);
}
});
ONEtxt.mouseout(function(){
ONEtxt.toFront();
ONEtxt.attr('opacity',1);
ONEtxt.data("over",false);
});
var rsrGroups = [];
function hovering(e){
this.animate({
transform: 's1.2' }, 100, "elastic");
}
function hoverout(e){
this.animate({
transform: 's1' }, 101);
}
ONE.mouseout(hoverout);
ONE.mouseover(hovering);
ONEtxt.mouseout(hoverout);
ONEtxt.mouseover(hovering);

You can make a function thats called, which animates both separately. You may need to tie the element and the text somehow in the function IF the handler will be a generic one for lots of different elements, whereas its currently hardcoded.
I've also added the following CSS...to stop the text element stealing focus.
jsfiddle
text {
pointer-events: none;
}
function ONEtxtAnimate() {
if(!ONE.data("over")) {
ONE.attr('opacity',
ONE.attr('opacity')*0.90);
ONE.toFront();
ONE.data("over",true);
ONEtxt.animate({ opacity: 1 },100);
ONEtxt.toFront();
}
};
function ONEtxtAnimateOff() {
ONE.attr('opacity',1);
ONE.data("over",false);
ONEtxt.toFront();
ONEtxt.animate({ opacity: 0.2 },100);
}
ONE.mouseout(hoverout);
ONE.mouseover(hovering);
function hovering(e){
this.animate({
transform: 's1.2' }, 100, "elastic");
ONEtxtAnimate();
}
function hoverout(e){
this.animate({
transform: 's1' }, 101);
ONEtxtAnimateOff();
}

I have an update to this. Ian helped a ton got me off to a great start. I ended up elaborating this one a bit so I wanted to post the update because it might help others.
This also worked and I added a click for a link
var ONEgrp = paper1.set();
// Main Path ONE
var ONE = paper1.path("M340.1064,218.2627l97.8281-75.5117c-39.5898-48.9438-100.123-80.2485-167.9785-80.2485V186.019C298.0166,186.019,323.1533,198.5244,340.1064,218.2627z");
ONE.attr({parent: 'ONEgrp',fill: "#EFA35A",stroke:"#fff",'stroke-width':1, cursor: 'pointer'}).data('id', 'ONE');
// Text for ONE
var ONEtxt = paper1.text(340, 145, "Total Rewards\nManagement\nT1/GR1");
ONEtxt.attr('fill', '#000');
ONEtxt.attr('font-size', '14px');
ONEtxt.attr('font-family', 'Helvetica, Arial, sans-serif');
ONEtxt.attr('font-weight', 400);
// ONE mouseover
var mouseover_anim = Raphael.animation({transform: 's1.2' }, 100, "elastic");
// ONEtxt mouseover
var mouseout_anim = Raphael.animation({transform: 's1.2' }, 100, "elastic");
// ONE mouseout
var mouseover_anim2 = Raphael.animation({transform: 's1' }, 100, "elastic");
// ONEtxt mouseout
var mouseout_anim2 = Raphael.animation({transform: 's1' }, 100, "elastic");
ONE.mouseover(function(){
ONE.animate(mouseover_anim);
ONEtxt.animateWith(ONE,mouseover_anim,mouseout_anim);
if(!ONE.data("over")) {
ONE.attr('opacity',
ONE.attr('opacity')*0.90);
ONE.toFront();
ONE.data("over",true);
ONEtxt.toFront();
ONEtxt.data("over",true);
}
if(!ONEtxt.data("over")) {
ONEtxt.toFront();
ONEtxt.data("over",true);
}
});
// ONE mouseout
ONE.mouseout(function(){
ONE.animate(mouseover_anim2);
ONEtxt.animateWith(ONE,mouseover_anim2,mouseout_anim2);
ONE.attr('opacity',1);
ONE.data("over",false);
ONE.data("over",false);
WhiteCircle.toFront();
badge.toFront();
badge2.toFront();
badge3.toFront();
badge4.toFront();
badge5.toFront();
badge6.toFront();
badge7.toFront();
badge8.toFront();
badge9.toFront();
});
var paper1Groups = [];
function hovering(e){
ONE.animate({
transform: 's1.2' }, 100, "elastic");
}
function hoverout(e){
ONE.animate({
transform: 's1' }, 100);
}
ONE.mouseout(hoverout);
ONE.mouseover(hovering);
// ONE url location // Change this link to change the location /
ONE.click(function() {
window.location.href = "/adimLink?id=18458";
});

Related

SVG Map w/ JS- Changing the states of multiple paths on hover

I'm working on a clickable SVG map using the Raphael library and referencing details from this tutorial. I've also set up a working jsfiddle here. Basically, for each state in the map, I have paths defined for the map shape itself and for a state abbreviation label—in the case of the fiddle, I'm showing one state, PA, for demonstration purposes. I have separate arrays defined for "regions" and "labels". Currently, I have the hover state working for the state shape (changing its color to a dark blue), but would also like the state abbreviation label to change to white while hovering on the state.
I have the following arrays and loop defined to handle the hover and click events for the regions (shapes), and I would like to add logic that finds the matching label and changes its fill attribute to white on hover (and reverts on mouseout):
// REGION ARRAY
var regions = {};
regions["pennsylvania"] = {href: "#", path: map.path("path here")};
// LABEL ARRAY
var labels = {};
labels["pennsylvania"] = {href: "#", path: map.path("path here")};
// REGION STYLES
var animationSpeed = 500;
var shapeStyle = {
fill: "#cdd6e9",
stroke: "#fff",
"stroke-width": 0.25,
"stroke-linejoin": "round",
cursor: "pointer"
};
var hoverStyle = {
fill: "#0a3a62"
}
// REGION LOOP
for (var regionName in regions) {
(function(region) {
region.path.attr(shapeStyle);
region.path[0].addEventListener("mouseout", function() {
region.path.animate(shapeStyle, animationSpeed);
}, true);
region.path[0].addEventListener("mouseover", function() {
region.path.animate(hoverStyle, animationSpeed);
}, true);
region.path[0].addEventListener("click", function() {
location.href = region.href;
}, true);
})(regions[regionName]);
}
Thus, in looping through the regions array, how would I adjust the script to find the matching label in the labels array and change its fill state? Thanks for any insight here.
Set the label events while you're setting your region events so you can match on the regionName. You can either use the let keyword on your for loop or you can pass the regionName or both (regions[regionName],labels[regionName]) to the immediate function as #Ian suggested.
var labelHoverStyle = { // add
fill: '#FFFFFF'
}
var labelStyle = {
fill: "#0a3a62",
stroke: "#0a3a62",
"stroke-width": 0.25,
"stroke-linejoin": "round",
cursor: "pointer"
}
Using Let
for(let regionName in regions) { // notice the variable declaration
(function (region) {
if (regionName == "district-of-columbia") {
region.path.attr(shapeStyle2);
region.path[0].addEventListener("mouseout", function() {
region.path.animate(shapeStyle2, animationSpeed);
labels[regionName].path.animate(labelStyle, animationSpeed);
}, true);
} else {
region.path.attr(shapeStyle);
region.path[0].addEventListener("mouseout", function() {
region.path.animate(shapeStyle, animationSpeed);
labels[regionName].path.animate(labelStyle, animationSpeed);
}, true);
}
region.path[0].addEventListener("mouseover", function() {
region.path.animate(hoverStyle, animationSpeed);
labels[regionName].path.animate(labelHoverStyle, animationSpeed);
}, true);
region.path[0].addEventListener("click", function() {
location.href = region.href;
}, true);
})(regions[regionName]);
}
Passing regionName or (regions[regionName],labels[regionName])
for(var regionName in regions) {
(function (region, label) { // notice the parameters
if (region.href.indexOf('district-of-columbia') > -1) {
region.path.attr(shapeStyle2);
region.path[0].addEventListener("mouseout", function() {
region.path.animate(shapeStyle2, animationSpeed);
label.path.animate(labelStyle, animationSpeed);
}, true);
} else {
region.path.attr(shapeStyle);
region.path[0].addEventListener("mouseout", function() {
region.path.animate(shapeStyle, animationSpeed);
label.path.animate(labelStyle, animationSpeed);
}, true);
}
region.path[0].addEventListener("mouseover", function() {
region.path.animate(hoverStyle, animationSpeed);
label.path.animate(labelHoverStyle, animationSpeed);
}, true);
....
})(regions[regionName], labels[regionName]); // notice the arguments
}

How to access the index position i in the drag stop handler of snapsvg

I'm grouping a few elements using snapSVG's group method, pushing them to an array and applying the drag method on the array elements by looping through each element.
Could you please help me in accessing the index postion of the dragged element (grps[i]) in the drag stop handler.
g1 and var g2 are the two gropus.
grps is the array that holds the two groups.
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.5.1/snap.svg-min.js"></script>
</head>
JavaScript
var s = Snap(800, 600);
var grps = [];
var objects = [];
var red = s.rect(50, 50, 200, 200).attr({
fill: "red"
});
var green = s.rect(60, 60, 100, 100).attr({
fill: "green"
});
var g1 = s.group(red, green);
grps.push(g1);
var red = s.rect(300, 50, 200, 200).attr({
fill: "red"
});
var green = s.rect(310, 60, 100, 100).attr({
fill: "green"
});
var g2 = s.group(red, green);
grps.push(g1, g2);
var drag_move = function(dx, dy) {
this.attr({
transform: this.data('origTransform') + (this.data('origTransform') ? "T" : "t") + [dx, dy]
});
};
var drag_start = function() {
this.data('origTransform', this.transform().local);
};
var drag_stop = function(i) {
console.log("finished dragging");
console.log(i);
};
for (i = 0; i < grps.length; i++) {
grps[i].drag(drag_move, drag_start, drag_stop);
}
JsBin Link: http://jsbin.com/tonazosicu/10/edit?js
Thanks
You can using Function.prototype.bind() to preset some parameters like below
for (i = 0; i < grps.length; i++) {
grps[i].drag(drag_move, drag_start, drag_stop.bind(null, i));
}
Then on drag_stop you can access them like below.
var drag_stop = function(index, event) {
console.log("finished dragging");
console.log(index);
console.log(event);
};
One can achieve the same thing (in lastest versions of Snap I think) with...
grps.ForEach( function( el, i ) {
el.drag(drag_move, drag_start, drag_stop.bind(null, i))
};
But ultimately you don't need to use i, if you just use 'this' in the handler in most cases, and can simply do....
grps.ForEach( function( el ) {
el.drag(drag_move, drag_start, drag_stop)
};

SVG and mouseover

I'm a JS newbie and have converted an SVG to JS using Raphael. I'm trying to make an interactive map of the USA with mouseover effects over multiple paths and circles.
I have the states as variables with a parent variable and the cities I've visted as variables with a parent variable. Here's a snippet of my JS:
var states = rsr.set();
var connecticut = rsr.path("M877.198,184.1l-0.6-4.2l-0.8-4.4l-1.602-6L870,170.4l-21.802,4.8l0.602,3.3l1.5,7.3v8.102 l-1.102,2.3l1.802,2.101l5-3.399l3.6-3.2l1.9-2.1l0.8,0.6l2.7-1.5l5.198-1.1L877.198,184.1z").attr({fill: '#D3D3D3','stroke-width': '0','stroke-opacity': '1'});
connecticut.node.id = 'Connecticut';
states.push(connecticut);
var cities = rsr.set();
var losAngeles = rsr.circle(87, 349, 5).attr({fill: '#3F3F3F','stroke-width': '0','stroke-opacity': '1'});
cities.push(losAngeles);
I'm having trouble creating mouseover effects on both the states AND cities. I'm thinking it could have something to do with the z-index?
I've written these for loops so far but only one ever works at a time.
for (var i = 0; i <= states.length; i++) {
states[i].mouseover(function() {
this.animate({
fill: '#fff',
transform: 's1.05'
}, 200);
});
states[i].mouseout(function() {
this.animate({
fill: '#D3D3D3',
transform: 's1'
}, 200);
});
}
for (var i = 0; i <= cities.length; i++) {
cities[i].mouseover(function() {
this.animate({
r : 10,
}, 200);
});
cities[i].mouseout(function() {
this.animate({
r : 5,
}, 200);
});
}
I've tried using toFront(); and toBack(); and still can't get it to work. Any suggestions?

How to hide items in the correct sequence? , Using waypoint, TweenMax, jquery

I'm collapsando the header when I scroll the browser window, I'm using waypoints to trigger a function when passing a certain limit.
My problem is how to do that, for example, when I scroll down first disappear content (form inputs) and then change the height of the header, and then when I scroll up first increase height and then display the contents (form inputs) .
How do you could do?
I have a example here: fiddle
JS:
$(document).ready(init);
function init(){
var header, pageContainer, pageContent, brandImage;
header = $('header');
pageContainer = $('#page-container');
pageContent = $('#page-content');
brandImage = $('.brand img');
//functions
collapseHaeder();
function collapseHaeder(){
if(pageContainer.hasClass('collapse-header')){
var waypoint = new Waypoint({
element: document.getElementById('page-content'),
handler: function(direction) {
var elementsToResize = $('header, .brand-holder, .header-content');
var hideElms = $('.hide-element');
if(direction == 'up'){
hideElements(hideElms);
resizeHeader(elementsToResize);
}else {
resizeHeader(elementsToResize);
hideElements(hideElms);
}
}
});
}
}
function resizeHeader(elemts){
var newHeight = 45;
var brandHeight = newHeight - 10;
var easingEffect = 'Quart.easeInOut';
if(!elemts.hasClass('resized')){
elemts.addClass('resized');
}else {
elemts.removeClass('resized');
newHeight = 140;
brandHeight = newHeight / 2;
}
//header elements containers
TweenMax.to(elemts, 1, {height:newHeight, ease: easingEffect});
//page container padding
TweenMax.to(pageContainer, 1, {paddingTop:newHeight, ease: easingEffect});
//brand image
TweenMax.to(brandImage, 1, {height:brandHeight, ease: easingEffect});
}
function hideElements(hiddenElement){
var classHidded = 'has-hided';
if(!hiddenElement.hasClass(classHidded)){
hiddenElement.addClass(classHidded);
hiddenElement.fadeOut(800);
}else {
hiddenElement.fadeIn(500);
hiddenElement.removeClass(classHidded);
}
}
}
There is no need to use jQuery's fadeIn and fadeOut when you are already using GSAP. Take a look at this jsFiddle that I have created, code of which is as follows:
/*global TweenMax,TimelineMax*/
$(document).ready(init);
function init() {
var header, pageContainer, pageContent, brandImage, brandHolder, headerContent, hideElement;
var duration = .8,
ease = 'Power4.easeInOut',
stagger = duration * .2;
var minHeight = 45;
var brandHeight = minHeight - 10;
var classNameResized = 'resized';
var classNameHidden = 'has-hided';
var fadeTween = null,
heightTween = null,
paddingTween = null,
imageTween = null;
header = $('header');
pageContainer = $('#page-container');
pageContent = $('#page-content');
brandImage = $('.brand img');
brandHolder = $('.brand-holder');
headerContent = $('.header-content');
hideElement = $('.hide-element');
//functions
initTweens();
collapseHaeder();
function initTweens() {
fadeTween = TweenMax.to(hideElement, duration, {
autoAlpha: 0,
display: 'none',
ease: ease,
paused: true
});
heightTween = TweenMax.to([header, brandHolder, headerContent], duration, {
height: minHeight,
ease: ease,
paused: true,
delay: stagger
});
paddingTween = TweenMax.to(pageContainer, duration, {
paddingTop: minHeight,
ease: ease,
paused: true,
delay: stagger
});
imageTween = TweenMax.to(brandImage, duration, {
height: brandHeight,
ease: ease,
paused: true,
delay: stagger
});
}
function addClasses() {
if (!header.hasClass(classNameResized)) {
header.addClass(classNameResized);
brandHolder.addClass(classNameResized);
headerContent.addClass(classNameResized);
}
if (!hideElement.hasClass(classNameHidden)) {
hideElement.addClass(classNameHidden);
}
}
function removeClasses() {
if (header.hasClass(classNameResized)) {
header.removeClass(classNameResized);
brandHolder.removeClass(classNameResized);
headerContent.removeClass(classNameResized);
}
if (hideElement.hasClass(classNameHidden)) {
hideElement.removeClass(classNameHidden);
}
}
function collapseHaeder() {
if (pageContainer.hasClass('collapse-header')) {
var waypoint = new Waypoint({
element: pageContent,
handler: function (direction) {
if (direction == 'up') {
fadeTween.reverse();
heightTween.reverse();
paddingTween.reverse();
imageTween.reverse();
removeClasses();
} else {
fadeTween.play();
heightTween.play();
paddingTween.play();
imageTween.play();
addClasses();
}
}
});
}
}
}
There are many things that can be improved here in terms of code structure, animation style etc e.g. animating translateY instead of height for a more smooth animation (Link & Link) but I have tried to keep the structure / approach of the code untouched.
Hope it helps.
P.S. I would strongly recommend to take a look at TimelineMax as well for all your sequencing needs in animations. I have also forked another version using TimelineMax.
T

Script doesn't work on elements loaded with infinite scrolling

I'm using this script on my tumblr page, which gives posts different random text colors:
function get_random_color() {
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 6; i++ ) {
color += letters[Math.round(Math.random() * 15)];
}
return color;}
$(function() {
$(".post").each(function() {
$(this).css("color", get_random_color());
}); });
The thing is the script isn't working for elements loaded with infinite scrolling. Can anyone help me rewrite this code? I don't know how to write javascript sadly.
Take a look at your blog's main.js script. You can call your custom function when you grab the new elements from another page. This is my proposed revision of your main.js file.
$(window).load(function () {
var $wall = $('#content');
$wall.imagesLoaded(function () {
$wall.masonry({
itemSelector: '.post',
isAnimated: false
});
});
$wall.infinitescroll({
navSelector: '#pagination',
nextSelector: '#pagination li a.pagination_nextlink',
itemSelector: '.post',
loadingImg: "http://static.tumblr.com/kwz90l7/bIdlst7ub/transparent.png",
loadingText: " ",
donetext: " ",
bufferPx: 100,
debug: false,
errorCallback: function () {
$('#infscr-loading').animate({
opacity: .8
}, 2000).fadeOut('normal');
}
}, function (newElements) {
var $newElems = $(newElements);
$newElems.hide();
$newElems.each(function(value){
value.css("color", get_random_color());
});
$newElems.imagesLoaded(function () {
$wall.masonry('appended', $newElems, {
isAnimated: false,
animationOptions: {
duration: 900,
easing: 'linear',
queue: false
}
}, function () {
$newElems.fadeIn('slow');
});
});
$(document).ready(function () {
$("a[rel^='prettyPhoto']").prettyPhoto({
deeplinking: false,
default_width: 600,
default_height: 550,
allow_resize: true,
});
});
});
$('#content').show(500);
});
function get_random_color() {
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 6; i++ ) {
color += letters[Math.round(Math.random() * 15)];
}
return color;
}
What I've done is add your get_random_color function and called it from within the Infinite Scroll call to add a custom color to each of the elements in $newElems so really, all I've done is taken your code and integrated it differently than what you were trying to do, which wasn't working. This should, theoretically, work. If it doesn't or you have questions, let me know.

Categories

Resources