RSS

Category Archives: RaphaelJS

RaphaelJS 2.0 onDragOver limitation

Layers

New RaphaelJs 2.0 provides a function to detect if an element is being dragged over another element. It’s called onDragOver and is a shortcut for drag.over.<id> event.

It works very well for 2 elements being directly on top of each other. But if a third element appears in between then unfortunately events are not firing any more. According to the source code it was intentional and I guess functionality is not going to be extended any time soon.

This is a major problem if you have several cascading elements and you need to fire an event on each of them. Unfortunately Element.hover() event is not fired while drag and drop either (at least not in FF that I used for testing).

The only solution I found so far is to use Element.drag() with Element.getBBox() method and compare coordinates myself to determine whether mouse is inside the element “box”. It’s a very ugly solution especially if elements are not of a simple rectangular shape. Otherwise the only option I can see is to check if a dot is inside the polygon etc.

If you have any ideas on better solution please let me know!

Advertisements
 
1 Comment

Posted by on August 15, 2011 in RaphaelJS

 

Tags: , , ,

RaphaelJS simple text align

Recently I noticed lots of questions on StackOverFlow regarding text alignment in RaphaelJS.

By default RaphaelJS alignes text to the middle as you can see on the image below:

default middle text alignment

To align text to the left you set text-anchor attribute to “start”. Or to “end” to align text to the right.

R.text(50, 100, 'Some text goes here').attr({'font-size': 11, 'text-anchor': 'start'});

Result – text aligned to the left:

text aligned to the left

 
Leave a comment

Posted by on July 27, 2011 in RaphaelJS

 

Tags:

String representation of path in RaphaelJS

raphaeljs paths

Simple thing but took me a few hours to find a solution. If you are using RaphaelJs and have performed some modifications on the path (like translation etc.) and then you would like to get a string representation of that modified path there is a very simple way to do it:

[path object].attr('path');

As always it’s all in the documentation if you know how to read it…

Quick working example:

var paper = Raphael(10, 50, 320, 200);
var p = paper.path("M10 10L90 90").attr({stroke: 'black'});

/* original path string */				
alert(p.attr('path')); //M10,10L90,90
				
p.translate(30,30);

/* modified path string */
alert(p.attr('path')); //M40,40L120,120
 
2 Comments

Posted by on May 26, 2011 in RaphaelJS

 

Tags: ,

SVG image zooming and panning with RaphaelJs


raphaeljs image zoom

View the demo here.

Easy way to do image zooming and panning without sending images to the server to be processed and trimmed with RaphaelJS and a few simple steps.

1. Set initial zoom, image window width and height:

var mapZoom = 1;

var mapWidth = 400;
var mapHeight = 400;
$('#mapHolder').css('width', mapWidth);
$('#mapHolder').css('height', mapHeight);

2. Create a RaphaelJS object and add a full-size image to it.

var paper = Raphael("mapHolder", mapWidth, mapHeight);
var mapImage = paper.image("https://dashasalo.files.wordpress.com/2011/09/figure1-worldatlas-large.jpg", 0, 0, mapWidth, mapHeight);

You should see your image resized to specified mapWidth and mapHeight.

3. Next step is to draw a menu of controls. I used RaphaelJS to draw the controls but you can also use images if you like.

var mapMenuHolderWidth = 40;
var mapMenuHolderHeight = 216;
var mapMenuHolderX = mapWidth - mapMenuHolderWidth - 6; // menu right margin
var mapMenuHolderY = 5; // menu top margin

// draw menu panel
var mapMenuHolder = paper.rect(mapMenuHolderX, mapMenuHolderY, mapMenuHolderWidth, mapMenuHolderHeight, 12).attr({fill: 'black', stroke: '#aaa', 'stroke-width': 2, opacity: 0.7});

Now controls themselves. You can find very nice icons and paths used to draw them on RaphaelJs website. SVG paths for icons were quite long and made code unreadable so I removed them from this code snippet. If you need paths have a look at the demo source code.

var maxIcon = paper.path("M22...051z").attr({fill: "#bbb", stroke: "#000", translation: (mapMenuHolderX+5) + ', '+(mapMenuHolderY + 10)});

var minIcon = paper.path("M22...884z").attr({fill: "#bbb", stroke: "#000", translation: (mapMenuHolderX+5) + ', '+(mapMenuHolderY + 7+ 34)});
  		
var arrowTop = paper.path("M239...73z").attr({fill: "#bbb", stroke: "#000", scale: 0.10, translation: '245, -25', rotation: 180});
  		
var arrowLeft = paper.path("M239...73z").attr({fill: "#bbb", stroke: "#000", scale: 0.10, translation: '245, 4', rotation: 90});
  		
var arrowRight = paper.path("M239...73z").attr({fill: "#bbb", stroke: "#000", scale: 0.10, translation: '245, 33', rotation: -90});
  		
var arrowBottom = paper.path("M239...73z").attr({fill: "#bbb", stroke: "#000", scale: 0.10, translation: '245, 63'});

I used one path for all the arrows and just rotated it to make it point different direction.

4. Now we add a bunch of handlers to the controls to catch the click event.

// maximize icon clicked 
maxIcon.click(function(e){
  	mapZoom++;
  	mapImage.scale(mapZoom, mapZoom);
  	e.stopPropagation();
  	e.preventDefault();
  }); 

// minimize icon clicked 
  minIcon.click(function(e){
  	if (mapZoom == 1) return;
  	mapZoom--;
  	mapImage.scale(mapZoom, mapZoom);
  	adjustMapEdge();
  	e.stopPropagation();
  	e.preventDefault();
  }); 
  
