planetaryjs globe not working - javascript - javascript

I want this type of globe and I am using planetaryjs for this.
I have added the necessary resources for that in external resources link
including all js files and data file.
why the globe is not loading?
jsfiddle link
(function() {
var canvas = document.getElementById('quakeCanvas');
// Create our Planetary.js planet and set some initial values;
// we use several custom plugins, defined at the bottom of the file
var planet = planetaryjs.planet();
planet.loadPlugin(autocenter({extraHeight: -120}));
planet.loadPlugin(autoscale({extraHeight: -120}));
planet.loadPlugin(planetaryjs.plugins.earth({
topojson: { file: '/world-110m.json' },
oceans: { fill: '#001320' },
land: { fill: '#06304e' },
borders: { stroke: '#001320' }
}));
planet.loadPlugin(planetaryjs.plugins.pings());
planet.loadPlugin(planetaryjs.plugins.zoom({
scaleExtent: [50, 5000]
}));
planet.loadPlugin(planetaryjs.plugins.drag({
onDragStart: function() {
this.plugins.autorotate.pause();
},
onDragEnd: function() {
this.plugins.autorotate.resume();
}
}));
planet.loadPlugin(autorotate(5));
planet.projection.rotate([100, -10, 0]);
planet.draw(canvas);
// Create a color scale for the various earthquake magnitudes; the
// minimum magnitude in our data set is 2.5.
var colors = d3.scale.pow()
.exponent(3)
.domain([2, 4, 6, 8, 10])
.range(['white', 'yellow', 'orange', 'red', 'purple']);
// Also create a scale for mapping magnitudes to ping angle sizes
var angles = d3.scale.pow()
.exponent(3)
.domain([2.5, 10])
.range([0.5, 15]);
// And finally, a scale for mapping magnitudes to ping TTLs
var ttls = d3.scale.pow()
.exponent(3)
.domain([2.5, 10])
.range([2000, 5000]);
// Create a key to show the magnitudes and their colors
d3.select('#magnitudes').selectAll('li')
.data(colors.ticks(9))
.enter()
.append('li')
.style('color', colors)
.text(function(d) {
return "Magnitude " + d;
});
// Load our earthquake data and set up the controls.
// The data consists of an array of objects in the following format:
// {
// mag: magnitude_of_quake
// lng: longitude_coordinates
// lat: latitude_coordinates
// time: timestamp_of_quake
// }
// The data is ordered, with the earliest data being the first in the file.
d3.json('/examples/quake/year_quakes_small.json', function(err, data) {
if (err) {
alert("Problem loading the quake data.");
return;
}
var start = parseInt(data[0].time, 10);
var end = parseInt(data[data.length - 1].time, 10);
var currentTime = start;
var lastTick = new Date().getTime();
var updateDate = function() {
d3.select('#date').text(moment(currentTime).utc().format("MMM DD YYYY HH:mm UTC"));
};
// A scale that maps a percentage of playback to a time
// from the data; for example, `50` would map to the halfway
// mark between the first and last items in our data array.
var percentToDate = d3.scale.linear()
.domain([0, 100])
.range([start, end]);
// A scale that maps real time passage to data playback time.
// 12 minutes of real time maps to the entirety of the
// timespan covered by the data.
var realToData = d3.scale.linear()
.domain([0, 1000 * 60 * 12])
.range([0, end - start]);
var paused = false;
// Pause playback and update the time display
// while scrubbing using the range input.
d3.select('#slider')
.on('change', function(d) {
currentTime = percentToDate(d3.event.target.value);
updateDate();
})
.call(d3.behavior.drag()
.on('dragstart', function() {
paused = true;
})
.on('dragend', function() {
paused = false;
})
);
// The main playback loop; for each tick, we'll see how much
// time passed in our accelerated playback reel and find all
// the earthquakes that happened in that timespan, adding
// them to the globe with a color and angle relative to their magnitudes.
d3.timer(function() {
var now = new Date().getTime();
if (paused) {
lastTick = now;
return;
}
var realDelta = now - lastTick;
// Avoid switching back to the window only to see thousands of pings;
// if it's been more than 500 milliseconds since we've updated playback,
// we'll just set the value to 500 milliseconds.
if (realDelta > 500) realDelta = 500;
var dataDelta = realToData(realDelta);
var toPing = data.filter(function(d) {
return d.time > currentTime && d.time <= currentTime + dataDelta;
});
for (var i = 0; i < toPing.length; i++) {
var ping = toPing[i];
planet.plugins.pings.add(ping.lng, ping.lat, {
// Here we use the `angles` and `colors` scales we built earlier
// to convert magnitudes to appropriate angles and colors.
angle: angles(ping.mag),
color: colors(ping.mag),
ttl: ttls(ping.mag)
});
}
currentTime += dataDelta;
if (currentTime > end) currentTime = start;
updateDate();
d3.select('#slider').property('value', percentToDate.invert(currentTime));
lastTick = now;
});
});
// Plugin to resize the canvas to fill the window and to
// automatically center the planet when the window size changes
function autocenter(options) {
options = options || {};
var needsCentering = false;
var globe = null;
var resize = function() {
var width = window.innerWidth + (options.extraWidth || 0);
var height = window.innerHeight + (options.extraHeight || 0);
globe.canvas.width = width;
globe.canvas.height = height;
globe.projection.translate([width / 2, height / 2]);
};
return function(planet) {
globe = planet;
planet.onInit(function() {
needsCentering = true;
d3.select(window).on('resize', function() {
needsCentering = true;
});
});
planet.onDraw(function() {
if (needsCentering) { resize(); needsCentering = false; }
});
};
};
// Plugin to automatically scale the planet's projection based
// on the window size when the planet is initialized
function autoscale(options) {
options = options || {};
return function(planet) {
planet.onInit(function() {
var width = window.innerWidth + (options.extraWidth || 0);
var height = window.innerHeight + (options.extraHeight || 0);
planet.projection.scale(Math.min(width, height) / 2);
});
};
};
// Plugin to automatically rotate the globe around its vertical
// axis a configured number of degrees every second.
function autorotate(degPerSec) {
return function(planet) {
var lastTick = null;
var paused = false;
planet.plugins.autorotate = {
pause: function() { paused = true; },
resume: function() { paused = false; }
};
planet.onDraw(function() {
if (paused || !lastTick) {
lastTick = new Date();
} else {
var now = new Date();
var delta = now - lastTick;
var rotation = planet.projection.rotate();
rotation[0] += degPerSec * delta / 1000;
if (rotation[0] >= 180) rotation[0] -= 360;
planet.projection.rotate(rotation);
lastTick = now;
}
});
};
};
})();

