Add and Remove Custom attribute in fabric js - javascript

I am trying to draw grid on canvas with custom attribute. After that trying to remove that grid using those added custom attribute.
So Grid is showing on canvas, but when i am trying to remove the grid. it's not working.
Code
//Draw Grid
function draw_grid(){
var grid = 50;
canvas.getObjects().forEach(function(o) {
if(o.custom_class == 'canvas_grid') {
console.log(o.custom_class);
canvas.remove(o);
}
});
for (var i = 0; i < (600 / grid); i++) {
var grid_line1 = new fabric.Line([i * grid, 0, i * grid, 600], {
stroke: '#ccc',
selectable: false,
'custom_class' : 'canvas_grid'
});
canvas.add(grid_line1);
var grid_line2 = new fabric.Line([0, i * grid, 600, i * grid], {
stroke: '#ccc',
selectable: false,
'custom_class' : 'canvas_grid'
});
canvas.add(grid_line2);
}
}
draw_grid();
/* Hide/Show Grid */
$(document).on('change','#show_grid',function(){
if($(this).is(':checked'))
{
draw_grid();
}
else
{
canvas.getObjects().forEach(function(o) {
if(o.custom_class === 'canvas_grid') {
canvas.remove(o);
}
});
}
});
Codepen
https://codepen.io/dhavalsisodiya/pen/OQEzVE

Just change the visible property, no need to add and remove all time.
DEMO
var canvas = new fabric.Canvas('canvas1');
//Draw Grid
function draw_grid() {
var grid = 50;
var lines = [];
for (var i = 0; i <= 600; i += grid) {
lines.push(new fabric.Line([i, 0, i, 300],{stroke:'#000',strokeWidth: 2}));
}
for (var i = 0; i <= 300; i += grid) {
lines.push(new fabric.Line([0, i, 600, i],{stroke:'#000',strokeWidth: 2}));
}
gridObj = new fabric.Group(lines, {
selectable:false,
evented:false,
hoverCursor: 'auto',
left: -2,
top: -2,
visible:false
});
canvas.add(gridObj);
canvas.renderAll();
console.log(gridObj.toJSON())
}
draw_grid();
/* Hide/Show Grid */
$(document).on('change', '#show_grid', function() {
gridObj.visible = $(this).is(':checked');
gridObj.dirty = true;
canvas.renderAll();
});
<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Show Grid: <input type="checkbox" id="show_grid" />
<canvas id="canvas1" width="600" height="300" style="border:1px solid #000000;"></canvas>
Here is codepen.
Or
Get all the objects having custom_class = canvas_grid property and remove them using canvas.remove(). Here is codepen.

Related

Drag, drop and delete objects on FabricJS canvas

