Reputation: 361
In the below Image I have three boxes in the canvas and there are three buttons at the bottom of the image. Whenever I click a button, the corresponding object in the canvas gets selected(i,e, when I click the green button, green rectangle in the canvas, gets selected).
My requirement is to highlight only the selected portion and other portion of the canvas should be grayed out. (Ex: If I click the green button green rectangle should be selected and other portion should be overlayed with a gray background).
Js Fiddle Link: https://jsfiddle.net/rnvs2hdk/1/
var canvas = new fabric.Canvas('c');
canvas.backgroundColor = 'yellow';
var li= []
canvas.renderAll();
fabric.Image.fromURL('http://fabricjs.com/assets/pug_small.jpg', function(myImg) {
var img1 = myImg.set({ left: 0, top: 0 ,width:400,height:500});
canvas.add(img1);
var green = new fabric.Rect({
left: 50,
top: 50,
width: 50,
height: 50,
fill: 'rgba(255,255,255,1)',
stroke: 'rgba(34,177,76,1)',
strokeWidth: 5,
name:"green"
});
var yellow = new fabric.Rect({
left: 150,
top: 50,
width: 50,
height: 50,
fill: 'rgba(255,255,255,1)',
stroke: 'rgba(255,255,0,1)',
strokeWidth: 5,
name:"yellow"
});
var red = new fabric.Rect({
left: 250,
top: 50,
width: 50,
height: 50,
fill: 'rgba(255,255,255,1)',
stroke: 'rgba(255,0,0,1)',
strokeWidth: 5,
name:"red"
});
canvas.add(green, yellow,red);
li.push(green);
li.push(yellow);
li.push(red);
li.some(v=>{
var btn = document.createElement("BUTTON"); // Create a <button> elem
btn.innerHTML = v.name;
btn.addEventListener('click',function(e){
var name = e.target
if(name.innerText == "green"){
canvas.setActiveObject(li[0]);
}
if(name.innerText == "yellow"){
canvas.setActiveObject(li[1]);
}
if(name.innerText == "red"){
canvas.setActiveObject(li[2]);
}
});// Insert text
document.body.appendChild(btn);
});
console.log(li);
});
Upvotes: 3
Views: 950
Reputation: 2862
Here's my solution. Using the after:render
event, you can perform canvas draw actions over each frame after it is rendered. This approach has the benefit of avoiding having to create and destroy fabric objects as needed which is an expensive operation.
Be sure to call the .setCoords()
method during actions like scaling and moving so that objects will update their position information while performing these actions.
var canvas = new fabric.Canvas('c');
canvas.backgroundColor = 'yellow';
var li = [];
canvas.renderAll();
fabric.Image.fromURL('http://fabricjs.com/assets/pug_small.jpg', function(myImg) {
var img1 = myImg.set({
left: 0,
top: 0,
width: 400,
height: 500
});
canvas.add(img1);
var green = new fabric.Rect({
left: 50,
top: 50,
width: 50,
height: 50,
fill: 'rgba(255,255,255,1)',
stroke: 'rgba(34,177,76,1)',
strokeWidth: 5,
name: "green",
hasRotatingPoint: false
});
var yellow = new fabric.Rect({
left: 150,
top: 50,
width: 50,
height: 50,
fill: 'rgba(255,255,255,1)',
stroke: 'rgba(255,255,0,1)',
strokeWidth: 5,
name: "yellow",
hasRotatingPoint: false
});
var red = new fabric.Rect({
left: 250,
top: 50,
width: 50,
height: 50,
fill: 'rgba(255,255,255,1)',
stroke: 'rgba(255,0,0,1)',
strokeWidth: 5,
name: "red",
hasRotatingPoint: false
});
canvas.add(green, yellow, red);
li.push(green);
li.push(yellow);
li.push(red);
li.some(v => {
var btn = document.createElement("BUTTON"); // Create a <button> elem
btn.innerHTML = v.name;
btn.addEventListener('click', function(e) {
var name = e.target
if (name.innerText == "green") {
canvas.setActiveObject(li[0]);
}
if (name.innerText == "yellow") {
canvas.setActiveObject(li[1]);
}
if (name.innerText == "red") {
canvas.setActiveObject(li[2]);
}
}); // Insert text
document.body.appendChild(btn);
});
console.log(li);
});
canvas.on({
'object:moving': function(e) {
//makes objects update their coordinates while being moved
e.target.setCoords();
},
'object:scaling': function(e) {
//makes objects update their coordinates while being scaled
e.target.setCoords();
}
});
//the after:render event allows you to perform a draw function on each frame after it is rendered
canvas.on('after:render', function() {
var ctx = canvas.contextContainer,
obj = canvas.getActiveObject();
if (obj) {
//set the fill color of the overlay
ctx.fillStyle = "rgba(0, 0, 0, 0.3)";
var bound = obj.getBoundingRect();
ctx.beginPath();
//draw rectangle to the left of the selection
ctx.rect(0, 0, bound.left, canvas.height);
//draw rectangle to the right of the selection
ctx.rect(bound.left + bound.width, 0, canvas.width - bound.left - bound.width, canvas.height);
//draw rectangle above the selection
ctx.rect(bound.left, 0, bound.width, bound.top);
//draw rectangle below the selection
ctx.rect(bound.left, bound.top + bound.height, bound.width, canvas.height - bound.top - bound.height)
ctx.fill();
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.min.js"></script>
<canvas id="c" width="400" height="400"></canvas>
<div id="bt"></div>
Upvotes: 2
Reputation: 361
I have Accomplished this creating 4 more rectangles around the actual rectangle. I have grayed out the outer rectangles so it gives the overlay effect. Also, I will delete all the rectangles before creating a new one.
let blankColor = "rgba(0,0,0,0)";
let grayOut = "rgba(0,0,0,0.4)";
let rect = createRect(x, y, width, height, 1, blankColor);
let rect1 = createRect(0, 0, getViewPortDimensions()[0], y, 0, grayOut);
let rect2 = createRect(0, y + height, getViewPortDimensions()[0], getViewPortDimensions()[1] - (y + height), 0, grayOut);
let rect3 = createRect(0, y, x, height, 0, grayOut);
let rect4 = createRect(x + width, y, getViewPortDimensions()[0] - (x + width), height, 0, grayOut);
state.canvas.add(rect, rect1, rect2, rect3, rect4);
Deleting the already drawn rectangle:
state.canvas.forEachObject((o, index) => {
if (index != 0) {
state.canvas.remove(o);
}
})
Upvotes: 1