Your globe is not loading for multiple reasons. First, if you open up your console you would see that the external resources were not loaded properly. As the error notes, your external resources failed to load because:
MIME type ('text/plain') is not executable, and strict MIME type checking is enabled.
You could overcome this issue in a twofold manner. You could either refer to this answer--which will help you sort the MIME type issue--or you may just use other links for you external resources. If you rather take the second route, you should add the links below to your external resources section, in the following order:
moment.js
d3.js
planetary.js
After you solved the first issue, you will see that you would not be able to link your JSON file by simply adding it to your external resources. These answers will offer you multiple approaches that will help you solve the JSON issue as well.
After you have successfully linked all of your external files, you will face a third problem. If you look at your code you will see that you are trying to get the time property for your data (JSON) object in line 75--var start = parseInt(data[0].time, 10);. However, as far as I could tell, your data object does not hold a time property--go ahead, and console.log() your data object to see its structure and properties. In other words, you might want to double check if the world-110m.json is the file that you want to work with.
Hope this helps.

Related

d3 scaleLinear update and zoom conflict

i have a problem combining manual update and zoom functionality in d3.
Here is a small demo code cut off out of a larger module which creates only a linear scale.
https://jsfiddle.net/superkamil/x3v2yc7j/2
class LinearScale {
constructor(element, options) {
this.element = d3.select(element);
this.options = options;
this.scale = this._createScale();
this.axis = this._createAxis();
this.linearscale = this._create();
}
update(options) {
this.options = Object.assign(this.options, options);
this.scale = this._createScale();
this.axis = this._createAxis();
this.linearscale.call(this.axis);
}
_create() {
const scale = this.element
.append('g')
.attr('class', 'linearscale')
.call(this.axis);
this.zoom = d3.zoom().on('zoom', () => this._zoomed());
this.element.append('rect')
.style('visibility', 'hidden')
.style('width', this.options.width)
.style('height', this.options.height)
.attr('pointer-events', 'all')
.call(this.zoom);
return scale;
}
_createScale() {
let range = this.options.width;
this.scale = this.scale || d3.scaleLinear();
this.scale.domain([
this.options.from,
this.options.to
]).range([0, range]);
return this.scale;
}
_createAxis() {
if (this.axis) {
this.axis.scale(this.scale);
return this.axis;
}
return d3.axisBottom(this.scale);
}
_zoomed() {
this.linearscale
.call(this.axis.scale(d3.event.transform.rescaleX(this.scale)));
let domain = this.axis.scale().domain();
this.element.dispatch('zoomed', {
detail: {
from: domain[0],
to: domain[1],
},
});
}
}
const scale = new LinearScale(document.getElementById('axis'), {
from: 0,
to: 600,
width: 600,
height: 100
});
document.getElementById('set').addEventListener('click', () => {
scale.update({
from: 0,
to: 100
});
});
Zoom x axis out to 0 - 10000 (random numbers)
Click on the "set" button
X axis sets the domain from 0 - 100
Start zooming out again
-> Expected: Zoom starts from the domain 0 - 100
-> Result: Zoom jumps back to the previous zoom level 0 - 10000
I know, that d3 is working with a scale copy and i'm updating the original scale but i don't find a way how to combine them or how to set the zoom level to the original scale.
https://github.com/d3/d3-zoom/blob/master/README.md#transform_rescaleX
Thanks!
You either recreate the zoom behaviour or just reset the current zoom transforms.
You should be able to reset each parameter( zoom and translate) or just reset them all. The following example also triggers the zoom specific events, but as you already have the domain set it should not be a visual problem.
Ex:
Add
this.element.select("rect").call(this.zoom.transform, d3.zoomIdentity);
in your update function.
More information regarding the zoom api in https://github.com/d3/d3-zoom/blob/master/README.md#zoom_transform . Also I found an example using a transition animation at https://bl.ocks.org/mbostock/db6b4335bf1662b413e7968910104f0f .
I also updated the fiddle:
update(options) {
this.options = Object.assign(this.options, options);
this.scale = this._createScale();
this.axis = this._createAxis();
this.linearscale.call(this.axis);
this.element.select("rect").call(this.zoom.transform, d3.zoomIdentity);
}
https://jsfiddle.net/x3v2yc7j/8/