I am having issues getting drag, drop and delete to work on a FabricJS using JQuery.
Select an object and click delete is working with no issues.
Here's a demo of my code Fiddle
I can get drag, drop and delete to work outside of FabricJS (Just dragging unrelated elements to delete) but I'm having difficulty combing the two.
Maybe I need to incorporate the delete button inside the canvas in order for the 'drop' to work? When I try this the #delete element disappears.
JS
var canvas = new fabric.Canvas('canvas');
$("#delete").click(function() {
deleteObjects();
});
//Test objects on the canvas
//Circle
var circle = new fabric.Circle({
left: 200,
top: 150,
radius: 30,
fill: "#ff0072"
});
circle.hasRotatingPoint = true;
canvas.add(circle);
// adding triangle
var triangle = new fabric.Triangle({
left: 130,
top: 150,
strokeWidth: 1,
width: 70,
height: 60,
fill: '#00b4ff',
selectable: true,
originX: 'center'
});
triangle.hasRotatingPoint = true;
canvas.add(triangle);
//Select all objects
function deleteObjects() {
var activeObject = canvas.getActiveObject(),
activeGroup = canvas.getActiveGroup();
if (activeObject) {
canvas.remove(activeObject);
}
}
//Drag and drop delete
$(function() {
$('#delete').droppable({
drop: function(event, ui) {
deleteObjects();
}
});
});
HTML
<section class="canvas__container">
<canvas id="canvas" width="400" height="400"></canvas>
<div id="delete">♻</div>
</section>
(I didn't paste my CSS as that's not particularly relevant)
This should get you close:
var bin;
var selectedObject;
fabric.Image.fromURL('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADUAAAA1CAIAAABuhDQnAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAPPSURBVGhDzZd9U+IwEIfv+3+2Gxln9PAEcYRBURQVkVeFe9rdC2laIFsK+Pzh2Cab/ZF9Sfpr9bMpr+/r6+v5+fn+/v5vyuXl5e8U/uGx2Wwy9Pr6yjQ1KIVZ32w2e3x8xL2oiYHJmGCoS1gw6MPB7e2t+iwF5laVUfqIEcGq1WrqZw9YhKXig75bHzlUiTIfFmRZdbCVHfrIG13yALC4utnMRn2EYM9siwEX22NdrA8b1y8ODY62SCzWd4Sd88GdOs5RoO+gObeJTbkY6qOs1OLoFFZ0Rh95UHkriQfX+UTM6Ot2uzr3RCBApfxnrY+T54SbJyAgOADX+krX7NnZGTeAm5ub8/NzfbUHQS2rPlTruJFOp7NYLGQR+P7+5vHz8/Pl5eX6+lonGfG3UPWV6ykPDw/L5VJWyIPQcjHxe43qM93nBEz8nSvk4+OD6KtBNKys9qKPqtYRC3d3d/P5XFYRptMpBei/ZGXyUg0suEaT6OOarq/toHI8HrPIZDLhWn91dTUajdKVE4g+tz2dasH16kSfte1Rp29vb71e7+LiQt78SeFRtPqQ7ExmVGZG4hphoo8fra+jQRx7A5QqJeySjJoNgs5jueQW8zL6Wq0WqSb2wnA41LG0gbm6YRqKUW+9rSFJVkj0mYxJsqDFs4s0Gh1OISlJcGJdr9cRR3ytvQZJsniiT9/FQeMVSwe+86240WiQjuDKxXozEiuzPupULB00OR3Lgmg/DTA09UKxMsfX10fuE8cguA6izHGnU9PJ5IaO7SITX1N9uPjijx1CXKFXCpDR4MfEO8rUh6n+3a5QFqQUGSbvaYrtdnswGBBThqghioO6Tr0kUCUyM4ZMfzH1Z3IoSEGksFVPT0/6nELcqQz2TB4R7X5JDJn+bD3fAilsJx0xyDYftDKqxnFkzjfs9XUcHPmYiD0QTQ5ZttDtVkC/31fLaNz6iT6wHkFsuRgKoiB/+ML7+7uprYBLPlB91vspLjFxv1J6L3/JRU5kV0C0RlPaCQX3U9bVQSOcEySftBguCpSwvN8H//xUfVDuLl45yFBBKWt9qP4J35d+5cFaH1gvqpXj2p4jow/tJ9zC/OZBRh9Yb0EV4nqyT6gPyn0L74nfU3wK9MGRazmoWZ9ifeSB9YuhNDjKp52jWB9gc4RdxMUWcbBRn3DQXNyUcz479AFlVXnTYcHCas2zWx8QAjpnJSpZhKW2x9QnSp/AAbhnRmLun/0xGPQJOCBvTPdFJmNiVSaY9TmIETlEsHDP55brR/zDIy8ZYkJ8KAspr+84/Gx9q9U/+FfCZwDz5ogAAAAASUVORK5CYII=', function(img) {
img.set({
left: 174,
top: 329,
selectable: false
});
bin = img;
canvas.add(img, circle, triangle);
});
canvas.on('object:selected', function(evn) {
selectedObject = evn.target;
})
canvas.on('mouse:up', function(evn) {
var x = evn.e.offsetX;
var y = evn.e.offsetY;
if (x > bin.left && x < (bin.left + bin.width) &&
y > bin.top && y < (bin.top + bin.height)) {
canvas.remove(selectedObject);
}
});
Here's your JSFiddle updated, https://jsfiddle.net/rekrah/jgruwse0/.
Let me know if you have any further questions.

