KineticJS: How to Cache a Group of Shapes? - javascript

I want to increase mobile drag and drop performance of canvas objects. I have a Group which contains different Shapes (Images, Stars, Recs,...). I want to use caching to increase the performance when I drag and drop or rotate the entire Group.
Here is a JSFiddle I created: http://jsfiddle.net/confile/k4Lsv73d/
function setFPSMeter() {
var RAF = (function () {
return window["requestAnimationFrame"] || window["webkitRequestAnimationFrame"] || window["mozRequestAnimationFrame"] || window["oRequestAnimationFrame"] || window["msRequestAnimationFrame"] || FRAF;
})();
function FRAF(callback) {
window.setTimeout(callback, 1000 / 60);
}
var fpsDiv = document.createElement("div");
fpsDiv.style.position = "absolute";
fpsDiv.style.zIndex="40000";
document.body.appendChild(fpsDiv);
var meter = new window.FPSMeter(fpsDiv, {
position: 'fixed',
zIndex: 10,
right: '5px',
top: '5px',
left: 'auto',
bottom: 'auto',
margin: '0 0 0 0',
interval: 1000,
graph: true,
history: 20,
theme: 'colorful',
heat: true
});
var tick = function () {
meter.tick();
RAF(tick);
};
tick();
}
setFPSMeter();
console.log(!!window.FPSMeter);
Kinetic.pixelRatio = 1;
//console.log(window.requestAnimationFrame);
// http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v5.1.0.min.js
var width = $("#container").width();
var height = $("#container").height();
console.log("width: "+width+" height: "+height);
var stage = new Kinetic.Stage({
container: 'container',
width: width,
height: 400
});
var layer = new Kinetic.Layer();
// add the layer to the stage
stage.add(layer);
var blob;
var imageObj = new Image();
imageObj.onload = function () {
var group = new Kinetic.Group({
draggable: true
});
var star = new Kinetic.Star({
innerRadius: 50,
outerRadius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 5,
numPoints: 20,
x: 200,
y: 100,
shadowOffset: 5,
shadowColor: 'black',
shadowBlur: 5,
shadowOpacity: 0.5
});
blob = new Kinetic.Image({
image: imageObj,
x: 50,
y: 40,
// draggable: true,
width: 300,
height: 300
});
// performance
// blob.transformsEnabled('position');
group.add(blob);
group.add(star);
// setTimeout(function () {
// add the shape to the layer
//layer.add(blob);
layer.add(group);
// blob.cache();
layer.batchDraw();
// }, 50);
loadEvents();
/*
blob.cache({
width: 500,
height: 500,
drawBorder: true
}).offset({
x: 10,
y: 10
});
*/
/*
blob.cache({
drawBorder: true
});
*/
};
imageObj.src = "https://dl.dropboxusercontent.com/u/47067729/sandwich2.svg";
function getDistance(p1, p2) {
return Math.sqrt(Math.pow((p2.x - p1.x), 2) + Math.pow((p2.y - p1.y), 2));
}
function loadEvents() {
var lastDist = 0;
var startScale = 1;
stage.getContent().addEventListener('touchstart', function (evt) {
console.log("touchstart");
blob.clearCache();
blob.cache();
}, false);
stage.getContent().addEventListener('touchmove', function (evt) {
clearCache
var touch1 = evt.touches[0];
var touch2 = evt.touches[1];
if (touch1 && touch2) {
// blob.draggable(false);
var dist = getDistance({
x: touch1.clientX,
y: touch1.clientY
}, {
x: touch2.clientX,
y: touch2.clientY
});
if (lastDist == 0) {
lastDist = dist;
}
console.log("touchmove dist: "+dist);
console.log("touchmove lastDist: "+lastDist);
console.log("blob.getScale().x: "+blob.getScale().x);
// scale
var scale = blob.getScale().x * dist / lastDist;
console.log("scale: "+scale);
blob.setScale(scale);
lastDist = dist;
//// rotate
///
layer.batchDraw();
}
}, false);
stage.getContent().addEventListener('touchend', function (evt) {
console.log("touchend");
// blob.draggable(true);
lastDist = 0;
}, false);
}
How can I use caching on a Group of Shapes in KineticJS?