How to bind a changing datasource to a set of svg?

I would like to visualize a circle with a random colour at a random x and y coordinate, then add an extra colourful circle at a random location every second.
I am using d3.timer to run a function that appends x and y coordinates to my dataset object, which is bound to all of the circle objects. When I print the dataset object, I can see that my function does in fact append the new x and y coordinates to my dataset object. However, the visualization does not update with the new circles. How can I add a new circle every second?
Relevant functions below:
var reshuffleData = function(){
for (var i=0; i<5; i++){
console.log('Reshuffling')
dataset.push({x: randomXPosition(), y: randomYPosition()})
}
console.log(dataset)
return true
}
d3.timer(reshuffleData, 10);
Full jsfiddle here: http://jsfiddle.net/d74Le5xk/
It wasn't not working because d3.timer is used incorrectly. As d3.timer just takes a function to paint next animation frame. We do not control when the this function will be called but it will be called most likely (1/ frames per second seconds). Where FPS may change every second.
If you want to do something periodically use setInterval also you need to redraw the circles once the dataset size is changed.
Following is the jsfiddle link for the working code.
http://jsfiddle.net/d74Le5xk/3/
Also attaching the code here for reference.
HTML
<svg class='canvas'></svg>
Javascript
(function () {
var width = 420, height = 200;
var randomXPosition = function(d){
return Math.random() * width;
}
var randomYPosition = function(d){
return Math.random() * height;
}
var dataset = [];
var circleBatchSize = 5;
var maxCircleCount = 100;
for (var i=0; i < circleBatchSize; i++){
dataset.push({x: randomXPosition(), y: randomYPosition()})
}
var testInterval = null;
var reshuffleData = function(){
for (var i=0; i<circleBatchSize; i++){
dataset.push({x: randomXPosition(), y: randomYPosition()})
//return true;
}
console.log('Reshuffled ' + dataset.length)
console.log(dataset)
if(dataset.length > maxCircleCount) {
clearInterval(testInterval);
}
}
console.log(dataset);
var colours = ['#FDBB30', '#EE3124', '#EC008C', '#F47521', '#7AC143', '#00B0DD'];
var randomColour = function() {
return colours[Math.floor(Math.random() * colours.length)];
}
//d3.timer(reshuffleData, 0, 5000);
testInterval = window.setInterval(reshuffleData, 2000);
var canvas = d3.select('.canvas')
.attr('width', width)
.attr('height', height)
.style('background-color', 'black');
var datasetOldLength = 0;
function drawCircles() {
if(datasetOldLength === dataset.length ) {
return;
}
datasetOldLength = dataset.length;
var circles = canvas.selectAll('circle')
.data(dataset)
.enter()
.append('circle')
.style('r', 20)
.style('fill', randomColour)
.style('cx', function(d) { return d.x} )
.style('cy', function(d) { return d.y} );
if(dataset.length > maxCircleCount) {
return true;
}
}
d3.timer(drawCircles, 1000);
})();
d3.timer usage explanation
# d3.timer(function[, delay[, time]])
[function] argument is called at every frame rendering by d3. It's called until it returns true.
optional [delay] in milliseconds to delay the first invocation of the [function]. Delay is taken since the [time] passed in third argument. If [time] is not passed delay starts from new Date().getTime().
optional [time] is the epoch time from when the delay is considered.
Reference https://github.com/mbostock/d3/wiki/Transitions#timers