this.canvasElement.getContext is not a function in jQuery

In my document I'm referring jQuery version 3.1.0, as well as oCanvas version 2.8.1. This is how I have my scripts represented in my document:
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/ocanvas/2.8.1/ocanvas.min.js"></script>
My code creates fading circles within the canvas element as such:
jQuery(document).ready(function($) {
var windowXArray = [],
windowYArray = [];
for (var i = 0; i < $(window).innerWidth(); i++) {
windowXArray.push(i);
}
for (var i = 0; i < $(window).innerHeight(); i++) {
windowYArray.push(i);
}
function randomPlacement(array) {
var placement = array[Math.floor(Math.random() * array.length)];
return placement;
}
var canvas = oCanvas.create({
canvas: '#connect',
//background: '#131417',
fps: 60
});
setInterval(function() {
var rectangle = canvas.display.ellipse({
x: randomPlacement(windowXArray),
y: randomPlacement(windowYArray),
origin: {
x: 'center',
y: 'center'
},
radius: 0,
fill: '#fff',
opacity: 1
});
canvas.addChild(rectangle);
rectangle.animate({
radius: 10,
opacity: 0
}, {
duration: '1000',
easing: 'linear',
callback: function() {
this.remove();
}
});
}, 100);
$(window).resize(function() {
canvas.width = $(window).innerWidth();
canvas.height = $(window).innerHeight();
});
$(window).resize();
});
The full error I receive is:
TypeError: this.canvasElement.getContext is not a function. (In 'this.canvasElement.getContext("2d")', 'this.canvasElement.getContext' is undefined)
and it complains within the actual jQuery document which is very odd. I have this same exact setup on a another project and it works with no issue. A screenshot of where the error occurs is below:
What could possibly be going on here?

JavaScript - Dynamically create SVG and modify for cursor

