Is there any way to write with jQuery in html. Hence when a user click any one of the shown image, a line between them will be appeared. I found a lot of info about " how to do it in canvas",and I found it is possible to just use the images in html file.
For example when clicked on id1 and the id4 a line appears that connects them.
Thanks to #LastCoder:
This won't work in IE8 or below due to CSS restrictions. It uses pure Javascript, no need for jQuery.
function getOffset( el ) { // return element top, left, width, height
var _x = 0;
var _y = 0;
var _w = el.offsetWidth|0;
var _h = el.offsetHeight|0;
while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
_x += el.offsetLeft - el.scrollLeft;
_y += el.offsetTop - el.scrollTop;
el = el.offsetParent;
}
return { top: _y, left: _x, width: _w, height: _h };
}
function connect(div1, div2, color, thickness) { // draw a line connecting elements
var off1 = getOffset(div1);
var off2 = getOffset(div2);
// bottom right
var x1 = off1.left + off1.width;
var y1 = off1.top + off1.height;
// top right
var x2 = off2.left + off2.width;
var y2 = off2.top;
// distance
var length = Math.sqrt(((x2-x1) * (x2-x1)) + ((y2-y1) * (y2-y1)));
// center
var cx = ((x1 + x2) / 2) - (length / 2);
var cy = ((y1 + y2) / 2) - (thickness / 2);
// angle
var angle = Math.atan2((y1-y2),(x1-x2))*(180/Math.PI);
// make hr
var htmlLine = "<div style='padding:0px; margin:0px; height:" + thickness + "px; background-color:" + color + "; line-height:1px; position:absolute; left:" + cx + "px; top:" + cy + "px; width:" + length + "px; -moz-transform:rotate(" + angle + "deg); -webkit-transform:rotate(" + angle + "deg); -o-transform:rotate(" + angle + "deg); -ms-transform:rotate(" + angle + "deg); transform:rotate(" + angle + "deg);' />";
//
// alert(htmlLine);
document.body.innerHTML += htmlLine;
}
Simply call it by using something like:
<a onclick="testIt();">Draw line</a>
window.testIt = function() {
var div1 = document.getElementById('div1');
var div2 = document.getElementById('div2')
connect(div1, div2, "#0F0", 2);
}
Here is the working example.
I think Using HTML-5 you can do this.Please try this link.Html-5
Related
Drawing a single self-link on a node in a node-link diagram can be done as described here: D3 Force Layout Self-Linking Node
What would you change if you need to draw multiple links on the same node?
I tried to add a 'rotation' to it based on the number of self-links that exist.
Given the code from the linked example I made the following changes:
function tick() {
link.attr("d", function(d) {
var x1 = d.source.x,
y1 = d.source.y,
x2 = d.target.x,
y2 = d.target.y,
dx = x2 - x1,
dy = y2 - y1,
dr = Math.sqrt(dx * dx + dy * dy),
// Defaults for normal edge.
drx = dr,
dry = dr,
xRotation = 0, // degrees
largeArc = 0, // 1 or 0
sweep = 1; // 1 or 0
// Self edge.
if ( x1 === x2 && y1 === y2 ) {
// Fiddle with this angle to get loop oriented.
var index = getIndexOfDuplicateEdge();
var degree = 360 / numberOfDuplicateEdges();
var degreeForIndex = degree * index;
xRotation = degreeForIndex; // Previously: -45;
// Needs to be 1.
largeArc = 1;
// Change sweep to change orientation of loop.
//sweep = 0; // I also tried to change it based on index % 2
// Make drx and dry different to get an ellipse
// instead of a circle.
drx = 30;
dry = 20;
// For whatever reason the arc collapses to a point if the beginning
// and ending points of the arc are the same, so kludge it.
x2 = x2 + 1;
y2 = y2 + 1;
}
return "M" + x1 + "," + y1 + "A" + drx + "," + dry + " " + xRotation + "," + largeArc + "," + sweep + " " + x2 + "," + y2;
});
This won't draw my ellipses as expected and I cannot find a way to handle this. Based on SVG from Mozilla the large-arc has to be 1. Sweep can be 0 or 1 and will 'mirror' my ellipsis. I can use xRotation between 90-180 with sweep 0/1 which will cover 180 degrees of my circle. However, i do not find a way to draw the ellipsis at the other 180 degree positions.
The number of self-links can vary, and I always want to have the 'best' distribution between ellipsis.
Ideally, it should look like:
The idea is to divide the circle into as many segments as petals your flower has. Then calculate the start- and end points for each petal on the circle and fitting an elipse on those points.
You can use the following code snippet to do achieve this: (the function assumes you have a svg element with the id "svgthing")
function radtodeg(angle) {
return angle * (180/Math.PI);
}
function flower( center_x, center_y, num_self_edges, start_angle, end_angle, radius, length ) {
var angle_sector = end_angle - start_angle;
var num_points = num_self_edges * 2;
var angle_per_point = angle_sector / num_points;
var angle_per_sector = angle_per_point * 2;
var str_builder = [];
for( var angle = start_angle; angle < end_angle; angle += angle_per_sector ) {
var start_sector_angle = angle;
var end_sector_angle = angle + angle_per_point;
var mid_sector_angle = angle + angle_per_point / 2;
var start_x = center_x + (radius * Math.cos(start_sector_angle));
var start_y = center_y + (radius * Math.sin(start_sector_angle));
var end_x = center_x + (radius * Math.cos(end_sector_angle));
var end_y = center_y + (radius * Math.sin(end_sector_angle));
var mid_x = center_x + (radius * Math.cos(mid_sector_angle));
var mid_y = center_y + (radius * Math.sin(mid_sector_angle));
str_builder.push("<path d='");
str_builder.push("M" + start_x + " " + start_y + ",");
str_builder.push("A " + length + " 1 " + radtodeg(mid_sector_angle) + " 0 1 " + end_x + " " + end_y);
str_builder.push("'/>\n");
str_builder.push("<circle cx='" + start_x + "' cy='" + start_y + "' r='5' />\n");
str_builder.push("<circle cx='" + end_x + "' cy='" + end_y + "' r='5'/>\n");
str_builder.push("<circle cx='" + mid_x + "' cy='" + mid_y + "' r='5'/>\n");
}
str_builder.push("<circle cx='" + center_x + "' cy='" + center_y + "' r='" + radius + "' />\n");
$("#svgthing").html(str_builder.join(""));
}
flower(60, 50, 8, 0, 2 * Math.PI, 50, 10);
The example call will generate a flower with 8 petals.
I am helping someone solve this. We have SVG elements in g tag. We are using the transformation on the g tag and converting it into matrix.
Here is the jsbin link.
We are a bit confused by the following:
How to calculate the 'resistor', denoted by dx & dy in line 23 & 24 in the jsbin.
Where we are going wrong with the calculations for rotate & scale using matrices.
Is there a better way of doing this mathematically? I need to mention that the relevant person has been struggling with this for over two weeks so appreciate the help.
The relevant Js code:
var mouseXY; //object to store the mouse event x & y
var angle1; //first angle before rotate
var center; //center of the shape that we want to rotate
//this function is call when mouse down
//on the resize button
//this function bind the mouse event
//to scale the shape
function bindScaleShapeEvent(){
mouseXYPosition(event);
document.addEventListener("mousemove",scaleImage);
document.addEventListener("mouseup",removeScaleImage);
}
//function to scale the shape when
//user mouse move
function scaleImage(event){
var parent = document.getElementById("1"); //parent of shape
var dx = (event.pageX - mouseXY.x);
var dy = (event.pageY - mouseXY.y);
dx /= 80; // 80 is the resistor value
dy /= 80;
var scaleX = dx;
var scaleY = dy;
var matrix = parent.getAttribute("transform");
var matrixArray = matrixToArray(matrix);
//make the new matrix of the shape
scaleString = "matrix(" +
(parseFloat(matrixArray[0]) + scaleX) + " " +
parseFloat(matrixArray[1]) + " " +
parseFloat(matrixArray[2]) + " " +
(parseFloat(matrixArray[3]) + scaleY) + " " +
parseFloat(matrixArray[4]) + " " +
parseFloat(matrixArray[5]) +")";
parent.setAttribute("transform", scaleString);
mouseXYPosition(event);
}
//convert the transformation to array
function matrixToArray(matrix){
matrix = matrix.split('(');
matrix = matrix[1].split(')');
matrix = matrix[0].split(' ');
return matrix;
}
//remove eventListener from the
//shape resize button
function removeScaleImage(){
document.removeEventListener("mousemove",scaleImage);
document.removeEventListener("mouseup",removeScaleImage);
}
//this function is call when mouse down
//on the rotate button
//this function bind the mouse event
//to rotate the shape
function bindRotateShapeEvent(event){
mouseXYPosition(event);
centerXY();//store the center of the shape that we want to rotate
var dx = (mouseXY.x) *3;
var dy = (mouseXY.y) *3;
angle1 = (180 * Math.atan2(dy, dx) / Math.PI); //first angle of the shape
document.addEventListener("mousemove",rotateImage);
document.addEventListener("mouseup",removeRotateImage);
}
//applying rotation on the shape
function rotateImage(event){
var dx = (event.pageX - mouseXY.x);
var dy = (event.pageY - mouseXY.y);
var parent = document.getElementById("1");
var angle2 = (180 * (Math.atan2(dy,dx)) / Math.PI);
var angle = angle2 - angle1;
var radian= 0.06 * angle;
var cos = Math.cos(radian);
var sin = Math.sin(radian);
var matrix ='matrix(' +
(cos) +
' ' + (sin) +
' ' + (-sin) +
' ' + (cos) +
' ' + (-center.cx * cos + center.cy * sin + center.cx) +
' ' + (-center.cx * sin - center.cy * cos + center.cy) +
')';
parent.setAttribute("transform",matrix);
}
function removeRotateImage(){
document.removeEventListener("mousemove",rotateImage);
document.removeEventListener("mouseup",removeRotateImage);
}
//save the center of the shape
function centerXY(){
var shape = document.getElementById("1");
var bbox = shape.getBBox();
center ={
cx: bbox.x + (bbox.width / 2),
cy: bbox.y + (bbox.height / 2)
};
}
//save the move x and y
function mouseXYPosition(event){
mouseXY = {
x: event.pageX,
y: event.pageY
};
}
The Jsbin link to the working demo.
Using Raphael JS I want to have a continuing sweeping clock.
I have kind of got it implemented but there is a problem.
The transform on the animation constantly requries a increasing integer to rotate. Eventually I'm going to hit a max integer.
If I use 0,90,180,270. When it hits 0 again it goes back the other way.
CodePen ->
http://codepen.io/ianw92/pen/yNLdZz
Javascript:
var svg = Raphael("container",400,400),
triangle = svg.path("M210 200L190 200L200 100Z").attr({fill:"#000"}),
circle = svg.circle(200,200,5).attr({fill:"#f00"});
// Rotation settings
var handAngle = 45,
centerX = 200,
centerY = 200;
y = 0
function a() {
y = (y + 90) % 360;
triangle.animate({transform: "r"+y + "," + centerX + "," + centerY}, 500, b);
}
function b() {
y = (y + 90) % 360;
triangle.animate({transform: "r"+y + "," + centerX + "," + centerY}, 500, a);
}
a()
Key was to use Raphael.animation(...).repeat(Infinity)
http://codepen.io/ianw92/pen/xGbKJq
var anim=Raphael.animation({transform: "r360" + "," + centerX + "," + centerY}, 60000, 'linear').repeat(Infinity);
triangle.animate(anim);
In my code I am resizing the Konva canvas and the konvajs-content elements to maintain a full screen appearance.
resizeCanvas()
window.onresize = function() { resizeCanvas(); }
function resizeCanvas() {
var w = window,
d = document,
e = d.documentElement,
g = d.getElementsByTagName('body')[0],
x = w.innerWidth || e.clientWidth || g.clientWidth,
y = w.innerHeight || e.clientHeight || g.clientHeight;
var ratio = x / y;
var should = 1920 / 1080;
var cv = document.getElementsByTagName("canvas")[0];
var cc = document.getElementsByClassName("konvajs-content")[0];
var cx, cy;
var cleft=0;
if(ratio > should) {
cx = (y * 1920/1080);
cy = y;
cleft = (x - cx) / 2;
} else {
cx = x;
cy = (x * 1080/1920);
cv.setAttribute("style", "width:" + x + "px;height:"+ (x*1080/1920) + "px;");
}
cc.setAttribute("style", "width:" + x + "px;height: " + y + "px;");
cv.setAttribute("style", "width:" + cx + "px;heigth: " + cy + "px; position: relative; left: " + cleft + "px");
}
This all works great until I try to capture any event input. As you can see in the JSFiddle, when trying to click on the 'Test' text you will not cause an event to fire.
But if you click ~40px to the left and 20px above the text you will fire the events.
https://jsfiddle.net/3qc3wtr3/2/
Is there a way to keep the resize behavior and to ensure that events are being fired based on the actual location of the elements?
You should use stage.width() and stage.height() to fit a page. You can add scale into stage if you need.
I have a problem with Raphael.js. I want to rotate the "compassScale" - set in the following code - in a relative manner.
This works for the paths, but all the texts "animate" to the absolute rotation of 30 degree. I want them to rotate to the 30 degrees relative from their actual positions.
var compassScale = paper.set();
var centerX = 200;
var centerY = 200;
var radius = 195;
var compasCircle = paper.circle(centerX, centerY, radius);
for(var i = 0; i < 360; i++) {
var winkelRad = i * (Math.PI/180)
var xStart = centerX + Math.sin(winkelRad) * radius;
var yStart = centerY + Math.cos(winkelRad) * radius;
var diff = 6;
if(i % 10 === 0){
compassScale.push(paper.text(centerX, centerY - radius + 18, i).rotate(i, centerX, centerY, true));
diff = 12;
} else if(i % 5 === 0) {
diff = 8;
}
var xEnd = centerX + Math.sin(winkelRad) * (radius - diff);
var yEnd = centerY + Math.cos(winkelRad) * (radius - diff);
compassScale.push(paper.path("M" + xStart + " " + yStart + " L" + xEnd + " " + yEnd));
}
compassScale.animate({rotation:"30 " + centerX + " " + centerY}, 5000);
Like you said, the problem is that you're animating all elements to 30 degrees, not their current rotation + 30 degrees. It's actually quite simple once you think of it that way. Here is your revised code that works:
var compassScale = paper.set();
var texts = []; // array to hold the text elements
var centerX = 200;
var centerY = 200;
var radius = 195;
var compasCircle = paper.circle(centerX, centerY, radius);
for(var i = 0; i < 360; i++) {
var winkelRad = i * (Math.PI/180)
var xStart = centerX + Math.sin(winkelRad) * radius;
var yStart = centerY + Math.cos(winkelRad) * radius;
var diff = 6;
if(i % 10 === 0){
texts.push(paper.text(centerX, centerY - radius + 18, i).rotate(i, centerX, centerY, true));
diff = 12;
} else if(i % 5 === 0) {
diff = 8;
}
var xEnd = centerX + Math.sin(winkelRad) * (radius - diff);
var yEnd = centerY + Math.cos(winkelRad) * (radius - diff);
compassScale.push(paper.path("M" + xStart + " " + yStart + " L" + xEnd + " " + yEnd));
}
compassScale.animate({rotation:"30 " + centerX + " " + centerY}, 5000);
// loop through the text elements, adjusting their rotation by adding 30 to their individual rotation
for (var i = 0, l = texts.length; i < l; i += 1) {
// node.attr("rotation") returns something like 50 200 200, so we have to split the string and grab the first number with shift
texts[i].animate({rotation: (30 + +texts[i].attr("rotation").split(" ").shift()) + " " + centerX + " " + centerY}, 5000);
}
Just a quick observation:
Looks like "Rotation" isn't part of the Atrr anymore since ver 2, so you can't use it in "animate", but you can replace that with "transform: "r" + (some degree)"..
eg:
element.animate( {transform: "r" + (-90)}, 2000, 'bounce');