Mouse position is only read on the first frame

i have been having trouble with reading a mouse position on a canvas. The code is working (semi) correctly as it reads the position when clicking he canvas in IE but only on one frame, in chrome it is just displaying the value as 0.
Here is the full code:
<script>
var blip = new Audio("blip.mp3");
blip.load();
var levelUp = new Audio("levelUp.mp3");
levelUp.load();
var canvas = document.getElementById('game');
var context = canvas.getContext('2d');
context.font = '18pt Calibri';
context.fillStyle = 'white';
//load and draw background image
var bgReady = false;
var background = new Image();
background.src = 'images/background.jpg';
background.onload = function(){
bgReady = true;
}
var startMessage = 'Click the canvas to start';
//load plane image
var planeReady = false;
var planeImage = new Image();
planeImage.src = 'images/plane.png';
planeImage.onload = function() {
planeReady = true;
}
//load missile image
var missileReady = false;
var missileImage = new Image();
missileImage.src = 'images/missile-flipped.gif';
missileImage.onload = function() {
missileReady = true;
}
//initialise lives and score
var score = 0;
var lives = 3;
var missilesLaunched = 0;
var missileSpeed = 5;
var level = 1;
var missileX = 960;
var missileY = Math.random() * 500;
if (missileY > 480) {
missileY = 480;
}
function getMousePos(canvas, event) {
return {
x: input.x - rect.left,
y: input.y - rect.top
};
}
function update_images(event) {
var pos = getMousePos(canvas.getBoundingClientRect(), mouseInput);
planeImage.y = pos.y;
missileX = missileX - missileSpeed;
if (missileX < - 70) {
missilesLaunched++;
missileX = 960;
missileY = Math.random() * 500;
if (missileY > 480) {
missileY = 480;
}
blip.play();
score = missilesLaunched;
if (score % 5 == 0) {
missileSpeed = missileSpeed + 2;
level++;
levelUp.play();
}
}
}
function reload_images() {
if (bgReady = true) {
context.drawImage(background, 0, 0);
}
if (planeReady = true) {
context.drawImage(planeImage, 10, planeImage.y);
}
if (missileReady = true) {
context.drawImage(missileImage, missileX, missileY);
}
context.fillText('Lives: ' + lives, 200, 30);
context.fillText('Score: ' + score, 650, 30);
context.fillText('Level: ' + missileSpeed, 420, 30);
context.fillText('Position: ' + missileImage.y, 420, 70);
}
function main(event) {
var mouseInput = { x: 0, y: 0 };
document.addEventListener("mousemove", function (event) {
mouseInput.x = event.clientX;
mouseInput.y = event.clientY;
});
update_images(event);
reload_images();
if (lives > 0) {
window.requestAnimationFrame(main);
}
else {
}
}
function start() {
context.drawImage(background, 0, 0);
context.fillText('Click the canvas to start', 350, 250);
function startMain(event) {
game.removeEventListener("click", startMain);
main(event);
}
canvas.addEventListener("mousedown", startMain);
}
start();
</script>
Joe, you should actually be capturing the mouse position every time you click...
...but you're actually also starting a new game (without stopping the old one), every time you click, too.
First problem: starting game engine several times to draw on the same instance of the canvas
Solution:
In your start function, you need to remove the mousedown event listener, after you've triggered it.
function start () {
// ... other setup
function startMain (event) {
canvas.removeEventListener("click", startMain);
main(event);
}
canvas.addEventListener("click", startMain);
}
Now it will only listen for the first click, before starting, and will only start once.
Second Problem: mouse doesn't update as expected
Solution: two issues here...
...first, you are passing event into main on first call...
...after that, you're passing main into requestAnimationFrame.
requestAnimationFrame won't call it with an event, it will call it with the number of microseconds (or ms or some other unit as a fractional precision of ms) since the page was loaded.
So the first time you got main({ type: "mousedown", ... });.
The next time you get main(4378.002358007);
So lets refactor the startMain we had above, so that main never ever collects an event, just a time.
function startMain ( ) {
canvas.removeEventListener("click", startMain);
requestAnimationFrame(main);
}
The next problem is that even if you were getting just events, you're only ever capturing a click event (which as we mentioned earlier, fires a new copy of the game logic).
Your solution is to separate the code which catches mouse events from the code which reads mouse position.
var mouseInput = { x: 0, y: 0 };
document.addEventListener("mousemove", function (event) {
mouseInput.x = event.clientX;
mouseInput.y = event.clientY;
});
function getMousePos (rect, input) {
return {
x : input.x - rect.left,
y : input.y - rect.top
};
}
// currently in updateImages (should not be there, but... a different story)
var pos = getMousePos(canvas.getBoundingClientRect(), mouseInput);
You've got other problems, too...
You're calling getMousePos and passing in game at the moment. I don't see where game is defined in your JS, so either you're making game somewhere else (begging for bugs), or it's undefined, and your app blows up right there.
You should really be building this with your console / dev-tools open, in a hands-on fashion, and cleaning bugs in each section, as you go.