Say I have a HTML5 canvas (In this case using fabric.js), and I want to change the cursor over the canvas to represent whatever brush size and colour has been selected. I'm thinking there should be a way to do this by changing an SVG's properties (size & colour) dynamically with JS so we don't have to use multiple images. Any ideas on if this is possible?
var canvas = new fabric.Canvas(c, {
isDrawingMode: true,
freeDrawingCursor: 'url("img/cursor.svg"), auto'
});
I think freeDrawingCursor is just looking for normal css property names. Here is an example of how to have a fabric object represent the cursor size and color:
var canvas = new fabric.Canvas('c', {
isDrawingMode: true,
freeDrawingCursor: 'none'
});
canvas.freeDrawingBrush.width = 10;
canvas.freeDrawingBrush.color = '#9f9';
var mousecursor = new fabric.Circle({
left: 0,
top: 0,
radius: canvas.freeDrawingBrush.width / 2,
fill: canvas.freeDrawingBrush.color,
originX: 'right',
originY: 'bottom',
})
canvas.add(mousecursor);
canvas.on('mouse:move', function(obj) {
mousecursor.top = obj.e.y - mousecursor.radius;
mousecursor.left = obj.e.x - mousecursor.radius;
canvas.renderAll()
})
canvas.on('mouse:out', function(obj) {
// put circle off screen
mousecursor.top = -100;
mousecursor.left = -100;
canvas.renderAll()
})
canvas {
border: 1px solid #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.3/fabric.js"></script>
<canvas id="c" width="600" height="600"></canvas>
Existing answers didn't work for me in 2020. Try the following:
this.canvas.isDrawingMode = true;
this.canvas.freeDrawingCursor = 'none';
const mousecursor = new fabric.Circle({
left: 0,
top: 0,
radius: this.canvas.freeDrawingBrush.width / 2,
fill: this.canvas.freeDrawingBrush.color,
originX: 'right',
originY: 'bottom',
});
this.canvas.add(mousecursor);
// Cursor in canvas
this.canvas.on('mouse:move', event => {
mousecursor.top = event.e.layerY + mousecursor.radius;
mousecursor.left = event.e.layerX + mousecursor.radius;
this.canvas.renderAll();
});
// Cursor out of canvas
this.canvas.on('mouse:out', event => {
mousecursor.top = -100;
mousecursor.left = -100;
this.canvas.renderAll();
});
Worked fine with Angular 7.
I was looking for the same and found my way to this question. Unfortunately the solution by STHayden is not working for me. So I've modified it a bit and came up with the code below. It uses two canvas layers, the bottom for drawing, the top for the "cursor". Works pretty well for me now, maybe someone will find it helpful :)
//drawing layer
var canvas = new fabric.Canvas("draw", {
isDrawingMode: true,
freeDrawingCursor: 'none'
});
//mouse cursor layer
var cursor = new fabric.StaticCanvas("cursor");
canvas.freeDrawingBrush.width = 20;
canvas.freeDrawingBrush.color = '#ff0000';
var cursorOpacity = .5;
//create cursor and place it off screen
var mousecursor = new fabric.Circle({
left: -100,
top: -100,
radius: canvas.freeDrawingBrush.width / 2,
fill: "rgba(255,0,0," + cursorOpacity + ")",
stroke: "black",
originX: 'center',
originY: 'center'
});
cursor.add(mousecursor);
//redraw cursor on new mouse position when moved
canvas.on('mouse:move', function (evt) {
var mouse = this.getPointer(evt.e);
mousecursor
.set({
top: mouse.y,
left: mouse.x
})
.setCoords()
.canvas.renderAll();
});
//put cursor off screen again when mouse is leaving
canvas.on('mouse:out', function () {
mousecursor
.set({
top: mousecursor.originalState.top,
left: mousecursor.originalState.left
})
.setCoords()
.canvas.renderAll();
});
//while brush size is changed show cursor in center of canvas
document.getElementById("size").oninput = function () {
var size = parseInt(this.value, 10);
mousecursor
.center()
.set({
radius: size/2
})
.setCoords()
.canvas.renderAll();
};
//after brush size has been changed move offscreen, update brush size
document.getElementById("size").onchange = function () {
var size = parseInt(this.value, 10);
canvas.freeDrawingBrush.width = size;
mousecursor
.set({
left: mousecursor.originalState.left,
top: mousecursor.originalState.top,
radius: size/2
})
.setCoords()
.canvas.renderAll();
};
//change mousecursor opacity
document.getElementById("opacity").onchange = function () {
cursorOpacity = this.value;
var fill = mousecursor.fill.split(",");
fill[fill.length-1] = cursorOpacity + ")";
mousecursor.fill = fill.join(",");
};
//change drawing color
document.getElementById("color").onchange = function () {
canvas.freeDrawingBrush.color = this.value;
var bigint = parseInt(this.value.replace("#", ""), 16);
var r = (bigint >> 16) & 255;
var g = (bigint >> 8) & 255;
var b = bigint & 255;
mousecursor.fill = "rgba(" + [r,g,b,cursorOpacity].join(",") + ")";
};
#cont {
position: relative;
width: 500px;
height: 500px;
}
canvas {
border: 1px solid;
}
#cont canvas, .canvas-container {
position: absolute!important;
left: 0!important;
top: 0!important;
width: 100%!important;
height: 100%!important;
}
#cursor {
pointer-events: none!important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.4/fabric.min.js"></script>
Color: <input id="color" type="color" value="#ff0000"><br/>
Brush size: <input id="size" type="range" min="1" max="100" step="1" value="20"><br/>
Brush opacity: <input id="opacity" type="number" min="0" max="1" step="0.1" value="0.5"><br/>
<div id="cont">
<canvas id="draw" width="500" height="500"></canvas>
<canvas id="cursor" width="500" height="500"></canvas>
</div>

