I am trying to implement some kind of Zoom/Pan features to maps generated with the google geochart API. There are some scripts online to zoom/pan svg images, but I am not succeeding in implementing them to the SVGs that the geochart api generates.
I am trying the SVGPan library http://code.google.com/p/svgpan/
The code I'm using to append the script to the SVG is:
google.visualization.events.addListener(chart, 'ready', function () {
var script = document.createElementNS('http://www.w3.org/2000/svg', 'script');
script.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', 'http://www.cyberz.org/projects/SVGPan/SVGPan.js');
var svg = document.getElementsByTagName('svg')[0];
svg.appendChild(script);
});
Here's the jsfiddle:
http://jsfiddle.net/cmoreira/CetaA/
With Firebug I can see the script is beeing place on the svg, but nothing happens, it doesn't implement the features in the svg, as expected.
I am not sure if this line is correct, since I didn't find any example online:
var script = document.createElementNS('http://www.w3.org/2000/svg', 'script');
Am I doing something wrong, or what I'm trying to do will be impossible?
Thanks for you help!
Cheers
Carlos
After some experiments, I was able to find a simple solution and since there were not any valuable answers so far, I decided to share what I have so far.
I decided to do it with CSS, redrawing the map with different values on zoom in/out and hide the overflow with CSS.
Here's a working example:
http://jsfiddle.net/cmoreira/CCUpf/
CSS
#canvas {
width:400px;
height:300px;
overflow:hidden;
}
#visualization {
top:0px;
left:0px;
}
Zoom/Pan Javascript
function zoomin() {
mw = mw+50;
mh = mh+50;
x = x-25;
y = y-25;
document.getElementById('visualization').style.top = y +'px';
document.getElementById('visualization').style.left = x +'px';
drawVisualization(mw,mh);
}
function zoomout() {
if(mw > 600) {
mw = mw-50;
mh = mh-50;
x = x+25;
y = y+25;
document.getElementById('visualization').style.top = y +'px';
document.getElementById('visualization').style.left = x +'px';
drawVisualization(mw,mh);
}
}
function left() {
x = x-50;
document.getElementById('visualization').style.left = x +'px';
}
function right() {
x = x+50;
document.getElementById('visualization').style.left = x +'px';
}
function up() {
y = y-50;
document.getElementById('visualization').style.top = y +'px';
}
function down() {
y = y+50;
document.getElementById('visualization').style.top = y +'px';
}
I know this is a very poor zoom/pan feature for the google geochart API, but it's something, right?
Any improvements to this simple aproach would be welcomed.
Thanks!
I would recommend you to try jVectorMap instead. It has support for the zoom/pan with mouse or touch events.
Related
So, I have been trying to create a platformer, but I only know so much about HTML. I have this snippet of code, and I am trying to figure out how to change the coordinates of it so I can finish my platformer.
var logo=document.createElement("img");
logo.src="https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png";
document.body.appendChild(logo);
//I am using the Google Logo so you can see the image.
If you mean a position of any element in page you can do it by using below code;
var logo=document.createElement("img");
logo.src="https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png";
document.body.appendChild(logo);
logo.style.position = "absolute";
logo.style.left = x_pos+'px';
logo.style.top = y_pos+'px';
Or do it as a function so you can attach it to an event like onmousedown
function placeDiv(x_pos, y_pos) {
var logo=document.createElement("img");
logo.src="https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png";
document.body.appendChild(logo);
logo.style.position = "absolute";
logo.style.left = x_pos+'px';
logo.style.top = y_pos+'px';
}
So Im trying to make something where the user is able to drag planets along their orbits and it will continuously update the other planets. Ideally I would like this to work with ellipses too.
So far I can drag an image node with jquery and check/change the coordinates, but i cannot update the position reliably while the user is dragging the object. I know there is an axis and a rectangle containment for draggable but this doesn't really help me.
I have a site for calculating planetary orbits http://www.stjarnhimlen.se/comp/ppcomp.html and a formula i think should help me if i can figure out how to constrain the draggable object with coordinate checks Calculating point on a circle's circumference from angle in C#?
But it seems like there should be an easier way to have a user drag a sphere along a circular track while it updates coords for other spheres
here's what i have so far. It's not much
http://jsfiddle.net/b3247uc2/2/
Html
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//code.jquery.com/ui/1.11.2/jquery-ui.js"></script>
Js
var $newPosX = 100,
$newPosY = 100;
//create image node
var x = document.createElement("IMG");
x.src = "http://upload.wikimedia.org/wikipedia/commons/a/a4/Sol_de_Mayo_Bandera_Argentina.png";
x.width = 100;
x.height = 100;
x.id = "sun";
x.hspace = 100;
x.vspace = 100;
document.body.appendChild(x);
//coords
var text = document.createTextNode($newPosX + " " + $newPosY);
document.body.appendChild(text);
//make sun draggable and update coords
$("#sun").draggable({
drag: function (event, ui) {
$newPosX = $(this).offset().left;
$newPosY = $(this).offset().top;
}
});
//0 millisecond update for displayed coords
setInterval(check, 0);
function check() {
//tries to constrain movement, doesn't work well
/*
if ($newPosX > 300) {
$("#sun").offset({
left: 200
});
}
*/
text.nodeValue = ($newPosX + " " + $newPosY);
}
Edit:
I found this and am trying to modify it to suit my purposes but have so far not had luck.
http://jsfiddle.net/7Asn6/
Ok so i got it to drag
http://jsfiddle.net/7Asn6/103/
This is pretty close to what I want but can be moved without being clicked on directly
http://jsfiddle.net/7Asn6/104/
Ok final edit on this question. This one seems to work well and have fixed my problems. I would still very much like to hear peoples implementation ideas or ideas to make it work smoother.
http://jsfiddle.net/7Asn6/106/
I'm making a small jigsaw like puzzle in html5. Each jigsaw piece is it's own canvas. I need to move the canvas element using the mouse position. I've managed to get the canvas that is clicked, I just need to move it. I tried manipulating the top and left style attributes but the canvas didn't move. Can this be done or am I trying something impossible.
Thanks!
function MouseDown(can, e)
{
MovingCanvas = can;
clicked = true;
}
function MouseMove(e)
{
if(clicked)
{
var mx = e.clientX;
var my = e.clientY;
MovingCanvas.style.top = my;
MovingCanvas.style.left = mx;
}
}
e.clientX and e.clientY are integers.
Styles expect a string of the form {NUMBER}{UNIT}.
You are missing a unit, therefore it won't work.
MovingCanvas.style.top = my+"px";
MovingCanvas.style.left = mx+"px";
Not sure exactly what to call it, but I am looking for a way to create a dotted outline/selection box effect via javascript/svg when you click and drag over an area, and then goes away on mouseUp (that could be added if it wasn't an original part) .
A jQuery library would be nice if it exists. I've done some looking around, and haven't found exactly what I am looking for.
I guess the theory would be get the coord from the first click, track the mouse coord moment and adjust the box accordingly.
But not writing it from scratch would be nice.
Here's a demo I made just for you :)
Demo (Static): http://jsfiddle.net/HNH2f/1/
Demo (Animated): http://jsfiddle.net/HNH2f/2/
You can use CSS to control the visual style of the marquee.
You can pass one or two functions to the trackMarquee method; both will be called with four arguments: the x1,y1,x2,y2 bounds of the marquee. The first function will be called when the marquee is released. The second function (if present) will be called each time the marquee moves (so that you can, for example, calculate what items are within that bounding box).
When you start dragging on the SVG document (or whatever element you choose to track) it will create a <rect class="marquee" />; during dragging it will adjust the size of the rectangle. Use CSS (as seen in the demo) to style this rectangle however you want. I'm using the stroke-dasharray property to make the border dotted.
For Stack Overflow posterity, here's the code (on the off chance that JSFiddle is down):
(function createMarquee(global){
var svgNS = 'http://www.w3.org/2000/svg',
svg = document.createElementNS(svgNS,'svg'),
pt = svg.createSVGPoint();
// Usage: trackMarquee( mySVG, function(x1,y1,x2,y2){}, function(x1,y1,x2,y2){} );
// The first function (if present) will be called when the marquee is released
// The second function (if present) will be called as the marquee is changed
// Use the CSS selector `rect.marquee` to select the marquee for visual styling
global.trackMarquee = function(forElement,onRelease,onDrag){
forElement.addEventListener('mousedown',function(evt){
var point0 = getLocalCoordinatesFromMouseEvent(forElement,evt);
var marquee = document.createElementNS(svgNS,'rect');
marquee.setAttribute('class','marquee');
updateMarquee(marquee,point0,point0);
forElement.appendChild(marquee);
document.documentElement.addEventListener('mousemove',trackMouseMove,false);
document.documentElement.addEventListener('mouseup',stopTrackingMove,false);
function trackMouseMove(evt){
var point1 = getLocalCoordinatesFromMouseEvent(forElement,evt);
updateMarquee(marquee,point0,point1);
if (onDrag) callWithBBox(onDrag,marquee);
}
function stopTrackingMove(){
document.documentElement.removeEventListener('mousemove',trackMouseMove,false);
document.documentElement.removeEventListener('mouseup',stopTrackingMove,false);
forElement.removeChild(marquee);
if (onRelease) callWithBBox(onRelease,marquee);
}
},false);
};
function callWithBBox(func,rect){
var x = rect.getAttribute('x')*1,
y = rect.getAttribute('y')*1,
w = rect.getAttribute('width')*1,
h = rect.getAttribute('height')*1;
func(x,y,x+w,y+h);
}
function updateMarquee(rect,p0,p1){
var xs = [p0.x,p1.x].sort(sortByNumber),
ys = [p0.y,p1.y].sort(sortByNumber);
rect.setAttribute('x',xs[0]);
rect.setAttribute('y',ys[0]);
rect.setAttribute('width', xs[1]-xs[0]);
rect.setAttribute('height',ys[1]-ys[0]);
}
function getLocalCoordinatesFromMouseEvent(el,evt){
pt.x = evt.clientX; pt.y = evt.clientY;
return pt.matrixTransform(el.getScreenCTM().inverse());
}
function sortByNumber(a,b){ return a-b }
})(window);
You are lucky I just made this myself. I'm using jQuery SVG plugin ( http://keith-wood.name/svg.html )
$("#paper2").mousedown(function(ev) {
ev.preventDefault();
var pX= (ev.pageX - this.offsetLeft) * viewBox[2]/parseInt($("#paper2").css("width"));
var pY= (ev.pageY - this.offsetTop) * viewBox[3]/parseInt($("#paper2").css("height"));
var rect = svg2.rect(
pX, //X
pY, //Y
1,1, //width and height
{ //Settings, you can make the box dotted here
fill: 'black', "fill-opacity": 0.3, stroke: 'red', strokeWidth: 3, id:rect
}
)
$("#paper2").mousemove(function(ev) {
ev.preventDefault();
var rect= $('#rect');
var pX= (ev.pageX - this.offsetLeft) * viewBox[2]/parseInt($("#paper2").css("width")) - rect.attr("x");
var pY= (ev.pageY - this.offsetTop) * viewBox[3]/parseInt($("#paper2").css("height")) - rect.attr("y");
rect.attr("width", pX);
rect.attr("height", pY);
});
$("#paper2").mouseup(function(ev) {
ev.preventDefault();
var div= $("#paper2");
div.unbind('mousemove');
div.unbind('mouseup');
})
});
paper2 is a div in which I have an svg element (so the svg element and the div have the same height/width). This is how I created the svg2 element:
var svg2;
var root2;
$(document).ready(function() {
$("#paper2").svg({
onLoad: function() {
svg2= $("#paper2").svg('get');
svg2.configure({id: 'svg2'});
var div= $("#paper2");
root2= svg2.root();
$("#svg2").attr("viewBox", viewBox[0]+','+viewBox[1]+','+viewBox[2]+','+viewBox[3]);
},
settings: {}
});
}
If you not using viewbox on the svg element you don't need this on the calculations:
* viewBox[2]/parseInt($("#paper2").css("*****"));
viewbox[2] would be the viewbox width and viewbox[3] would be the viewbox height.
Based on Creating an HTML 5 canvas painting application I created a HTML5 canvas painting application. It works fine, but after creating each object I just need to drag the objects.
Working demo
How to implement drag and drop of the figures?
When the user clicks on the canvas, you have to check the coordinates (compare it to the coordinates for the objects), and see if it's on an object. E.g. You can test if a point (e.g. the coordinates for the mousedown even) is within a circle with this method:
function (pt) {
return Math.pow(pt.x - point.x,2) + Math.pow(pt.y - point.y,2) <
Math.pow(radius,2);
};
If the mousedown is on the object, you have to change the objects coordinates according to how the mouse is moved.
Here is an example, where you can drag a circle:
<!DOCTYPE html>
<html>
<head>
<script>
window.onload = function() {
drawCircle(circle);
element = document.getElementById('canvas');
element.addEventListener('mousedown', startDragging, false);
element.addEventListener('mousemove', drag, false);
element.addEventListener('mouseup', stopDragging, false);
element.addEventListener('mouseout', stopDragging, false);
}
function mouseX(e) {
return e.clientX - element.offsetLeft;
}
function mouseY(e) {
return e.clientY - element.offsetTop;
}
var Point = function (x, y) {
this.x = x;
this.y = y;
return this;
}
var Circle = function (point, radius) {
this.point = point;
this.radius = radius;
this.isInside = function (pt) {
return Math.pow(pt.x - point.x, 2) + Math.pow(pt.y - point.y, 2) <
Math.pow(radius, 2);
};
return this;
}
function startDragging(e) {
var p = new Point(e.offsetX, e.offsetY);
if(circle.isInside(p)) {
deltaCenter = new Point(p.x - circle.point.x, p.y - circle.point.y);
}
}
function drag(e) {
if(deltaCenter != null) {
circle.point.x = (mouseX(e) - deltaCenter.x);
circle.point.y = (mouseY(e) - deltaCenter.y);
drawCircle(circle);
}
}
function stopDragging(e) {
deltaCenter = null;
}
function drawCircle(circle) {
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(circle.point.x, circle.point.y, circle.radius, 0, Math.PI*2, true);
ctx.fill();
}
var circle = new Circle(new Point(30, 40), 25);
var deltaCenter = null;
var element;
</script>
</head>
<body>
<canvas id="canvas" width="400" height="300"></canvas>
</body>
</html>
Try it on jsFiddle
The same effect can be accomplished using Raphael.js (http://raphaeljs.com/) with Joint.jS (http://www.jointjs.com/).
Shapes created with Raphael can be accessed like any DOM element and can be manipulated via attributes. It is an awesome framework.
Joint.js helps in connecting the shapes. They also have a diagramming library and can help create ERD, Statemachine and several common diagrams. The best part is that you can extend their diagram element and create your own custom elements. Its jaw-dropingly cool.
Checkout their demos with source code at http://www.jointjs.com/demos
If you are using the raphael as "raw" lib you must handle the undo/redo by yourself.
The graphiti lib did have Undo/Redo Stack inside and supports the export for SVG, PNG, JSON,...
Additional you have some kind of Viso like connectors and ports.
http://www.draw2d.org/graphiti/jsdoc/#!/example
Greetings
I don't think there's an easy way to do this.
If you're just dealing with lines, my approach would be to keep track of all lines created, with starting coordinates, ending coordinates and some kind of z-index. When the user starts a dragging action (onmousedown), you have to check if the point is near the line, and then update the object and redraw the canvas when the mouse is moved.
How can I tell if a point belongs to a certain line?
This gets a lot more complicated if you're dealing with complex objects though. You'll probably have to find a solution to check if a point is inside a path.
Objects drawn into HTML5 Canvas are turned into pixels and then forgotten. You can't adjust properties on them and have the canvas update to see the effects. You can remember them yourself, but the canvas will still have those pixels set, so you'd have to basically redraw the whole canvas (or at least some of it) when you adjust a property.
You might want to consider SVG for this application instead, SVG elements are remembered in the DOM and when their properties are updated the browser will update the graphic to reflect the changes.
If you must use canvas, then you're going to need to write quite a bit of code to handle mouse-hits, object properties, and repaints.