HighCharts--custom redraw handler borks zooming?

I'm trying to make a column chart in HighCharts with annotations that are properly repositioned whenever the chart is resized or zoomed.
http://jsfiddle.net/2tJ3G/
You can see what I mean by resizing the frame around the chart. The annotations stay where they should be.
The problem only comes about when this redraw function is called from a zoom.. the chart just reloads. I've had some success providing a function for the redraw event, however this has completely broken select-zooming (which just shows all data). Here's my handler:
function drawIt() {
var optionsTmp = options;
chart = new Highcharts.Chart(optionsTmp, function(chart) {
var text, box, point;
var count = 0;
for (var annotX in annots) {
var annot = annots[annotX];
if (annot.length > 0) {
//draw rectangles / text with annot vals
}
count++;
}
});
options = optionsTmp;
}
It's possible I'm not properly passing through the new extremes from the zoom (options.xAxis.min) but trying to pass this into the handler hasn't given me much luck.
Any ideas?
The problem is that you are trying to remove chart, and create new one - this is not possible just by setting empty container - you need to also destroy chart. But still this is not possible to destroy chart while redrawing - it will cause errors.
I think you should try another solution, for example updating that annotations. Something similar is done here with extra lines for points, see: http://www.highcharts.com/jsbin/oyadep/4/edit#javascript,live
Of course, it's not finished but works fine when zooming in.
function drawPaths(options) {
var maxNum = 2;
//add the paths
group_init = options.renderer.g().add();
// console.log(options);
for (i = 0; i < maxNum; i++) {
$.each(options.series[i].data, function(i2, point) {
var xpos = options.xAxis[0].translate(this.x);
var ypos = options.yAxis[0].translate(this.y, false, true);
var gr = this.y; //gross revenue
var p_gr = 0; //for tests
var eq; //equation
var my;
//console.log(this);
eq = gr + 1; //equation
my = options.yAxis[0].translate(eq, false, true);
//console.log(xpos, ypos, my);
options.renderer.path(['M', xpos + 105, ypos + 5, 'L', xpos + 105, my + 5]).attr({
'stroke-width': 1,
stroke: '#ad2b2b'
}).add(group_init);
});
}
}
Long story short, my setExtremes function (which gets called before a total redraw of the chart) was stomping on the feet of the following redraw function. I had to set the thresholds there and silence it early.
setExtremes: function(e) {
doZoom = true;
min= e.min;
max = e.max;
e.preventDefault();
e.stopPropagation();
}
Corrected fiddle:
http://jsfiddle.net/9LgUf/1/

The author of this Raphael JS extension has vanished from the face of the internet, help me with his script