// top arrow clicked		
arrowTop.click(function(e){
  	mapImage.translate(0, 20);
  	adjustMapEdge();
  	e.stopPropagation();
  	e.preventDefault();
}); 
  
// bottom arrow clicked		
arrowBottom.click(function(e){
  	mapImage.translate(0, -20);
  	adjustMapEdge();
  	e.stopPropagation();
  	e.preventDefault();
}); 
  
// left arrow clicked		
arrowLeft.click(function(e){
  	mapImage.translate(20, 0);
  	adjustMapEdge();
  	e.stopPropagation();
  	e.preventDefault();
}); 
  		
// right arrow clicked
arrowRight.click(function(e){
  	mapImage.translate(-20, 0);
  	adjustMapEdge();
  	e.stopPropagation();
  	e.preventDefault();
});  	

We will have a look at adjustMapEdge function in a second.

You can also add nice hover effects to the controls with mouseover and mousedown events (look at my demo).

5. AdjustMapEdge function makes sure that image doesn’t move out of its window boundaries. For instance, if user moves up for long enough eventually he/she will hit the image top edge. When this happens we shouldn’t allow the image move any further up and just use its current location even if the user drags the image up again. This way from this position the user can only move down, left or right.

var adjustMapEdge = function()
{
    if (mapImage.attr('x') > 0) mapImage.attr({'x': 0});
    if (mapImage.attr('x') < (mapWidth - mapImage.attr('width'))) mapImage.attr({'x': (mapWidth - mapImage.attr('width'))});
    		
    if (mapImage.attr('y') > 0) mapImage.attr({'y': 0});
    if (mapImage.attr('y') < (mapHeight - mapImage.attr('height') )) mapImage.attr({'y': (mapHeight - mapImage.attr('height') )});
};

6. Panning the image. Panning the image can be split into 3 basic steps – grab the image, hold the mouse button and drag the image, release the mouse button and drop the image.

As there is no reliable way to say whether mouse button is being hold when the mouse moves, we use a javascript variable to store the click state. In the code snippet below when mouse button is clicked I set clicking variable to true. Once the mouse button is released set clicking to false. And while mouse moves check if clicking is still true (hasn’t been released).

// coordinates of the last drag position
var lastX = 0, lastY = 0;

// record the click event on the image for drag and drop
var clicking = false;

// set clicking to true to record start of drag and drop
// and update the last coordinates
$('#mapHolder').mousedown(function(e){
    	clicking = true;
    	lastX = e.pageX;
    	lastY = e.pageY;
    	$('#mapHolder').css('cursor', 'move');
    	e.stopPropagation();
    	e.preventDefault();
});

// when the mouse button is released set clicking to false
// to stop drag and drop		
$(document).mouseup(function(e){
    	clicking = false;
    	$('#mapHolder').css('cursor', 'default');
    	e.stopPropagation();
    	e.preventDefault();
});

$('#mapHolder').mousemove(function(e){
    	// when mouse if moved check if user is also holding a mouse button
    	if (clicking == false) return;
    		
    	var currentMapPosX = 0, currentMapPosY = 0;
    	
    	// get difference between current and previous mouse position	
    	if ((mapImage.attr('x') <= 0) && (mapImage.attr('x') >= (mapWidth - mapImage.attr('width'))))
    	{
    		currentMapPosX = e.pageX - lastX;
    	}
    		
    	if ((mapImage.attr('y') <= 0) && (mapImage.attr('y') >= (mapHeight - mapImage.attr('height'))))
    	{
    		currentMapPosY = e.pageY - lastY;
    	}
    	
    	// move the image	
    	mapImage.translate(currentMapPosX, currentMapPosY);
    	
    	// record previous position
    	lastX = e.pageX;
    	lastY = e.pageY;
    	
    	// check for image edge	
    	adjustMapEdge();
});		
 
39 Comments

Posted by on April 13, 2011 in JavaScript, RaphaelJS

 

Tags: , , , ,

Creating a world map with RaphaelJS (svg)

SVG and RaphaelJs allow to create really nice experience. Some experimentation on the topic – world map.


svg world map

It’s really incredibly quick and easy to create. All you need is SVG paths to draw the countries. These can easily be found on Wikipedia (look for world map svg files) or paths and selections can be converted to SVG paths with various image editors (like Gimp). Also you can download the paths I used from jsfiddle.

Once you got the paths create a canvas to draw on with RaphaelJS:

var paper = Raphael('mapHolder'); // mapHolder is an ID of div element to be used as canvas

If you use paths from my example you need to include worldpaths.js file to your page and call getPaths function passing default settings for the paths:

var map = getPaths(paper, { fill: "#333", stroke: "#000", "stroke-width": .5, "stroke-linejoin": "round" });

Then loop through the paths array and “draw” SVG paths on canvas:

for (var countryCode in map) {							        
    (function (countryPath) {
        // give paths some opacity look nice
        countryPath.attr({opacity: 0.6});
        
        // get random colour
        colour = Raphael.getColor();
	
        // fill country path with colour when mouse goes over the country path					
        countryPath[0].onmouseover = function() 
        {
             countryPath.animate({fill: colour, stroke: colour }, 300);
             paper.safari();
        };
       
        // return to default grey colour
        countryPath[0].onmouseout = function() 
        {
             countryPath.animate({fill: "#333", stroke: "#000"}, 300);
             paper.safari();
        };
    })(map[countryCode]);
} 

And vuala!

View live demo here.

 
6 Comments

Posted by on April 4, 2011 in RaphaelJS

 

Tags: , ,

 
%d bloggers like this: