Can this script be used to create multiple SVG elements? - javascript

I have the following JavaScript that creates a graph using snap SVG.
However I need to create 4 SVG's in total and I was wondering if I could use only this script for it? And if so, how would I go about adjusting it? Or is it necessary with 4 seperate scripts to create the 4 SVG's?
In short; what's the best way going about this?
Thanks in advance.
The script:
//#SVG
var price = [0,-50, -100, -130, -150, -200];
//CHART VALUES
var chartH = $('#svg').height();
var chartW = $('#svg').width();
// PARSE PRICES TO ALIGN WITH BOTTOM OF OUR SVG
var prices = [];
for (i = 0; i < price.length; i++) {
prices[i] = price[i] + $('#svg').height();
}
function draw() {
//DEFINE SNAP SVG AND DETERMINE STEP NO.
var paper = Snap('#svg');
var steps = prices.length;
// EVENLY DISTRIBUTE OUR POINTS ALONG THE X AXIS
function step(i, chartW) {
return chartW / prices.length * i;
}
var points = [];
var breakPointsX = [];
var breakPointsY = [];
var point = {};
for (i = 1; i < prices.length; i++) {
//CALCULATE CURRENT POINT
var currStep = step(i, chartW);
var y = prices[i];
point.x = Math.floor(currStep);
point.y = y;
//CALCULATE PREVIOUS POINT
var prev = i - 1;
var prevStep = step(prev, chartW);
var prevY = prices[prev];
point.prevX = Math.floor(prevStep);
point.prevY = prevY;
if (point.prevX === 0 || point.prevY === 0){
point.prevX = 15;
point.prevY = chartH - 0
}
// SAVE PATH TO ARRAY
points[i] = " M" + point.prevX + "," + point.prevY + " L" + point.x + "," + point.y;
// SAVE BREAKPOINTS POSITION
var r = 30;
breakPointsX[i] = point.x;
breakPointsY[i] = point.y;
}
// DRAW LINES
for (i = 0; i < points.length; i++) {
var myPath = paper.path(points[i]);
var len = myPath.getTotalLength();
myPath.attr({
'stroke-dasharray': len,
'stroke-dashoffset': len,
'stroke': '#3f4db3',
'stroke-linecap': 'round',
'stroke-width': 6,
'stroke-linejoin': 'round',
'id': 'myLine' + i,
'class': 'line'
});
}
// DRAW BREAKPOINTS
for (i = 0; i < points.length; i++) {
var circle = paper.circle(breakPointsX[i], breakPointsY[i], 5);
circle.attr({
'fill': '#fff',
'stroke': '#3f4db3',
'stroke-width': 3,
'id': 'myCirc' + i,
'class':'breakpoint'
});
}
// DRAW COORDINATE SYSTEM
var xAxis = paper.path('M0,'+chartH+'L'+chartW+","+chartH);
var yAxis = paper.path('M0,'+chartH+'L0,0');
var xOff = xAxis.getTotalLength();
var yOff = yAxis.getTotalLength();
var start = (prices.length*250+"ms");
yAxis.attr({
'stroke':'black',
'stroke-width':10,
'stroke-dasharray':yOff,
'stroke-dashoffset':yOff,
'id':'yAxis'
});
xAxis.attr({
'stroke':'black',
'stroke-width':5,
'stroke-dasharray':xOff,
'stroke-dashoffset':xOff,
'id':'xAxis'
});
console.log(start);
$('#yAxis').css({
'-webkit-transition-delay':start,
'-webkit-transition':'all 200ms ease-in'
});
$('#xAxis').css({
'-webkit-transition-delay':start,
'-webkit-transition':'all 200ms ease-in'
});
$('#xAxis').animate({
'stroke-dashoffset':'0'
});
$('#yAxis').animate({
'stroke-dashoffset': '0'
});
}
function animate(){
for (i=0;i<prices.length;i++){
var circ = $('#myCirc'+i);
var line = $('#myLine'+i);
circ.css({
'-webkit-transition':'all 550ms cubic-bezier(.84,0,.2,1)',
'-webkit-transition-delay':375+(i*125)+"ms"
});
line.css({
'-webkit-transition':'all 250ms cubic-bezier(.84,0,.2,1)',
'-webkit-transition-delay':i*125+"ms"
});
line.animate({
'stroke-dashoffset':0
});
circ.css({
'transform':'scale(1)'
});
}
}
var alreadyPlayed = false;
$(window).load(function(){
draw();
animate();
});

