I would like to draw a ellipse shape by svg path. I use below code but the shape looks like a round corner rectangle.
How can I adjust the parameter to get a eclipse path!
demo()
function demo() {
var width = 400
var height = 200
var margin = {left:20,top:20,right:20,bottom:20}
var svg = d3.select('body').append("svg")
.attr('width',width + margin.left + margin.right)
.attr('height',height + margin.top + margin.bottom)
.style('border','2px solid red')
var g = svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`)
var data = [[0,0,400,100]]
g.selectAll('node')
.data(data)
.enter()
.append('path')
.attr('class','node')
.attr('stroke','black')
.attr('stroke-width',2)
.attr('fill','none')
.attr('d',function(d) {
var w = d[2]
var h = d[3]
var ax = d[0]
var ay = d[1] + h/2
var bx = ax + w/2
var by = ay + h/2
var cx = ax + w
var cy = ay
var dx = bx
var dy = d[1]
add_dot(g,[[ax,ay],[bx,by],[cx,cy],[dx,dy]])
var path = ['M',ax,ay,'C',ax,by,ax,by,bx,by,
cx,by,cx,by,cx,cy,
cx,dy,cx,dy,dx,dy,
ax,dy,ax,dy,ax,ay]
return path.join(' ')
})
function add_dot(g,data) {
g.selectAll('dots')
.data(data)
.join('circle')
.attr('class','dots')
.attr('cx',d => d[0])
.attr('cy',d => d[1])
.attr('r',5)
.attr('fill','red')
}
}
<script src="https://d3js.org/d3.v6.min.js"></script>
The path C command draws a cubic bezier segment, which can approximate an ellipse, but is not exact. The A command draws an arc segment, a sector of an ellipse.
demo()
function demo() {
var width = 400
var height = 200
var margin = {left:20,top:20,right:20,bottom:20}
var svg = d3.select('body').append("svg")
.attr('width',width + margin.left + margin.right)
.attr('height',height + margin.top + margin.bottom)
.style('border','2px solid red')
var g = svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`)
var data = [[0,0,400,100]]
g.selectAll('node')
.data(data)
.enter()
.append('path')
.attr('class','node')
.attr('stroke','black')
.attr('stroke-width',2)
.attr('fill','none')
.attr('d',function(d) {
var w = d[2]/2
var h = d[3]/2
var ax = d[0]
var ay = d[1] + h
var bx = ax + w
var by = ay + h
var cx = ax + w*2
var cy = ay
var dx = bx
var dy = d[1]
add_dot(g,[[ax,ay],[bx,by],[cx,cy],[dx,dy]])
var path = ['M',ax,ay,'A',w,h,0,0,0,bx,by,
w,h,0,0,0,cx,cy,
w,h,0,0,0,dx,dy,
w,h,0,0,0,ax,ay]
return path.join(' ')
})
function add_dot(g,data) {
g.selectAll('dots')
.data(data)
.join('circle')
.attr('class','dots')
.attr('cx',d => d[0])
.attr('cy',d => d[1])
.attr('r',5)
.attr('fill','red')
}
}
<script src="https://d3js.org/d3.v6.min.js"></script>
demo()
function demo() {
var width = 400
var height = 200
var margin = {left:20,top:20,right:20,bottom:20}
var svg = d3.select('body').append("svg")
.attr('width',width + margin.left + margin.right)
.attr('height',height + margin.top + margin.bottom)
.style('border','2px solid red')
var g = svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`)
var data = [[0,0,400,100]]
g.selectAll('node')
.data(data)
.enter()
.append('path')
.attr('class','node')
.attr('stroke','black')
.attr('stroke-width',2)
.attr('fill','none')
.attr('d',function(d) {
var w = d[2]
var h = d[3]
var ax = d[0]
var ay = d[1] + h/2
var bx = ax + w/2
var by = ay + h/2
var cx = ax + w
var cy = ay
var dx = bx
var dy = d[1]
add_dot(g,[[ax,ay],[bx,by],[cx,cy],[dx,dy]])
var path = ['M',ax,ay,'C',ax,(ay+by)/2,(ax+bx)/2,by,bx,by,
(bx+cx)/2,by,cx,(by+cy)/2,cx,cy,
cx,(cy+dy)/2,(cx+dx)/2,dy,dx,dy,
(dx+ax)/2,dy,ax,(dy+ay)/2,ax,ay]
return path.join(' ')
})
function add_dot(g,data) {
g.selectAll('dots')
.data(data)
.join('circle')
.attr('class','dots')
.attr('cx',d => d[0])
.attr('cy',d => d[1])
.attr('r',5)
.attr('fill','red')
}
}
<script src="https://d3js.org/d3.v6.min.js"></script>
Related
I drew a multi-line chart that changes based on some user input. I want to add a reference line to always appear at the value y=100. I was able to manually place a line, but it is not always exactly at y=100.
Formatting issues aside, this is what I have for one possible user input. As you can see, the reference line is slightly below 100:
And my code:
const svg = d3.select("svg");
const width = +svg2.attr("width");
const height = +svg2.attr("height");
const render = data =>{
const xValue = d => +d.week;
const yValue = d => +d.power_score;
const margin = {top:50, right:70, bottom:60, left:20};
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
const colorValue = d => d.team;
// define scales
const xScale = d3.scaleLinear()
.domain([1, d3.max(data, xValue)])
.range([0, innerWidth-250])
.nice();
const yScale = d3.scaleLinear()
.domain([d3.min(data, yValue)-10, d3.max(data, yValue)+10])
.range([innerHeight, 0])
.nice();
const colorScale = d3.scaleOrdinal(d3.schemeCategory10);
const g = svg2.append("g")
.attr('transform', 'translate(75, 50)');
// create axes
const xAxis = d3.axisBottom(xScale)
.tickSize(-innerHeight-10);
const yAxis = d3.axisLeft(yScale)
.tickSize(-innerWidth+240);
const xAxisG = g.append("g").call(xAxis)
.attr("transform", "translate(0, 400)");
xAxisG.select(".domain")
.remove();
xAxisG.append("text")
.attr("class", "axis-label")
.attr("y", 40)
.attr("x", (innerWidth-250)/2)
.attr("fill", "black")
.text("Week");
const yAxisG = g.append("g").call(yAxis)
.attr("transform", "translate(-10, 0)")
.select(".domain")
.remove();
yAxisG.append("text")
.attr("class", "axis-label")
.attr("transform", "rotate(-90)")
.attr("y", -35)
.attr("x", -innerHeight/4)
.attr("fill", "black")
.text("Power Score");
// generate line
const lineGenerator = d3.line()
.x(d => xScale(xValue(d)))
.y(d => yScale(yValue(d)));
// sort data for legend
const lastYValue = d =>
yValue(d.values[d.values.length - 1]);
// group data
const nested = d3.nest()
.key(colorValue)
.entries(data)
.sort((a, b) =>
d3.descending(lastYValue(a), lastYValue(b)));
colorScale.domain(nested.map(d => d.key));
// manually add horizonal line here
svg2.append("g")
.attr("transform", "translate(75, 267)")
.append("line")
.attr("x2", innerWidth-250)
.style("stroke", "black")
.style("stroke-width", "2px")
.style("stroke-dasharray", "3, 3");
// add lines with mouseover effect
g.selectAll(".line-path").data(nested)
.enter().append("path")
.attr("class", "line-path")
.attr("d", d => lineGenerator(d.values))
.attr("stroke", d => colorScale(d.key))
.attr("stroke-width", "3")
.attr("opacity", "0.5")
.on("mouseover", function(d, i) {
d3.select(this).transition()
.duration("50")
.attr("stroke-width", "5")
.attr("opacity", "1")})
.on("mouseout", function(d, i) {
d3.select(this).transition()
.duration("50")
.attr("stroke-width", "3")
.attr("opacity", "0.5")});
d3.line()
.x(d => xScale(xValue(d)))
.y(d => yScale(yValue(d)));
// draw legend
const colorLegend = (selection, props) => {
const {
colorScale,
circleRadius,
spacing,
textOffset
} = props;
const groups = selection.selectAll('g')
.data(colorScale.domain());
const groupsEnter = groups
.enter().append('g')
.attr('class', 'tick');
groupsEnter
.merge(groups)
.attr('transform', (d, i) =>
`translate(0, ${i * spacing})`
);
groups.exit().remove();
groupsEnter.append('circle')
.merge(groups.select('circle'))
.attr('r', circleRadius)
.attr('fill', colorScale);
groupsEnter.append('text')
.merge(groups.select('text'))
.text(d => d)
.attr('dy', '0.32em')
.attr('x', textOffset);
}
svg2.append("g")
.attr("transform", "translate(710, 60)")
.call(colorLegend, {
colorScale,
circleRadius: 4,
spacing: 15,
textOffset: 8
});
// Title
g.append("text")
.attr("class", "title")
.attr("x", (innerWidth-250)/4)
.attr("y", -10)
.text("Weekly Power Ranks");
};
d3.csv('data.csv').then(data => {
data.forEach(d => {
d.week = +d.week;
d.power_score = +d.power_score;
});
render(data);
});
Instead of using magic numbers...
svg2.append("g")
.attr("transform", "translate(75, 267)")
//magic numbers---------------^----^
...use the same y scale you're using to paint the paths:
g.append("g")
.attr("transform", `translate(75, ${yScale(100)})`)
Also, append it to the already translated <g>, or apply the same translation (again, more magic numbers in your code...).
I am new to D3 and have been working along with examples and changing correcting my code using those examples,
Below is my D3 Code and it works well except the bar chart is not scaled, I seem to not understand the error on my part.
The data is coming from a TSV file and has 2 columns, 1- Categorical 2- Numerical.
<script type = "text/javascript">
var margin = { top:80, bottom:80, right:80, left:80},
width = 960 - margin.left-margin.right,
height = 500 - margin.top - margin.bottom;
var svg = d3.select("body").append("svg")
.attr("width",width + margin.left + margin.right)
.attr("height",height + margin.top + margin. bottom)
.append("g")
.attr("transform", "translate("+margin.left+"," + margin.bottom+")");
d3.tsv("ticket.tsv",function(error, data) {
dataset = data.map(function(d) { return [d["ticket"],+d["num"] ] ;})
var xScale = d3.scale.ordinal()
.domain(data.map(function(d){ return [d.ticket];}))
.rangeRoundBands([0,width],0.1);
var yScale = d3.scale.linear()
.domain([0,d3.max(data,function(d) { return Math.max([d.num]);})])
.range([height, 0]);
var rect = svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x",function(d,i) { return xScale(d.ticket);})
.attr("y",function (d) { return height - (yScale (d.num)) ;})
.attr("height",function (d) { return d.num;})
.attr("width",xScale.rangeBand())
.attr("fill","orange");
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0"+","+(height)+")")
.call(xAxis);
svg.append("g")
.attr("class", "axis")
.call(yAxis);
;});
</script>
Here is the Solved Code and Picture, I have commented the code I have corrected.
<script type = "text/javascript">
var margin = { top:80, bottom:80, right:80, left:80},
width = 960 - margin.left-margin.right,
height = 500 - margin.top - margin.bottom;
var svg = d3.select("body").append("svg")
.attr("width",width + margin.left + margin.right)
.attr("height",height + margin.top + margin. bottom)
.append("g")
.attr("transform", "translate("+margin.left+"," + margin.bottom+")");
d3.tsv("tickets.tsv",function(error, data) {
dataset = data.map(function(d) { return [d["ticket"],+d["num"] ] ;})
var xScale = d3.scale.ordinal()
.domain(data.map(function(d){ return [d.ticket];}))
.rangeRoundBands([0,width],0.1);
var yScale = d3.scale.linear()
.domain([0,d3.max(data,function(d) { return Math.max([d.num]);})])
.range([height,0]);
var rect = svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x",function(d) { return xScale(d.ticket);})
.attr("y",function (d) { return yScale (d.num) ;})**//I made the change HERE**
.attr("height",function (d) { return height - yScale(d.num);})**// and HERE**
.attr("width",xScale.rangeBand())
.attr("fill","orange");
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0"+","+(height)+")")
.call(xAxis);
svg.append("g")
.attr("class", "axis")
.call(yAxis);
;});
</script>
i am new in fabric js want to set the drag limit
i have also try with https://github.com/kangax/fabric.js/wiki/Working-with-events
not able to get the solution.
please check the attached image, object can move anyware but it should be display in red area only.i want this. help me...thanks in advance !!
While Orangepill's answer is correct, it produces a "stuttering" when your object hits the object bounds. If you have a rectangular bounding box (and not a complex bounding object) an alternative is to allow the object to be dragged along the bounds and "slide" along the bounding box. You do this by capping the coordinates values and letting the other dimension move as usual. An example snippet would look like so:
var canvas = new fabric.Canvas("bounded");
var boundingBox = new fabric.Rect({
fill: "none",
width: 600,
height: 400,
hasBorders: false,
hasControls: false,
lockMovementX: true,
lockMovementY: true,
evented: false,
stroke: "red"
});
var movingBox = new fabric.Rect({
width: 100,
height: 100,
hasBorders: false,
hasControls: false
});
canvas.on("object:moving", function() {
var top = movingBox.top;
var bottom = top + movingBox.height;
var left = movingBox.left;
var right = left + movingBox.width;
var topBound = boundingBox.top;
var bottomBound = topBound + boundingBox.height;
var leftBound = boundingBox.left;
var rightBound = leftBound + boundingBox.width;
// capping logic here
movingBox.setLeft(Math.min(Math.max(left, leftBound), rightBound - movingBox.width));
movingBox.setTop(Math.min(Math.max(top, topBound), bottomBound - movingBox.height));
});
canvas.add(boundingBox);
canvas.add(movingBox);
See this example in JSFiddle here
Felix Fung's answer was a starting point, but there are many things to consider. Here is a version that accounts for some of them.
It handles the canvas having a viewport transform (ie, zoomed/panned) and objects that are center-origined instead of left/top-origined. It also constrains objects wider/taller than the viewport to the top/left instead of the bottom/right.
canvas.on("object:moving", function(e) {
var obj = e.target;
var canvas = obj.canvas;
var top = obj.top;
var left = obj.left;
var zoom = canvas.getZoom();
var pan_x = canvas.viewportTransform[4];
var pan_y = canvas.viewportTransform[5];
// width & height we are constraining to must be calculated by applying the inverse of the current viewportTransform
var c_width = canvas.width / zoom;
var c_height = canvas.height / zoom;
var w = obj.width * obj.scaleX
var left_adjust, right_adjust
if(obj.originX == "center") {
left_adjust = right_adjust = w / 2;
} else {
left_adjust = 0;
right_adjust = w;
}
var h = obj.height * obj.scaleY;
var top_adjust, bottom_adjust;
if(obj.originY == "center") {
top_adjust = bottom_adjust = h / 2;
} else {
top_adjust = 0;
bottom_adjust = h;
}
// if you need margins set them here
var top_margin = 0;
var bottom_margin = 0;
var left_margin = 0;
var right_margin = 0;
var top_bound = top_margin + top_adjust - pan_y;
var bottom_bound = c_height - bottom_adjust - bottom_margin - pan_y;
var left_bound = left_margin + left_adjust - pan_x;
var right_bound = c_width - right_adjust - right_margin - pan_x;
if( w > c_width ) {
obj.setLeft(left_bound);
} else {
obj.setLeft(Math.min(Math.max(left, left_bound), right_bound));
}
if( h > c_height ) {
obj.setTop(top_bound);
} else {
obj.setTop(Math.min(Math.max(top, top_bound), bottom_bound));
}
});
What had worked for me is to create an event listener for the object:moving event. When the move is happening you update the goodtop and goodleft variables and once you are out of bounds to reposition the object to the last good points.
var goodtop, goodleft, boundingObject;
canvas.on("object:moving", function(){
var obj = this.relatedTarget;
var bounds = boundingObject;
obj.setCoords();
if(!obj.isContainedWithinObject(bounds)){
obj.setTop(goodtop);
obj.setLeft(goodleft);
canvas.refresh();
} else {
goodtop = obj.top;
goodleft = obj.left;
}
});
I used Michael Johnston's snipped as a starting point to add bounding control for rotated elements. This snipped only covers the cases when either (obj.centerX && obj.centerY == "center") || (obj.centerX && obj.centerY != "center")
canvasRef.current.on("object:moving", function (e) {
var obj = e.target;
var canvas = obj.canvas;
var zoom = canvas.getZoom();
var pan_x = canvas.viewportTransform[4];
var pan_y = canvas.viewportTransform[5];
// get left, top, width, and height of object
var left = obj.left;
var top = obj.top;
var width = obj.width * obj.scaleX;
var height = obj.height * obj.scaleY;
// width & height we are constraining to must be calculated by applying the inverse of the current viewportTransform
var c_width = canvas.width / zoom;
var c_height = canvas.height / zoom;
// calculate values that define the origin of the object, when it is centered in the center or not
var left_adjust, right_adjust;
if (obj.originX == "center") {
left_adjust = right_adjust = width / 2;
} else {
left_adjust = 0;
right_adjust = width;
}
var top_adjust, bottom_adjust;
if (obj.originY == "center") {
top_adjust = bottom_adjust = height / 2;
} else {
top_adjust = 0;
bottom_adjust = height;
}
// support for rotated objects
if (obj.angle) {
var angle = obj.angle;
if (angle > 270) {
angle -= 270;
} else if (angle > 180) {
angle -= 180;
} else if (angle > 90) {
angle -= 90;
}
const radians = angle * (Math.PI / 180);
const w_opposite = width * Math.sin(radians);
const w_adjacent = width * Math.cos(radians);
const h_opposite = height * Math.sin(radians);
const h_adjacent = height * Math.cos(radians);
if (obj.originX != "center" && obj.originY != "center") {
if (obj.angle <= 90) {
left_adjust = h_opposite;
top_adjust = 0;
right_adjust = w_adjacent;
bottom_adjust = h_adjacent + w_opposite;
} else if (obj.angle > 90 && obj.angle <= 180) {
left_adjust = h_adjacent + w_opposite;
top_adjust = h_opposite;
right_adjust = 0;
bottom_adjust = w_adjacent;
} else if (obj.angle > 180 && obj.angle <= 270) {
left_adjust = w_adjacent;
top_adjust = w_opposite + h_adjacent;
right_adjust = h_opposite;
bottom_adjust = 0;
} else {
left_adjust = 0;
top_adjust = w_adjacent;
right_adjust = w_opposite + h_adjacent;
bottom_adjust = h_opposite;
}
}
if (obj.originX == "center" && obj.originY == "center") {
if (obj.angle <= 90 || (obj.angle > 180 && obj.angle <= 270)) {
left_adjust = (w_adjacent + h_opposite) / 2;
right_adjust = (w_adjacent + h_opposite) / 2;
top_adjust = (h_adjacent + w_opposite) / 2;
bottom_adjust = (h_adjacent + w_opposite) / 2;
} else {
left_adjust = (h_adjacent + w_opposite) / 2;
right_adjust = (h_adjacent + w_opposite) / 2;
top_adjust = (w_adjacent + h_opposite) / 2;
bottom_adjust = (w_adjacent + h_opposite) / 2;
}
}
}
// if you need margins set them here
var top_margin = 0;
var bottom_margin = 0;
var left_margin = 0;
var right_margin = 0;
var top_bound = top_margin + top_adjust - pan_y;
var bottom_bound = c_height - bottom_adjust - bottom_margin - pan_y;
var left_bound = left_margin + left_adjust - pan_x;
var right_bound = c_width - right_adjust - right_margin - pan_x;
if (width > c_width) {
obj.set("left", left_bound);
} else {
obj.set("left", Math.min(Math.max(left, left_bound), right_bound));
}
if (height > c_height) {
obj.set("top", top_bound);
} else {
obj.set("top", Math.min(Math.max(top, top_bound), bottom_bound));
}
});
SEE THE WORKING EXAMPLE
It's as simple as a water try this
just use this js
<script type="text/javascript">
//use global variable for canvas object
var canvas;
var ctx;
function onLoad() {
//get fabric canvas with id mycanvas
canvas = new fabric.Canvas('mycanvas');
canvas.on("mouse : down",function{
//get canvas 2d context
ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.rect(115,60,221,390);//specify bounded rectangle
ctx.closePath();
ctx.clip();
ctx.save();
});
//now restore the context on mouse up
canvas.on("mouse : up",function(){
ctx.restore();//restore the context
});
}
</script>
Hope this will help you.
Njoy coding :)
now u can have the same clipping region for every object supported by Fabric.js for transformation and moving.
try it :).
im new to this d3.js stuff and i'm willing to learn it, i'm doing a site and i need my path to be open at the end and i want to add some tooltips to it, i've done read a few documents over the web and none made sense in my code, can anyone shed some light into it please, thanks in advance!! :)
my code is the following, it generates two graphics:
function getDateX(day2)
{
day = new String(day2);
return new Date(day.substring(0, 4) + '-' + day.substring(4, 6) + '-' + day.substring(6, 8));
}
function plotData(id, data, type)
{
var margin = {top: 40, right: 15, bottom: 30, left: 55},
width = 455,
height = 270 - margin.top - margin.bottom;
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis().scale(x).orient("bottom").ticks(data.length < 7 ? data.length : (data.length > 14 ? data.length / 5 : data.length / 3) ).tickFormat(d3.time.format("%d/%m"));
var yAxis = d3.svg.axis().scale(y).orient("left").ticks(5);
var area = d3.svg.area()
.x(function(d) { return x(getDateX(d.day)); })
.y0(height)
.y1(function(d) { return y( type == 'score' ? parseFloat(d.score) : d.good + d.neutral + d.bad)});
var svg = d3.select("#" + id)
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.data(data);
x.domain(d3.extent(data, function(d) { return getDateX(d.day); }));
var min = d3.min(data, function(d) { return parseFloat(d.score); });
if(min > 6)
min = 6;
else if(min > 1)
min--;
if(type == 'score')
y.domain([min, 10]);
else
y.domain([0, d3.max(data, function(d) { return d.good + d.neutral + d.bad; })]);
svg.append("path").datum(data).attr("class", "area").attr("d", area);
svg.append("g").attr("class", "x axis").attr("transform", "translate(0," + height + ")").call(xAxis);
svg.append("g").attr("class", "y axis").call(yAxis);
}
var scores =
[
<%
boolean appendComma = false;
for(MovieGrade grade : info.getGrades())
{
if(appendComma)
out.print(",");
else
appendComma = true;
out.print("{\"day\":" + grade.getDay() + ", \"good\":" + grade.getGood() + ", \"neutral\":" + grade.getNeutral() + ", \"bad\":" + grade.getBad() + ", \"score\":\"" + grade.getScore() + "\"}");
}
%>
];
plotData('evolucao-score', scores, 'score');
plotData('evolucao-comments', scores, 'comments');
I have a path in svg, it's original point is on the right, the following points are on the left, then I add a textPath to svg, all text are upside down.
How can I keep the text upright and always show from left to right?
This cost me a lot of time!
I'll appreciate your answer!
<!DOCTYPE html>
<html>
<head>
<style>
</style>
<script>
function CreateSVG(points, container) {
var id = "idSVG";
var ns = "http://www.w3.org/2000/svg";
var xlinkns = "http://www.w3.org/1999/xlink";
var lineWidth = 10;
var color = "rgba(120,120,120,0.7)";
var path = document.createElementNS(ns, "path");
path.setAttribute('stroke-linecap', 'round');
path.setAttribute('stroke-linejoin', 'round');
path.setAttribute('stroke-dasharray', 'solid');
path.setAttribute('fill', 'none');
path.setAttribute('id', id);
var _canvas = document.createElementNS(ns, 'svg');
_canvas.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xlink', xlinkns);
_canvas.setAttribute('pointer-events', 'none');
//this._canvas.appendChild(this.path);
var defs = document.createElementNS(ns, "defs");
defs.appendChild(path);
var useSvg = document.createElementNS(ns, 'use');
useSvg.setAttributeNS(xlinkns, 'xlink:href', '#' + id);
useSvg.setAttribute('fill', 'none');
useSvg.setAttribute('stroke', 'red');
var textSvg = document.createElementNS(ns, 'text');
var textPathSvg = document.createElementNS(ns, 'textPath');
textPathSvg.setAttributeNS(xlinkns, 'xlink:href', '#' + id);
textPathSvg.appendChild(document.createTextNode('test text'));
textSvg.appendChild(textPathSvg);
_canvas.appendChild(defs);
_canvas.appendChild(useSvg);
_canvas.appendChild(textSvg);
var minX, maxX, minY, maxY, x, y, width, height;
for(var i = 0, len = points.length; i < len; i++) {
x = points[i].x;
y = points[i].y;
if(minX == undefined || minX > x) {
minX = x;
}
if(maxX == undefined || maxX < x) {
maxX = x;
}
if(minY == undefined || minY > y) {
minY = y;
}
if(maxY == undefined || maxY < y) {
maxY = y;
}
}
minX = minX - lineWidth;
minY = minY - lineWidth;
width = maxX - minX + lineWidth;
height = maxY - minY + lineWidth;
_canvas.setAttribute('style', 'position:absolute;');
_canvas.setAttribute('width', width);
_canvas.setAttribute('height', height);
_canvas.setAttribute('viewBox', '0 0 ' + width + ' ' + height);
_canvas.setAttribute('stroke', 'true');
path.setAttribute('stroke-width', lineWidth);
path.setAttribute('stroke', color);
var d = [];
for(var i = 0, len = points.length; i < len; i++) {
d[i] = (points[i].x - minX + lineWidth / 2) + ',' + (points[i].y - minY + lineWidth / 2);
}
path.setAttribute('d', 'M' + d.join(' '));
container.appendChild(_canvas);
}
</script>
</head>
<body onload="CreateSVG([{x:200, y:200}, {x:40, y:60}], document.getElementById('svgContainer'))">
<div id="svgContainer"></div>
</body>
</html>