group.cache({
width: blob.width(),
height: blob.height(),
x : blob.x(),
y : blob.y(),
drawBorder: true
}).offset({
x: 10,
y: 10
});
http://jsfiddle.net/k4Lsv73d/2/
You should better config x, y, width and height attrs for caching.

Related

Draw only inside drawn polygon

I would like to freedraw but only inside object i drawn before. Eg. i need to draw triangle using my code below, and then free draw lines but only inside this triangle, how to clip it so color wouldn't be outside of it?
var min = 99;
var max = 999999;
var polygonMode = true;
var pointArray = new Array();
var lineArray = new Array();
var activeLine;
var activeShape = false;
var canvas;
$(window).load(function() {
prototypefabric.initCanvas();
$('#create-polygon').click(function() {
prototypefabric.polygon.drawPolygon();
});
});
var prototypefabric = new function() {
this.initCanvas = function() {
canvas = window._canvas = new fabric.Canvas('c');
canvas.setWidth(800);
canvas.setHeight(800);
canvas.setBackgroundImage('https://i.imgur.com/tsnGll2.jpg', canvas.renderAll.bind(canvas));
//canvas.selection = false;
canvas.on('mouse:down', function(options) {
if (options.target && options.target.id == pointArray[0].id) {
prototypefabric.polygon.generatePolygon(pointArray);
}
if (polygonMode) {
prototypefabric.polygon.addPoint(options);
}
});
canvas.on('mouse:up', function(options) {
});
canvas.on('mouse:move', function(options) {
if (activeLine && activeLine.class == "line") {
var pointer = canvas.getPointer(options.e);
activeLine.set({
x2: pointer.x,
y2: pointer.y
});
var points = activeShape.get("points");
points[pointArray.length] = {
x: pointer.x,
y: pointer.y
};
activeShape.set({
points: points
});
canvas.renderAll();
}
canvas.renderAll();
});
};
};
prototypefabric.polygon = {
drawPolygon: function() {
polygonMode = true;
pointArray = new Array();
lineArray = new Array();
activeLine;
},
addPoint: function(options) {
var random = Math.floor(Math.random() * (max - min + 1)) + min;
var id = new Date().getTime() + random;
var circle = new fabric.Circle({
radius: 5,
fill: '#ffffff',
stroke: '#333333',
strokeWidth: 0.5,
left: (options.e.layerX / canvas.getZoom()),
top: (options.e.layerY / canvas.getZoom()),
selectable: false,
hasBorders: false,
hasControls: false,
originX: 'center',
originY: 'center',
id: id
});
if (pointArray.length == 0) {
circle.set({
fill: 'red'
})
}
var points = [(options.e.layerX / canvas.getZoom()), (options.e.layerY / canvas.getZoom()), (options.e.layerX / canvas.getZoom()), (options.e.layerY / canvas.getZoom())];
line = new fabric.Line(points, {
strokeWidth: 2,
fill: '#999999',
stroke: 'green',
class: 'line',
originX: 'center',
originY: 'center',
selectable: false,
hasBorders: false,
hasControls: false,
evented: false
});
if (activeShape) {
var pos = canvas.getPointer(options.e);
var points = activeShape.get("points");
points.push({
x: pos.x,
y: pos.y
});
var polygon = new fabric.Polygon(points, {
stroke: '#333333',
strokeWidth: 1,
fill: '#cccccc',
opacity: 0.3,
selectable: false,
hasBorders: false,
hasControls: false,
evented: false
});
canvas.remove(activeShape);
canvas.add(polygon);
activeShape = polygon;
canvas.renderAll();
console.log(points);
} else {
var polyPoint = [{
x: (options.e.layerX / canvas.getZoom()),
y: (options.e.layerY / canvas.getZoom())
}];
var polygon = new fabric.Polygon(polyPoint, {
stroke: '#333333',
strokeWidth: 1,
fill: '#cccccc',
opacity: 0.3,
selectable: false,
hasBorders: false,
hasControls: false,
evented: false
});
activeShape = polygon;
canvas.add(polygon);
}
activeLine = line;
pointArray.push(circle);
lineArray.push(line);
canvas.add(line);
canvas.add(circle);
canvas.selection = false;
},
generatePolygon: function(pointArray) {
var points = new Array();
$.each(pointArray, function(index, point) {
points.push({
x: point.left,
y: point.top,
});
canvas.remove(point);
});
$.each(lineArray, function(index, line) {
canvas.remove(line);
});
canvas.remove(activeShape).remove(activeLine);
var polygon = new fabric.Polygon(points, {
stroke: '#333333',
strokeWidth: 0.5,
fill: 'rgba(255, 255, 255, 0.5)',
opacity: 1,
hasBorders: false,
hasControls: false
});
canvas.add(polygon);
activeLine = null;
activeShape = null;
polygonMode = false;
canvas.selection = true;
}
};
// Removing active object on backspace
$(document).keydown(function(event) {
if (event.which == 8) {
if (canvas.getActiveObject()) {
canvas.getActiveObject().remove();
}
}
});
// end of Removing active object on backspace
// Freedrawing
function enableFreeDrawing() {
removeEvents();
canvas.isDrawingMode = true;
canvas.freeDrawingBrush.color = e.target.value;
}
document.getElementById('colorpicker').addEventListener('click', function(e) {
canvas.freeDrawingBrush.color = e.target.value;
canvas.freeDrawingBrush.width = 2;
});
function removeEvents() {
canvas.isDrawingMode = false;
canvas.selection = false;
canvas.off('mouse:down');
canvas.off('mouse:up');
canvas.off('mouse:move');
}
// Manipulate canvas
$(function() {
$('#zoomIn').click(function() {
canvas.setZoom(canvas.getZoom() * 1.1);
});
$('#zoomOut').click(function() {
canvas.setZoom(canvas.getZoom() / 1.1);
});
$('#goRight').click(function() {
var units = 10;
var delta = new fabric.Point(units, 0);
canvas.relativePan(delta);
});
$('#goLeft').click(function() {
var units = 10;
var delta = new fabric.Point(-units, 0);
canvas.relativePan(delta);
});
$('#goUp').click(function() {
var units = 10;
var delta = new fabric.Point(0, -units);
canvas.relativePan(delta);
});
$('#goDown').click(function() {
var units = 10;
var delta = new fabric.Point(0, units);
canvas.relativePan(delta);
});
$('#zoomIn').click(function() {
canvas.setZoom(canvas.getZoom() * 1.1);
});
$('#zoomOut').click(function() {
canvas.setZoom(canvas.getZoom() / 1.1);
});
});
// End of manipulate canvas
canvas {
border: 1px solid black;
border-radius: 27px;
}
* {
font-family: "Roboto", sans-serif;
font-weight: 100;
}
body {
overflow: hidden;
}
/*#c {*/
/* border: 1px solid black;*/
/* background: url(assets/geoportal.JPG);*/
/* background-size: cover;*/
/* border-radius: 27px;*/
/*}*/
.canvas-container {
margin: 0 auto;
margin-top: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<canvas id="c" width="800" height="800"></canvas>
<ul class="action-list">
<li>
<button class="thmb" href="#" id="create-polygon">Narysuj polygon</button>
</li>
<li>
<div id="colorpicker">
<button class="thmb" href="#" onclick="enableFreeDrawing()" value="#FFFF00">Yellow</button>
<button class="thmb" href="#" onclick="enableFreeDrawing()" value="#00FF00">Green</button>
<button class="thmb" href="#" onclick="enableFreeDrawing()" value="#F00000">Red</button>
<button class="thmb" href="#" onclick="removeEvents()">Stop Drawing</button>
</div>
</li>
<li>
<button id="goLeft" class="thmb"><</button>
<button id="goRight" class="thmb">></button>
<button id="goUp" class="thmb">Up</button>
<button id="goDown" class="thmb">Down</button>
<button id="zoomIn" class="thmb">+</button>
<button id="zoomOut" class="thmb">-</button>
</li>
</ul>
</div>
Link: https://codepen.io/Basstalion/pen/gOpKOjP

Place image to alpha channels in canvas

I need to find top, left, max width and max height values of all transparent places in canvas.
http://jsfiddle.net/alperxx/t521jra4/ (fiddle)
I have 4 transparent place in canvas. but i can't detect true places. First alpha channel corners colour must be red. Next is: black, yellow and green. But my groups is not correct.
If I complete the groups truely, every uploading photos will place to alpha places.
My Processes are in comment.
function alphaRatio(ctx) {
var pixelData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
const data = pixelData.data;
const pixel_groups = [];
var total = 0;
for (let i = 0, dx = 0; dx < data.length; i++, dx = i << 2) {
if (data[dx + 3] == 0) {
//First, I'm catching all transparent pixels.
total++;
var x = (dx / 4) % ctx.canvas.width;
var y = ~~((dx / 4) / ctx.canvas.width);
// if pixel after a last grouped item add to in.
var found = false;
if (pixel_groups.length) {
for (im = 0; im < pixel_groups.length; im++) {
last_pixels = pixel_groups[im][pixel_groups[im].length - 1];
if (
last_pixels.x + 1 == x || last_pixels.x - 1 == x ||
last_pixels.y + 1 == y || last_pixels.y - 1 == y
) {
found = true;
pixel_groups[im].push({
x: x,
y: y
});
break;
}
}
}
if (!found) {
pixel_groups.push([{
x: x,
y: y
}]);
}
}
}
// i grouped all them
if (pixel_groups.length) {
console.debug(pixel_groups);
for (i = 0; i < pixel_groups.length; i++) {
var alphawidth = {};
var alphaheight = {};
for (im = 0; im < pixel_groups[i].length; im++) {
//now lets calculate first pixel left and top for width and height
if (typeof(alphawidth['min']) === 'undefined') {
alphawidth['min'] = pixel_groups[i][im].x;
alphawidth['max'] = pixel_groups[i][im].x;
} else {
if (pixel_groups[i][im].x < alphawidth['min']) {
alphawidth['min'] = pixel_groups[i][im].x;
}
if (pixel_groups[i][im].x > alphawidth['max']) {
alphawidth['max'] = pixel_groups[i][im].x;
}
}
if (typeof(alphaheight['min']) === 'undefined') {
alphaheight['min'] = pixel_groups[i][im].y;
alphaheight['max'] = pixel_groups[i][im].y;
} else {
if (pixel_groups[i][im].y < alphaheight['min']) {
alphaheight['min'] = pixel_groups[i][im].y;
}
if (pixel_groups[i][im].y > alphaheight['max']) {
alphaheight['max'] = pixel_groups[i][im].y;
}
}
}
// update group key for only x y w h
pixel_groups[i] = {
x: pixel_groups[i][0].x,
y: pixel_groups[i][0].y,
w: alphawidth['max'] - alphawidth['min'],
h: alphaheight['max'] - alphaheight['min']
};
}
// for test alpha places put a colour all corners
for (i = 0; i < pixel_groups.length; i++) {
var colour = ['red', 'black', 'yellow', 'green'];
canvas.add(new fabric.Circle({
left: pixel_groups[i].x,
top: pixel_groups[i].y,
radius: 4,
fill: colour[i],
originX: 'center',
originY: 'center',
hasControls: false
}));
canvas.add(new fabric.Circle({
left: pixel_groups[i].x + pixel_groups[i].w,
top: pixel_groups[i].y + pixel_groups[i].h,
radius: 4,
fill: colour[i],
originX: 'center',
originY: 'center',
hasControls: false
}));
canvas.add(new fabric.Circle({
left: pixel_groups[i].x + pixel_groups[i].w,
top: pixel_groups[i].y,
radius: 4,
fill: colour[i],
originX: 'center',
originY: 'center',
hasControls: false
}));
canvas.add(new fabric.Circle({
left: pixel_groups[i].x,
top: pixel_groups[i].y + pixel_groups[i].h,
radius: 4,
fill: colour[i],
originX: 'center',
originY: 'center',
hasControls: false
}));
}
return pixel_groups;
}
return false;
}

Fabric.js - rotate one object the other rotate too

I have an application using HTML5 canvas via Fabric.js. And I would like to do arrow with text. Then moving arrow the text is in correct position, but is problem with rotation:
incorrect text position:
text position have to be:
My code is:
var canvas = new fabric.Canvas('c');
var line = new fabric.Line([10, 50, 200, 50], {
stroke: 'black',
strokeWidth: 3,
strokeDashArray: [10, 5]
});
var line2 = new fabric.Line([199, 50, 180, 60], {
stroke: 'black',
strokeWidth: 3
});
var line3 = new fabric.Line([199, 50, 180, 40], {
stroke: 'black',
strokeWidth: 3
});
var strele = new fabric.Group([line, line2, line3], {});
tekst = new fabric.IText('<<extend>>', {
left: 50,
top: 20,
fontFamily: 'Arial',
fill: '#333',
fontSize: 20
});
canvas.add(strele, tekst);
function onMoving(options) {
if (options.target === strele) {
tekst.left = strele.left + ((strele.width * strele.scaleX) / 2) - (tekst.width * tekst.scaleX) / 2;
tekst.top = strele.top - strele.height * strele.scaleY;
} else if (options.target === tekst) {
strele.left = tekst.left - ((strele.width * strele.scaleX) / 2 - (tekst.width * tekst.scaleX) / 2);
strele.top = tekst.top + strele.height * strele.scaleY;
}
canvas.forEachObject(function(o) {
o.setCoords()
});
}
function onRotating(options) {
if (options.target === strele) {
tekst.angle = strele.angle;
tekst.left = strele.left((strele.width * strele.scaleX) / 2) - (tekst.width * tekst.scaleX) / 2;
tekst.top = strele.top - strele.height * strele.scaleY;
}
}
canvas.on('object:moving', onMoving);
canvas.on('object:rotating', onRotating);
My code in fiddle
How can I fix this?
Here is an approach that closely resembles your image.
To simplify the calculations I changed the objects origin(X & Y) to center, the rest is just some math and conditions for the angles. My approach was to keep the text readable and not overlap the line.
var canvas = new fabric.Canvas('c');
var line = new fabric.Line([160, 100, 350, 100], {stroke: 'black', strokeWidth: 3, strokeDashArray: [10, 5]});
var line2 = new fabric.Line([350, 100, 330, 110], {stroke: 'black', strokeWidth: 3});
var line3 = new fabric.Line([350, 100, 330, 90], {stroke: 'black', strokeWidth: 3});
var strele = new fabric.Group([line, line2, line3], {originX: "center", originY: "center"});
var tekst = new fabric.IText('<<extend>>', {left: 160, top: 70, fontFamily: 'Arial',fill: 'red', fontSize: 20, selectable: false,
originX: "center", originY: "center"
});
canvas.add(strele, tekst);
canvas.setActiveObject(canvas.item(0));
canvas.on('object:moving', onMoving);
canvas.on('object:rotating', onRotating);
function onMoving() {
tekst.left = strele.left + 20 * Math.sin(strele.angle * Math.PI /180);
tekst.top = strele.top - 20 * Math.cos(strele.angle * Math.PI /180);
}
function onRotating() {
var ang = strele.angle % 360;
if (ang < 270) {
if (ang > 180) {
ang = strele.angle % 180;
} else if (ang > 90) {
ang = 180+strele.angle;
}
}
tekst.angle = ang
onMoving()
}
function rotate() {
strele.angle += 22.2;
strele.setCoords()
onRotating()
canvas.renderAll();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.4/fabric.min.js"></script>
<button id="rotate" onclick="rotate()">rotate</button>
<canvas id="c" width="600" height="600"></canvas>

How to draw sector (not arc) in fabric js?

I am trying to draw semecircle (sector) with fabricjs:
$('#button').click(function(){
fov+=10;
drawSector(fov);
});
$('#button2').click(function(){
fov-=10;
drawSector(fov);
});
var w=500,h=500;
var R = 100;
var fov = 75.0;
var lastFOV;
var ele = {
center: {x: 0.5, y:0.5},
focal: {x: 0.5, y: 0.5},
radius: 0.6,
transform: {
rotate: 0,
translate: {
x: 0,
y: 0,
},
scale: {
x: 1,
y: 1,
}
},
stops: [
{offset: '0', color: "purple",alpha:'0.75'},
{offset: '0.9', color: "transparent",opacity:'0'}
]
};
var tr_str = "rotate("+ele.transform.rotate+",0.5,0.5) translate("+ele.transform.translate.x*w+","+ele.transform.translate.y*h+") scale("+ele.transform.scale.x+","+ele.transform.scale.y+")";
var tr_matrix = fabric.parseTransformAttribute(tr_str);
var rg = {
type: 'radial',
x1: ele.center.x,
y1: ele.center.y,
r1: 0,
x2: ele.focal.x,
y2: ele.focal.y,
r2: R,
//transformMatrix: [1,0,0,2,0,0],
//gradientTransform: [1,0,0,2,0,0],
gradientTransform: tr_matrix,
colorStops: (function(){
var color_stops = {};
for(var i=0;i<ele.stops.length;i++){
color_stops[ele.stops[i].offset] = ele.stops[i].color;
}
return color_stops;
})()
};
var HideControls = {
'tl':false,
'tr':false,
'bl':false,
'br':false,
'ml':false,
'mt':false,
'mr':false,
'mb':false,
'mtr':true
};
var canvas = new fabric.Canvas('canvas1');
canvas.setWidth(w);
canvas.setHeight(h);
var x,y,my,startPoints;
var sector;
var rotationAngle = 0;
drawSector(fov);
function drawSector(fov){
$('#fov').html("FOV = "+fov);
x = Math.cos(fov*Math.PI/180.0)*R;
y = Math.sin(fov*Math.PI/180.0)*R;
my = -Math.sin(fov/2.*Math.PI/180.0)*R/2.;
startPoints = [
{x: 0, y: 0},
{x: R, y: 0},
{x: x, y: y}
];
if(sector) {
rotationAngle = sector.angle;
canvas.remove(sector);
}
sector = new fabric.Polygon(startPoints,{
left: w/2,
top: h/2,
originX:'left',
originY:'top',
centeredRotation:false,
hasBorders:false,
lockMovementX:true,
lockMovementY:true,
rotatingPointOffset:my,
width: R,
height: R
});
sector.setControlsVisibility(HideControls);
sector.setGradient('fill', rg);
rotationAngle = rotationAngle==0?-fov/2:rotationAngle-(fov-lastFOV)/2;
sector.setAngle(rotationAngle);
canvas.add(sector);
lastFOV = fov;
}
https://jsfiddle.net/2v0es4xh/35/
But when FOV is bigger than 90, rotationX/Y is changing.
There is something in Leaflet http://jieter.github.io/Leaflet-semicircle/examples/semicircle.html
It would be great if I can draw semicircle like this using fabric.
is there anyone who tried to do this?
Thank you in advance.
Try something like this: http://jsfiddle.net/qoadhrop/
Set two semi-circles on top of a main circle to give you this kind of "radius" effect
HTML:
<canvas id="c" width="300" height="300"></canvas>
JS:
var canvas = new fabric.Canvas('c');
var c1 = new fabric.Circle({
radius: 50,
left: 100,
top: 100,
stroke: '#000',
strokeWidth: 1,
fill: 'lightblue',
opacity: 0.8
});
var c2 = new fabric.Circle({
radius: 50,
left: 100,
top: 100,
startAngle: 0,
endAngle: Math.PI,
stroke: '#000',
strokeWidth: 1,
fill: 'red'
});
c2.setAngle(45);
var c3 = new fabric.Circle({
radius: 50,
left: 100,
top: 100,
flipX: true,
startAngle: 0,
endAngle: Math.PI,
stroke: '#000',
strokeWidth: 1,
fill: 'red'
});
c3.setAngle(-45);
canvas.add(c1);
canvas.add(c2);
canvas.add(c3);
The previous code by A. Lau doesn't work. So, this is my simple implementation. You can draw any sectors and rotate them. Try live example at https://jsfiddle.net/irwinwelsh/rn9emuqd/
var canvas = new fabric.Canvas('c');
function arc(left,top,radius,angle, rotate) {
var coeff = Math.PI/180;
var xshift = Math.sqrt(2) * radius * ( Math.cos(45 * coeff) - Math.cos((45 + rotate) * coeff));
var yshift = Math.sqrt(2) * radius * ( Math.sin(45 * coeff) - Math.sin((45 + rotate) * coeff));
left = left + xshift;
top = top + yshift;
var width = 2 * radius * Math.sin((angle/2) * coeff);
var height = radius * Math.cos((angle/2) * coeff);
var triangle = new fabric.Triangle({
width: width,
height: height,
left: left + width/2,
top: top,
fill: 'rgba(0,200,0,0.3)',
angle: 180,
});
var circle = new fabric.Circle({
radius: radius,
fill: 'rgba(0,200,0,0.3)',
left: left - radius,
top: top - radius,
startAngle: (270 - angle/2) * coeff,
endAngle: (270 + angle/2) * coeff,
angle: 0,
});
var group = new fabric.Group([circle,triangle],{
angle : rotate,
});
return group;
}
var sector = arc(100,200,200,30, 30);
canvas.add(sector);

Fabric.js bounding box with blurred shadows

I'm trying to draw a bounding box around objects that have a shadow. object.getBoundingRect() does not take into account shadows. Regular shadows are straightforward, but when blur is added it is more difficult.
Is there a way to get the bounds?
https://jsfiddle.net/e77c0owf/
function getObjBounds(obj) {
var bounds = obj.getBoundingRect();
var shadow = obj.getShadow();
if (shadow !== null) {
var blur = shadow.blur;
var signX = shadow.offsetX >= 0.0 ? 1.0 : -1.0;
var signY = shadow.offsetY >= 0.0 ? 1.0 : -1.0;
var offsetX = (shadow.offsetX + (signX * blur)) * Math.abs(obj.scaleX);
var offsetY = (shadow.offsetY + (signY * blur)) * Math.abs(obj.scaleY);
if (offsetX > 0) {
bounds.width += offsetX;
} else if (offsetX < 0) {
bounds.width += Math.abs(offsetX);
bounds.left -= Math.abs(offsetX);
}
if (offsetY > 0) {
bounds.height += offsetY;
} else if (offsetY < 0) {
bounds.height += Math.abs(offsetY);
bounds.top -= Math.abs(offsetY);
}
}
return bounds;
}
You have to check fabricjs calculations for blurring and scaling, plus you have to take in account situation where offset of shadow is smaller than blur value.
function getObjBounds(obj) {
var bounds = obj.getBoundingRect();
var shadow = obj.getShadow();
if (shadow !== null) {
var blur = shadow.blur;
var mBlur = blur * Math.abs(obj.scaleX + obj.scaleY) / 4
var signX = shadow.offsetX >= 0.0 ? 1.0 : -1.0;
var signY = shadow.offsetY >= 0.0 ? 1.0 : -1.0;
var mOffsetX = shadow.offsetX * Math.abs(obj.scaleX);
var mOffsetY = shadow.offsetY * Math.abs(obj.scaleY);
var offsetX = mOffsetX + (signX * mBlur);
var offsetY = mOffsetY + (signY * mBlur);
if (mOffsetX > mBlur) {
bounds.width += offsetX;
} else if (mOffsetX < -mBlur) {
bounds.width -= offsetX;
bounds.left += offsetX;;
} else {
bounds.width += mBlur * 2;
bounds.left -= mBlur - mOffsetX;
}
if (mOffsetY > mBlur) {
bounds.height += offsetY;
} else if (mOffsetY < -mBlur) {
bounds.height -= offsetY;
bounds.top += offsetY;
} else {
bounds.height += mBlur * 2;
bounds.top -= mBlur - mOffsetY;
}
}
return bounds;
}
resulting fiddle:
https://jsfiddle.net/e77c0owf/1/
// ************************************
// Bounding box calculation logic
// ************************************
function getObjBounds(obj) {
var bounds = obj.getBoundingRect();
var shadow = obj.getShadow();
if (shadow !== null) {
var blur = shadow.blur;
var mBlur = blur * Math.abs(obj.scaleX + obj.scaleY) / 4
var signX = shadow.offsetX >= 0.0 ? 1.0 : -1.0;
var signY = shadow.offsetY >= 0.0 ? 1.0 : -1.0;
var mOffsetX = shadow.offsetX * Math.abs(obj.scaleX);
var mOffsetY = shadow.offsetY * Math.abs(obj.scaleY);
var offsetX = mOffsetX + (signX * mBlur);
var offsetY = mOffsetY + (signY * mBlur);
if (mOffsetX > mBlur) {
bounds.width += offsetX;
} else if (mOffsetX < -mBlur) {
bounds.width -= offsetX;
bounds.left += offsetX;;
} else {
bounds.width += mBlur * 2;
bounds.left -= mBlur - mOffsetX;
}
if (mOffsetY > mBlur) {
bounds.height += offsetY;
} else if (mOffsetY < -mBlur) {
bounds.height -= offsetY;
bounds.top += offsetY;
} else {
bounds.height += mBlur * 2;
bounds.top -= mBlur - mOffsetY;
}
}
return bounds;
}
// ************************************
// Draw a ton of test cases below here
// ************************************
// Create a canvas
var canvas = new fabric.Canvas('c', {
backgroundColor: '#FFFFFF'
});
canvas.add(new fabric.Rect({
fill: 'red',
left: 100,
top: 100,
width: 50,
height: 50,
shadow: {
color: 'black',
blur: 0,
offsetX: -20,
offsetY: -10
}
}));
canvas.add(new fabric.Rect({
fill: 'red',
left: 30,
top: 20,
width: 50,
height: 50,
shadow: {
color: 'black',
blur: 0,
offsetX: -20,
offsetY: 0
}
}));
canvas.add(new fabric.Rect({
fill: 'red',
left: 30,
top: 250,
width: 50,
height: 50,
shadow: {
color: 'black',
blur: 0,
offsetX: 60,
offsetY: -60
}
}));
canvas.add(new fabric.Rect({
fill: 'red',
left: 180,
top: 20,
width: 50,
height: 50,
shadow: {
color: 'black',
blur: 10,
offsetX: 20,
offsetY: 20
}
}));
canvas.add(new fabric.Rect({
fill: 'red',
left: 180,
top: 150,
width: 50,
height: 50,
shadow: {
color: 'black',
blur: 20,
offsetX: 0,
offsetY: 0
}
}));
canvas.add(new fabric.Rect({
fill: 'red',
left: 220,
top: 280,
width: 50,
height: 50,
shadow: {
color: 'black',
blur: 20,
offsetX: -10,
offsetY: -10
}
}));
canvas.add(new fabric.Rect({
fill: 'red',
left: 280,
top: 20,
width: 50,
height: 50,
scaleX: 2.3,
scaleY: 2.3,
shadow: {
color: 'black',
blur: 10,
offsetX: 20,
offsetY: 20
}
}));
canvas.add(new fabric.Rect({
fill: 'red',
left: 360,
top: 230,
width: 50,
height: 50,
scaleX: 0.5,
scaleY: 0.5,
shadow: {
color: 'black',
blur: 20,
offsetX: 0,
offsetY: 0
}
}));
canvas.add(new fabric.Rect({
fill: 'red',
left: 380,
top: 290,
width: 50,
height: 50,
scaleX: 2.0,
scaleY: 2.0,
shadow: {
color: 'black',
blur: 10,
offsetX: 0,
offsetY: 0
}
}));
canvas.add(new fabric.Rect({
fill: 'red',
left: 140,
top: 380,
width: 50,
height: 50,
scaleX: -4.0,
scaleY: 0.5,
shadow: {
color: 'black',
blur: 20,
offsetX: -10,
offsetY: -10
}
}));
// Draw bounding boxes
var objs = canvas.getObjects();
var boxes = [];
for (var i = 0; i < objs.length; i++) {
var box = getObjBounds(objs[i]);
boxes.push(new fabric.Rect({
fill: '',
stroke: 'blue',
strokeWidth: 1,
left: box.left,
top: box.top,
width: box.width,
height: box.height
}));
}
for (var j = 0; j < boxes.length; j++) {
canvas.add(boxes[j]);
}
canvas.renderAll();
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.js"></script>
<canvas id="c" width="550" height="450"></canvas>

Categories