Related

Pointing arrow on precise location on donut chart

I am trying to point to certain parts on a donut chart. The chart is divided into 9 parts.
comArray is the main array which contain all parts. angleArray is the angles in degrees for each part. useArray contains the parts that should be located through the pointer.
My code is working for sometime but then it starts locating on some different point. For example, if pointer needs to point to 3 it locates properly but after 1 hour it will start locating on the 6 part for 3 part.
What I am doing wrong? You can check the working example here: https://bishttech.com/newtest/
$(document).ready(function() {
var comArray = [1, 2, 3, 4, 5, 6, 7, 8, 9]; // main array of all parts
var angleArray = [20, 60, 100, 140, 180, 220, 260, 300, 340]; // angle in degrees for all parts
var useArray = [3, 6, 8, 2]; // pointer should stop on these parts
//console.log(areacount);
var canvas = document.getElementById("chart");
var ctx = canvas.getContext("2d");
function drawMultiRadiantCircle(xc, yc, r, radientColors) {
var partLength = (2 * Math.PI) / radientColors.length;
var start = 0;
var gradient = null;
var startColor = null,
endColor = null;
var startAngle = 0;
for (var i = 0; i < radientColors.length; i++) {
startColor = radientColors[i];
// endColor = radientColors[(i + 1) % radientColors.length];
endColor = adjust(startColor, -30);
// x start / end of the next arc to draw
var xStart = xc + Math.cos(start) * r;
var xEnd = xc + Math.cos(start + partLength) * r;
// y start / end of the next arc to draw
var yStart = yc + Math.sin(start) * r;
var yEnd = yc + Math.sin(start + partLength) * r;
ctx.beginPath();
gradient = ctx.createLinearGradient(xStart, yStart, xEnd, yEnd);
gradient.addColorStop(0, endColor);
gradient.addColorStop(0.5, startColor);
gradient.addColorStop(1.0, endColor);
ctx.strokeStyle = gradient;
ctx.arc(xc, yc, r, start, start + partLength);
ctx.lineWidth = 30;
ctx.stroke();
ctx.closePath();
start += partLength;
startAngle = partLength;
}
}
var someColors = ["#fb4168", "#4efb7f", "#fbf672", "#63e0fb", "#5b5bfb", "#f079fb", "#e0dffb", "#1aa3fb", "#1aa3fb"];
drawMultiRadiantCircle(300, 300, 200, someColors);
// get location of value
function getCurLoc(value) {
for (var i = 0; i < comArray.length; i++) {
if (value == comArray[i]) {
return i;
break;
}
}
}
var startingPnt = 0;
var curangleDegrees = 0;
var prevangleDegrees = 0;
var prevLoc = 0;
var curLoc = 0;
var rotateNum = 0;
var diffLoc = 0;
//function for displaying data on chart
function displayData() {
var maxPnt = useArray.length - 1;
var currentValue = useArray[startingPnt];
var getLoc = getCurLoc(currentValue); // getting location of value in comArray
if (rotateNum == 0) {
curangleDegrees = angleArray[getLoc];
curLoc = getLoc;
} else {
curLoc = getLoc;
diffLoc = curLoc - prevLoc;
curangleDegrees = (diffLoc * 40);
}
$(".img").rotate(curangleDegrees, {
easing: 'easeInExpo'
});
console.log(getLoc);
if (startingPnt == maxPnt) {
startingPnt = 0;
} else {
startingPnt++;
}
prevLoc = curLoc;
prevangleDegrees = curangleDegrees;
rotateNum++;
}
setInterval(function() {
displayData();
}, 10000);
function adjust(color, amount) {
return '#' + color.replace(/^#/, '').replace(/../g, color => ('0' + Math.min(255, Math.max(0, parseInt(color, 16) + amount)).toString(16)).substr(-2));
}
});
.img {
margin: 48px 52px;
position: absolute;
z-index: 1;
}
#container {
width: 50%;
display: block;
margin: 0 auto;
position: relative;
}
#chart {
position: relative;
z-index: 20;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<div id="container">
<img class='img' src="images/arrowcircle.png" alt="" />
<canvas id="chart" width="800" height="800"></canvas>
</div>