Charts.js: thin donut chart background

I want to create a donut chart with a thin gray background in it.
The only way I found to create it is adding a second donut chart to create the background.
Is there any way to do it simpler?
HTML:
<div class="chart-cont">
<canvas id="pointsUsed" height="200"></canvas>
<canvas id="pointsUsedBg" height="200"></canvas>
</div>
CSS:
.chart-cont {
position: relative;
}
#pointsUsed {
position: absolute;
top: 0;
left: 0;
z-index: 2;
}
#pointsUsedBg {
position: absolute;
top: 0;
left: 0;
transform: scale(0.96,0.96);
}
JavaScript:
var pointsUsed = [
{
value: 44250,
color: "#FF5F33",
},
{
value: 100000,
color: "transparent",
},
];
var pointsUsedBg = [
{
value: 100000,
color: "#EEEEEE",
},
];
var pointsUsed_ctx = document.getElementById("pointsUsed").getContext("2d");
var pointsUsedBg_ctx = document.getElementById("pointsUsedBg").getContext("2d");
var pointsUsed = new Chart(pointsUsed_ctx).Doughnut(pointsUsed, {
segmentShowStroke: false,
segmentStrokeWidth : 0,
percentageInnerCutout: 87,
showTooltips: false,
animationEasing: 'easeInOutCubic',
responsive: true
});
var pointsUsedBg = new Chart(pointsUsedBg_ctx).Doughnut(pointsUsedBg, {
segmentShowStroke: false,
segmentStrokeWidth : 0,
percentageInnerCutout: 94,
showTooltips: false,
animation: false,
responsive: true
});
JSFiddle
Thanks!
You can extend the Doughnut chart to do this, like so
Chart.types.Doughnut.extend({
name: "DoughnutAlt",
initialize: function (data) {
// call the actual initialize
Chart.types.Doughnut.prototype.initialize.apply(this, arguments);
// save the actual clear method
var originalClear = this.clear;
// override the clear method to draw the background after each clear
this.clear = function () {
// call the original clear method first
originalClear.apply(this, arguments)
var ctx = this.chart.ctx;
// use any one of the segments to get the inner and outer radius and center x and y
var firstSegment = this.segments[0];
// adjust 0.3 to increaase / decrease the width of the background
var gap = (firstSegment.outerRadius - firstSegment.innerRadius) * (1 - 0.3) / 2;
// draw the background
ctx.save();
ctx.fillStyle = "#EEE";
ctx.beginPath();
ctx.arc(firstSegment.x, firstSegment.y, firstSegment.outerRadius - gap, 0, 2 * Math.PI);
ctx.arc(firstSegment.x, firstSegment.y, firstSegment.innerRadius + gap, 0, 2 * Math.PI, true);
ctx.closePath();
ctx.fill();
ctx.restore();
}
}
});
You would call it like this
var pointsUsed = new Chart(pointsUsed_ctx).DoughnutAlt(pointsUsed, {
...
Fiddle - http://jsfiddle.net/7nfL1m7d/

Kinetic JS canvas game (Layering issue)(Javascript)

I am trying to recreate the game http://www.sinuousgame.com/ and studying html5 canvas and kineticJS.
This is my fiddle:
http://jsfiddle.net/2WRwY/7/
My problem:
The tail part of the player in the fiddle doesn't seem to retract back.
The red ball objects should appear over the player objects.
(Try running the fiddle with layer.removeChildren(); and without it.)
Right now,I have commented "layer.removeChildren();" on the fiddle.. (basically which causes the problem for me)
Here's my html:
<!DOCTYPE html>
<html>
<head>
<title>Collision Detection-player</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="../css/style.css"/>
</head>
<body>
<div id="container" style="width: auto; height: auto; background:#000; margin:auto; float:left;"></div>
<script src="../js/jquery.min.js"></script>
<script src="../js/kinetic-v5.0.0.min.js"></script>
<script src="../js/main_kinetic_combined.js"></script>
</body>
</html>
Here's my javascript:
//The working player code
var LimitedArray = function(upperLimit) {
var storage = [];
// default limit on length if none/invalid supplied;
upperLimit = +upperLimit > 0 ? upperLimit : 100;
this.push = function(item) {
storage.push(item);
if (storage.length > upperLimit) {
storage.shift();
}
return storage.length;
};
this.get = function(flag) {
return storage[flag];
};
this.iterateItems = function(iterator) {
var flag, l = storage.length;
if (typeof iterator !== 'function') {
return;
}
for (flag = 0; flag < l; flag++) {
iterator(storage[flag]);
}
};
};
var tail = new LimitedArray(50);
var flag = 0, jincr = 0;
var stage = new Kinetic.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
listening: true
});
var layer = new Kinetic.Layer({
listening: true
});
stage.add(layer);
var player = new Kinetic.Circle({
x: 20,
y: 20,
radius: 6,
fill: 'cyan',
stroke: 'black',
draggable: true
});
layer.add(player);
// move the circle with the mouse
stage.getContent().addEventListener('mousemove', function() {
<!--layer.removeChildren(); -->
layer.add(player);
player.setPosition(stage.getPointerPosition());
var obj = {
x: stage.getPointerPosition().x,
y: stage.getPointerPosition().y
};
tail.push(obj);
var arr = [];
tail.iterateItems(function(p) {
arr.push(p.x, p.y);
});
var line = new Kinetic.Line({
points: arr,
stroke: 'white',
strokeWidth: 2,
lineCap: 'round',
lineJoin: 'round'
});
layer.add(line);
// layer.draw();
});
var x = 0;
var y = 0;
var noOfEnemies = 150;
var enemyArmada = new Array();
createEnemy();
function createEnemy() {
for (var i = 0; i < noOfEnemies; i++) {
var enemy = new Kinetic.Circle({
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight,
radius: 4.5 + 1.5 * Math.random(),
fill: 'red',
stroke: 'black',
});
enemy.speedX = enemy.speedY = (0.3 + Math.random() * 50);
enemyArmada.push(enemy);
layer.add(enemy);
}
}
var anim = new Kinetic.Animation(function(frame) {
for (var i = 0; i < noOfEnemies; i++) {
var e = enemyArmada[i];
e.position({
x: e.position().x - e.speedX * frame.timeDiff / 500,
y: e.position().y + e.speedY * frame.timeDiff / 500
});
if (e.position().y < 0 || e.position().x < 0) {
e.position({
x: (Math.random() * (window.innerWidth + 600)),
y: -(Math.random() * window.innerHeight)
});
}
}
}, layer);
anim.start();
Any suggestions?
The problem is that after you've added the enemies, you're adding the player and the line to the layer again, so they'll be on top. Also, on each mouse move, you're creating the line over and over again.
So, instead, you should just update the line points (and you don't need the layer.removeChildren(); line at all), like this:
var line = new Kinetic.Line({
points: [],
stroke: 'white',
strokeWidth: 2,
lineCap: 'round',
lineJoin: 'round'
});
layer.add(line);
layer.add(player);
// move the circle with the mouse
stage.getContent().addEventListener('mousemove', function() {
player.position(stage.getPointerPosition());
var obj = {
x: stage.getPointerPosition().x,
y: stage.getPointerPosition().y
};
tail.push(obj);
var arr = [];
tail.iterateItems(function(p) {
arr.push(p.x, p.y);
});
line.points(arr);
});
See fiddle: http://jsfiddle.net/Kunstmord/p9fnq/2/
This way, you're only creating the line once. This also seems to fix the non-disappearing trail.
Also, please note:
1) use position instead of setPosition and getPosition (see KineticJS 5.0 docs)
2) adding <!-- --> does not comment out a line in Javascript (layer.removeChildren(); line).

Categories

Resources