The extension I'm talking about is the Raphael-zpd: http://pohjoisespoo.net84.net/src/raphael-zpd.js
/* EDIT The script is added to a Raphael document with this command var zpd = new RaphaelZPD(paper, { zoom: true, pan: true, drag: false}); where paper is your canvas */
The script was originally released at the authors github http://www.github.com/somnidea which no longer exists.
What I wanted to do was run the mousewheel zoom out to the threshold as soon as the raphael is loaded. The zoomthreshold is set at the beginning of the script zoomThreshold: [-37, 20]. In the mousewheel scroll function it is compared to zoomCurrent which is by default 0 me.zoomCurrent = 0;
This is the whole mousewheel event part
me.handleMouseWheel = function(evt) {
if (!me.opts.zoom) return;
if (evt.preventDefault)
evt.preventDefault();
evt.returnValue = false;
var svgDoc = evt.target.ownerDocument;
var delta;
if (evt.wheelDelta)
delta = evt.wheelDelta / 3600; // Chrome/Safari
else
delta = evt.detail / -90; // Mozilla
if (delta > 0) {
if (me.opts.zoomThreshold)
if (me.opts.zoomThreshold[1] <= me.zoomCurrent) return;
me.zoomCurrent++;
} else {
if (me.opts.zoomThreshold)
if (me.opts.zoomThreshold[0] >= me.zoomCurrent) return;
me.zoomCurrent--;
}
var z = 1 + delta; // Zoom factor: 0.9/1.1
var g = svgDoc.getElementById("viewport"+me.id);
var p = me.getEventPoint(evt);
p = p.matrixTransform(g.getCTM().inverse());
// Compute new scale matrix in current mouse position
var k = me.root.createSVGMatrix().translate(p.x, p.y).scale(z).translate(-p.x, -p.y);
me.setCTM(g, g.getCTM().multiply(k));
if (!me.stateTf)
me.stateTf = g.getCTM().inverse();
me.stateTf = me.stateTf.multiply(k.inverse());
}
The reason I can't just draw a smaller SVG to begin with is that I'm using raster images as the background and need them to be higher resolution. I would still like to start at the furthest point I've set at the threshold. Is it possible for me to somehow use this script to do this? I'm naturally using it otherwise to handle mouse zoom/pan.
//EDIT
There is also this function at the end of the script, but so far I've been unable to work it.
Raphael.fn.ZPDPanTo = function(x, y) {
var me = this;
if (me.gelem.getCTM() == null) {
alert('failed');
return null;
}
var stateTf = me.gelem.getCTM().inverse();
var svg = document.getElementsByTagName("svg")[0];
if (!svg.createSVGPoint) alert("no svg");
var p = svg.createSVGPoint();
p.x = x;
p.y = y;
p = p.matrixTransform(stateTf);
var element = me.gelem;
var matrix = stateTf.inverse().translate(p.x, p.y);
var s = "matrix(" + matrix.a + "," + matrix.b + "," + matrix.c + "," + matrix.d + "," + matrix.e + "," + matrix.f + ")";
element.setAttribute("transform", s);
return me;
}
Seems like it's used for panning through the document through say click events so that a click would execute the function with the given coordinates. However, as said I've been unable to work it. I don't know how it's supposed to function. I tried paper.ZPDPanTo(100, 100); as well as just ZPDPanTo(100,100) but nothing happens.
You may also want to check out the working branch for Raphaël 2.0, which supposedly adds support for viewBox and transforms, see https://github.com/DmitryBaranovskiy/raphael/tree/2.0.
This doesn't answer your question fully, but it seems quite possible that Raphaël 2.0 will address your use-case.
If you're using pure svg then you can manipulate the zoom&pan positions via the SVG DOM properties currentTranslate and currentScale, see this example.
An example using RAPHAEL ZPD:
var paper = Raphael("container",800,760);
window.paper = paper;
zpd = new RaphaelZPD(paper, { zoom: true, pan: true, drag: false });
paper.circle(100,100, 50).attr({fill:randomRGB(),opacity:0.95});
paper.rect(100,100, 250, 300).attr({fill:randomRGB(),opacity:0.65});
paper.circle(200,100, 50).attr({fill:randomRGB(),opacity:0.95});
paper.circle(100,200, 50).attr({fill:randomRGB(),opacity:0.95});
http://jsfiddle.net/4PkRm/1/

Categories

Resources