how to recognize point(x y) is inside svg group geometry elements

as my code (whether a point is inside an element like rectangle, polygon, polyline, line, circle) is working perfectly but if I want to implement this one for the group element of rectangle, polygon, polyline, line, circle, then it does not work (means isPointInFill() or isPointInStroke() does not work for the group Id).
I would be grateful if someone help me solving this.
--------------------------------------------------------
const width = document.getElementById("svgID").viewBox.baseVal.width;
const height = document.getElementById("svgID").viewBox.baseVal.height;
let draw = Snap('#svg');
let c = 0; // a counter for each generated element
let count = 0; // counter for rows
let size = Math.round(0.05 * width);
let circleSize = 25;
let circleColor = ["#ff0000", "#000000", "#00ffe1", "#0051ff"];
for (let i = 0; i <= width; i = i + size) {
count++;
for (let j = 0; j <= height; j = j + size) {
// increment our element counter
c += 1;
let rect = draw.rect(i, j, size, size);
let circle4 = draw.circle(i + (size / 2), j + (size / 2), circleSize);
circle4.attr({
fill: circleColor[3],
"fill-opacity": 1,
stroke: "#000",
"stroke-width": "1px",
id: "circle4_" + c,
name: "circle4_" + c
});
rect.attr({
fill: "#d00bf3",
"fill-opacity": 0.2,
stroke: "#000",
"stroke-width": "1px",
id: "rect_" + c,
name: "rect" + c
});
const circleID = document.getElementById(`circle4_${c}`);
//for individual element id:
/*let element_ids = ["polygon870", "polygon36458", "polygon34", "polygon38"];*/
//for group id:
let element_ids = ["g5523", "g5526", "g5529", "g5532", "g5535", "g5538" ];
//let b_062 = document.getElementById("b_06.2");
let point = {x: circleID.cx.baseVal.value, y: circleID.cy.baseVal.value};
/*let point = document.getElementById("svgId").createSVGPoint();
point.x = circleID.cx.baseVal.value;
point.y = circleID.cy.baseVal.value;*/
//console.log(`Point at ${point.x}, ${point.y}:`, polygon870.isPointInStroke(point));
//console.log(`Point at ${point.x}, ${point.y}:`, polygon36458.isPointInStroke(point));
//console.log(`Point at ${point.x}, ${point.y}:`, b_062.isPointInFill(new DOMPoint(point.x, point.y)));
/*let result = g5523.isPointInFill(205, 205);
console.log(`Point at (${point.x}, ${point.y})- (circleID: ${result}`);*/
for (let id of element_ids) {
let result = document.getElementById(id).isPointInFill(new DOMPoint(point.x, point.y));
let result2 = document.getElementById(id).isPointInStroke(new DOMPoint(point.x, point.y));
if (result) {
console.log(`Point at (${point.x}, ${point.y})- (circleID: ${circleID.id}): ${result}`);
console.log(`Point at (${point.x}, ${point.y})- (circleID: ${circleID.id}): ${result2}`);
}
}
}
}

Clear and re-draw using svg.js

I want to draw some elements using svg.js then clear the canvas and draw new elements. I'm using draw.clear() but when I re-draw the elements, they aren't visible. Is this approach correct or am I missing something? I also tried remove().
var width = 500;
var height = 500;
var pos_x = 0;
var pos_y = 0;
var texto = [];
var grupo = [];
var rect = [];
var clip = [];
var random_rotation = [];
var latino = 'ABCDEFGHIJKLMNOPQRSTUVXYZ'.split('');
var cirilico = 'АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯ'.split('');
var hebreo = 'אבגדהוזיךכל םמןנסעףפפצקחט '.split('');
var griego = 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨ'.split('');
var arabe = 'ابتثجحخدذرزسشصضطظعغفقكلمنهويةءأإ'.split('');
var coreano = 'ㅏㅑㅓㅕㅗㅛㅜㅠㅡㅣㅐㅒㅔㅖㅘㅙㅚㅝㅞㅟㅢㄱㄴㄷㄹㅁㅂㅅㅇㅈㅊㅋㅌㅍㅎㄲㄸㅃㅆㅉㄳㄵㄶㄺㄻㄿㄽㄾㄿㅀㅄ'.split('');
var glagolitico = 'ⰀⰁⰂⰃⰄⰅⰆⰇⰈⰉⰊⰋⰌⰍⰎⰏⰐⰑⰒⰓⰔⰕⰖⰗⰘⰙⰚⰛⰜⰝⰞⰟⰠⰡⰢⰣⰤⰥⰦⰧⰨⰩⰪⰫⰬⰭⰮ'.split('');
var hiragana = 'あかさたなはまやらわがざだばぱいきしちにひみり(ゐ)ぎじぢびぴうくすつぬふむゆるぐずづぶぷえけせてねへめれ(ゑ)げぜでべぺおこそとのほもよろをごぞどぼぽ'.split('');
var katakana = 'アカサタナハマヤラワガザダバパヴァイキシチニヒミリ(ヰ)ギジヂビピヴィウクスツヌフムユルグズヅブプヴエケセテネヘメレ(ヱ)ゲゼデベペヴェオコソトノホモヨロヲゴゾドボポヴォ'.split('');
var sistemas = [latino, cirilico, hebreo, griego, arabe, coreano, glagolitico, hiragana, katakana];
var alfabeto = [];
var letra = [];
var draw = SVG().addTo('.container').size(width, height).attr({id: 'svg'});
function getRndInteger(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function getRndFloat(min, max) {
return (Math.random() * (max - min)) + min;
}
// Create symbols
function grafema(){
for (var i = 0; i <= 3; i++){
alfabeto[i] = getRndInteger(0, sistemas.length - 1);
letra[i] = sistemas[alfabeto[i]][getRndInteger(0, sistemas[alfabeto[i]].length - 1)];
random_rotation[i] = getRndInteger(0, 360);
texto[i] = draw.text(letra[i])
.font({
family: 'Arial Unicode MS',
size: 500,
anchor: 'middle'
})
.transform({
translateX: width/2,
translateY: (height/2) * -1,
rotate: random_rotation[i],
scale: getRndFloat(0.5, 0.6)
});
grupo[i] = draw.group()
.attr({
id: 'letra ' + i
});
grupo[i].add(texto[i]);
rect[i] = draw.rect(200, 200)
.attr({
x: pos_x,
y: pos_y,
})
.transform({
rotate : random_rotation[i],
});
pos_x = pos_x + 200;
if (pos_x > 200) {
pos_x = 0;
pos_y = pos_y + 200;
}
clip[i] = draw.clip().add(rect[i]);
grupo[i].clipWith(clip[i]);
}
}
grafema();
draw.clear();
grafema();
This is a simplified version of a program I'm trying to create. In the future I would like to clear and re-draw whenever the user makes a click.
The problem in your code is not the clear() method. You are using global variables pos_x and pos_y and you are incrementing them. In your second drawing, the images are outside the visible area. To solve your issue, initialise those variables in the beginning of your grafema function:
// ...
function grafema() {
pos_x = 0;
pos_y = 0;
for (var i = 0; i <= 3; i++) {
// ...
Example of execution with infinite drawings:
https://jsbin.com/mehuholiwo/edit?js,output

FabricJS: big grid objects is blurry

I want to draw a large grid with fabricjs, but it is too blurry. I use fabricJS canvas.toSVG() export to a svg file, and I open the svg file in browser, it's view good. so I think this is most possible likely a bug for edge.
The test code is:
var data = [];
var temp = [0,0,-0.012,-0.012,-0.012,-0.012,-0.012,-0.012,0,0,-0.012,-0.012,0,0.049,0.073,0.049,0.049,0.037,-0.012,-0.012,-0.024,-0.049,-0.024,-0.049,-0.061,-0.012,-0.061,-0.086,0.061,0.146,0.354,0.403,-0.647,-1.88,-1.672,-0.757,-0.33,-0.098,0.024,0.012,0.073,0.122,0.098,0.146,0.183,0.171,0.207,0.232];
for (var i = 0; i < 200; i++) {
data = data.concat(temp);
}
// case 1 : blurry
var canvas1 = new fabric.Canvas("ecg1");
var width = 8000;
var height = 400;
canvas1.setWidth(width);
canvas1.setHeight(height);
var bg1 = getBackgroundPath(width, height);
var ecg1 = getEcgPath(data);
canvas1.add(bg1);
canvas1.add(ecg1);
canvas1.renderAll(); // blurry
// case 2 : ok
var canvas2 = new fabric.Canvas("ecg2");
var data2 = data.slice(0, 3000);
width = 1000;
height = 400;
canvas2.setWidth(width);
canvas2.setHeight(height);
var bg2 = getBackgroundPath(width, height);
var ecg2 = getEcgPath(data2);
canvas2.add(bg2);
canvas2.add(ecg2);
canvas2.renderAll();
// case 3 : blurry
var canvas3 = new fabric.Canvas("ecg3");
canvas3.setWidth(width);
canvas3.setHeight(height);
canvas3.add(new fabric.Group([bg2, ecg2]));
canvas3.renderAll();
function getBackgroundPath(width, height) {
var grid = [];
for (var y = 0; y <= height; y += 10) {
grid.push("M");
grid.push("0");
grid.push(y);
grid.push("H");
grid.push(width);
}
for (var x = 0; x <= width; x += 10) {
grid.push("M");
grid.push(x);
grid.push("0");
grid.push("V");
grid.push(height);
}
return new fabric.Path(grid.join(" "), {
top : 0,
left : 0,
stroke: 'pink',
strokeWidth: 1
});
}
function getEcgPath(data) {
var xm = 2;
var ym = 100;
var max = Math.max.apply(Math, data);
var path = [];
for (var i = 0; i < data.length; i++) {
path.push("L");
path.push(i * xm);
path.push((max - data[i]) * ym);
}
path[0] = "M";
return new fabric.Path(path.join(" "), {
top : 0,
left : 0,
fill : '',
stroke: 'black',
strokeWidth: 1
});
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<canvas id="ecg1"></canvas>
<canvas id="ecg2"></canvas>
<canvas id="ecg3"></canvas>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.3/fabric.min.js" ></script>
</body>
</html>
the result is
Please have further reading here:
http://fabricjs.com/fabric-object-caching
This question is actually easily solvable disabling caching for the ECG line.
If your application needs to draw just a big path or an handfull of them, caching is not necessary.
How caching works?
Caching draw the object on a separate canvas and then reuse that canvas every frame when you move it.
This is way more performing than having to execute multiple fill and stroke operation each frame for many objects, especially when objects are complex.
To avoid performances pitfall the canvas used for caching is limited to 2 mega pixels by default ( you can push it up changing fabric.perfLimitSizeTotal ) and whatever is the mega pixel size, the largest size is limited to 4096 by default ( if you do not need to support ie11 you can make it bigger changing fabric.maxCacheSideLimit ).
In your case the path is smoothly handled by the cpu.
For the grid we need some more code.
var data = [];
var temp = [0,0,-0.012,-0.012,-0.012,-0.012,-0.012,-0.012,0,0,-0.012,-0.012,0,0.049,0.073,0.049,0.049,0.037,-0.012,-0.012,-0.024,-0.049,-0.024,-0.049,-0.061,-0.012,-0.061,-0.086,0.061,0.146,0.354,0.403,-0.647,-1.88,-1.672,-0.757,-0.33,-0.098,0.024,0.012,0.073,0.122,0.098,0.146,0.183,0.171,0.207,0.232];
for (var i = 0; i < 200; i++) {
data = data.concat(temp);
}
// case 1 : blurry
var canvas1 = new fabric.Canvas("ecg1");
var width = 8000;
var height = 400;
canvas1.setWidth(width);
canvas1.setHeight(height);
var bg1 = getBackgroundPath(width, height);
var ecg1 = getEcgPath(data);
canvas1.add(bg1);
canvas1.add(ecg1);
canvas1.renderAll(); // blurry
function getBackgroundPath(width, height) {
var grid = [];
for (var y = 0; y <= height; y += 10) {
grid.push("M");
grid.push("0");
grid.push(y);
grid.push("H");
grid.push(width);
}
for (var x = 0; x <= width; x += 10) {
grid.push("M");
grid.push(x);
grid.push("0");
grid.push("V");
grid.push(height);
}
return new fabric.Path(grid.join(" "), {
top : 0,
left : 0,
stroke: 'pink',
strokeWidth: 1
});
}
function getEcgPath(data) {
var xm = 2;
var ym = 100;
var max = Math.max.apply(Math, data);
var path = [];
for (var i = 0; i < data.length; i++) {
path.push("L");
path.push(i * xm);
path.push((max - data[i]) * ym);
}
path[0] = "M";
return new fabric.Path(path.join(" "), {
top : 0,
left : 0,
fill : '',
stroke: 'black',
strokeWidth: 1,
objectCaching: false,
});
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<canvas id="ecg1"></canvas>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.3/fabric.min.js" ></script>
</body>
</html>
#AndreaBogazzi
Thanks your answer. After I use fabric.Object.prototype.objectCaching = false, it's shows good.
In my application, the draw grid code is as follows.
var data = [];
var temp = [0,0,-0.012,-0.012,-0.012,-0.012,-0.012,-0.012,0,0,-0.012,-0.012,0,0.049,0.073,0.049,0.049,0.037,-0.012,-0.012,-0.024,-0.049,-0.024,-0.049,-0.061,-0.012,-0.061,-0.086,0.061,0.146,0.354,0.403,-0.647,-1.88,-1.672,-0.757,-0.33,-0.098,0.024,0.012,0.073,0.122,0.098,0.146,0.183,0.171,0.207,0.232];
for (var i = 0; i < 200; i++) {
data = data.concat(temp);
}
// case 1 : blurry
var canvas1 = new fabric.Canvas("ecg1");
var width = 8000;
var height = 400;
canvas1.setWidth(width);
canvas1.setHeight(height);
var bg11 = getBackgroundDot(width, height);
var bg12 = getBackgroundPath(width, height);
var ecg1 = getEcgPath(data);
canvas1.add(bg11);
canvas1.add(bg12);
canvas1.add(ecg1);
canvas1.renderAll(); // blurry
function getBackgroundDot() {
var dotLineWidth = 2;
var dot = [];
dot.push("M 0 0");
for (var y = 0; y <= height; y += 10) {
if (y % 50 == 0) {
continue;
}
dot.push("M");
dot.push("0");
dot.push(y);
dot.push("H");
dot.push(width);
}
var sda = [0, 10];
for (var i = 1; i < 5; i++) {
sda.push(dotLineWidth);
sda.push(8);
}
return new fabric.Path(dot.join(" "), {
top : 0,
left : 0,
stroke: 'pink',
strokeWidth: dotLineWidth,
strokeDashArray: sda
});
}
function getBackgroundPath(width, height) {
var grid = [];
for (var y = 0; y <= height; y += 50) {
grid.push("M");
grid.push("0");
grid.push(y);
grid.push("H");
grid.push(width);
}
for (var x = 0; x <= width; x += 50) {
grid.push("M");
grid.push(x);
grid.push("0");
grid.push("V");
grid.push(height);
}
return new fabric.Path(grid.join(" "), {
top : 0,
left : 0,
stroke: 'pink',
strokeWidth: 1
});
}
function getEcgPath(data) {
var xm = 2;
var ym = 100;
var max = Math.max.apply(Math, data);
var path = [];
for (var i = 0; i < data.length; i++) {
path.push("L");
path.push(i * xm);
path.push((max - data[i]) * ym);
}
path[0] = "M";
return new fabric.Path(path.join(" "), {
top : 0,
left : 0,
fill : '',
stroke: 'black',
strokeWidth: 1
});
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<canvas id="ecg1"></canvas>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.3/fabric.min.js" ></script>
</body>
</html>

Adding grid over Fabric.js canvas

I just started using Fabric.js (I have to say, I'm impressed).
I want to add a grid over the fabric objects. In the following code, I am putting my grid canvas right over on the Fabric canvas. The problem here is that, I now cannot move my fabric objects!
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<script type='text/javascript' src='http://code.jquery.com/jquery-1.7.1.js'></script>
<script type='text/javascript' src='http://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.2.0/fabric.all.min.js'></script>
</head>
<body>
<div style="height:480px;width:640px;border:1px solid #ccc;position:relative;font:16px/26px Georgia, Garamond, Serif;overflow:auto;">
<canvas id="rubber" width="800" height="800"
style="position: absolute; left: 0; top: 0; z-index: 0;"></canvas>
<canvas id="myCanvas" width="800" height="800"
style="position: absolute; left: 0; top: 0; z-index: 1;"></canvas>
</div>
<script>
//<![CDATA[
$(window).load(function(){
$(document).ready(function () {
function renderGrid(x_size,y_size, color)
{
var canvas = $("#myCanvas").get(0);
var context = canvas.getContext("2d");
context.save();
context.lineWidth = 0.5;
context.strokeStyle = color;
// horizontal grid lines
for(var i = 0; i <= canvas.height; i = i + x_size)
{
context.beginPath();
context.moveTo(0, i);
context.lineTo(canvas.width, i);
context.closePath();
context.stroke();
}
// vertical grid lines
for(var j = 0; j <= canvas.width; j = j + y_size)
{
context.beginPath();
context.moveTo(j, 0);
context.lineTo(j, canvas.height);
context.closePath();
context.stroke();
}
context.restore();
}
renderGrid(10,15, "gray");
});
});//]]>
var canvas = new fabric.Canvas('rubber');
canvas.add(new fabric.Circle({ radius: 30, fill: '#f55', top: 100, left: 100 }));
canvas.selectionColor = 'rgba(0,255,0,0.3)';
canvas.selectionBorderColor = 'red';
canvas.selectionLineWidth = 5;
</script>
</body>
</html>
I am hoping that there is a way to do this in Fabric itself.
Any help would be awesome, thanks!
This two lines of code will work:
var gridsize = 5;
for(var x=1;x<(canvas.width/gridsize);x++)
{
canvas.add(new fabric.Line([100*x, 0, 100*x, 600],{ stroke: "#000000", strokeWidth: 1, selectable:false, strokeDashArray: [5, 5]}));
canvas.add(new fabric.Line([0, 100*x, 600, 100*x],{ stroke: "#000000", strokeWidth: 1, selectable:false, strokeDashArray: [5, 5]}));
}
A shorter version and more generic for copy/paste :
var oCanvas; // must be your canvas object
var gridWidth; // <= you must define this with final grid width
var gridHeight; // <= you must define this with final grid height
// to manipulate grid after creation
var oGridGroup = new fabric.Group([], {left: 0, top: 0});
var gridSize = 20; // define grid size
// define presentation option of grid
var lineOption = {stroke: 'rgba(0,0,0,.4)', strokeWidth: 1, selectable:false, strokeDashArray: [3, 3]};
// do in two steps to limit the calculations
// first loop for vertical line
for(var i = Math.ceil(gridWidth/gridSize); i--;){
oGridGroup.add( new fabric.Line([gridSize*i, 0, gridSize*i, gridHeight], lineOption) );
}
// second loop for horizontal line
for(var i = Math.ceil(gridHeight/gridSize); i--;){
oGridGroup.add( new fabric.Line([0, gridSize*i, gridWidth, gridSize*i], lineOption) );
}
// Group add to canvas
oCanvas.add(oGridGroup);
I hope this will help you----
function draw_grid(grid_size) {
grid_size || (grid_size = 25);
currentCanvasWidth = canvas.getWidth();
currentcanvasHeight = canvas.getHeight();
// Drawing vertical lines
var x;
for (x = 0; x <= currentCanvasWidth; x += grid_size) {
this.grid_context.moveTo(x + 0.5, 0);
this.grid_context.lineTo(x + 0.5, currentCanvasHeight);
}
// Drawing horizontal lines
var y;
for (y = 0; y <= currentCanvasHeight; y += grid_size) {
this.grid_context.moveTo(0, y + 0.5);
this.grid_context.lineTo(currentCanvasWidth, y + 0.5);
}
grid_size = grid_size;
this.grid_context.strokeStyle = "black";
this.grid_context.stroke();
}
My solution is -
var width = canvas.width;
var height = canvas.height;
var j = 0;
var line = null;
var rect = [];
var size = 20;
console.log(width + ":" + height);
for (var i = 0; i < Math.ceil(width / 20); ++i) {
rect[0] = i * size;
rect[1] = 0;
rect[2] = i * size;
rect[3] = height;
line = null;
line = new fabric.Line(rect, {
stroke: '#999',
opacity: 0.5,
});
line.selectable = false;
canvas.add(line);
line.sendToBack();
}
for (i = 0; i < Math.ceil(height / 20); ++i) {
rect[0] = 0;
rect[1] = i * size;
rect[2] = width;
rect[3] = i * size;
line = null;
line = new fabric.Line(rect, {
stroke: '#999',
opacity: 0.5,
});
line.selectable = false;
canvas.add(line);
line.sendToBack();
}
canvas.renderAll();
You have to save all line objects for removing grid, or you can added all line objects to a group, and you can remove the group for removing grid, well I think this is not elegant one, but worked.
If you don't insist on generating your grid dynamically you might want to consider the native overlay image function that fabric.js provides.
var canvas = new fabric.Canvas('rubber');
canvas.setOverlayImage('grid.png', canvas.renderAll.bind(canvas));
It won't hinder interactions with the objects on the canvas at all.
I really liked #Draeli's answer, but it seemed it wasn't working with the latest fabric version. Fixed the old one and added one slight adjustment that was necessary for myself - centring the grid.
Anyhow, maybe someone else finds it useful:
const gridSize = 100;
const width = this.canvas.getWidth();
const height = this.canvas.getHeight();
const left = (width % gridSize) / 2;
const top = (height % gridSize) / 2;
const lines = [];
const lineOption = {stroke: 'rgba(0,0,0,1)', strokeWidth: 1, selectable: false};
for (let i = Math.ceil(width / gridSize); i--;) {
lines.push(new fabric.Line([gridSize * i, -top, gridSize * i, height], lineOption));
}
for (let i = Math.ceil(height / gridSize); i--;) {
lines.push(new fabric.Line([-left, gridSize * i, width, gridSize * i], lineOption));
}
const oGridGroup = new fabric.Group(lines, {left: 0, top: 0});
this.canvas.add(oGridGroup);
this.canvas - this supposedly is the fabric instance.
Here is my solution, works with Fabric Js 4.
at the end there is 2 events listeners that append the grid when object moving and remove the grid when object move end. (improvement of #draeli answer https://stackoverflow.com/a/35936606/1727357 )
(function () {
const gcd = (a, b) => {
if (!b) {
return a;
}
return gcd(b, a % b);
}
const gridWidth = canvas.getWidth();
const gridHeight = canvas.getHeight();
const oGridGroup = [];
console.log(gcd(gridWidth, gridHeight));
const gridRows = gcd(gridWidth, gridHeight);
const gridCols = gcd(gridWidth, gridHeight);
const lineOption = { stroke: 'rgba(0,0,0,.1)', strokeWidth: 1, selectable: false, evented: false };
for (let i = 0; i <= gridWidth; i += gridCols) {
oGridGroup.push(new fabric.Line([i, 0, i, gridHeight], lineOption));
}
for (let i = 0; i <= gridHeight; i += gridRows) {
oGridGroup.push(new fabric.Line([0, i, gridWidth, i], lineOption));
}
const theGorup = new fabric.Group(oGridGroup);
theGorup.set({
selectable: false,
evented: false
})
canvas.on('mouse:down', function (event) {
if (event.target) {
canvas.add(theGorup);
}
});
canvas.on('mouse:up', function (event) {
canvas.remove(theGorup);
});
}())
Updated:
Answer is based on the code posted by rafi:
I have updated the missing grid_context.
Kindly replace "<your canvas Id>" with your canvas Id in string. For e.g. "myCanvas".
Usually, any operation you do on the canvas will clear out grid on the canvas, register the after:render event on fabric.js canvas to redraw it. So you can always see it.
var canvas = new fabric.Canvas(<your canvas Id>);
canvas.on('after:render',function(ctx){
draw_grid(25);
});
function draw_grid(grid_size) {
grid_size || (grid_size = 25);
var currentCanvas = document.getElementById(<your canvas Id>);
var grid_context = currentCanvas.getContext("2d");
var currentCanvasWidth = canvas.getWidth();
var currentCanvasHeight = canvas.getHeight();
// Drawing vertical lines
var x;
for (x = 0; x <= currentCanvasWidth; x += grid_size) {
grid_context.moveTo(x + 0.5, 0);
grid_context.lineTo(x + 0.5, currentCanvasHeight);
}
// Drawing horizontal lines
var y;
for (y = 0; y <= currentCanvasHeight; y += grid_size) {
grid_context.moveTo(0, y + 0.5);
grid_context.lineTo(currentCanvasWidth, y + 0.5);
}
grid_context.strokeStyle = "#0000002b";
grid_context.stroke();
}

Categories

Resources