Overlay rectangle on Fabric.js image - javascript

I have a custom image class as follows. What i want to do is after applying a certain logic, I want to show a colored border on the bounding box of the image. Since setting the borders makes the image selectable i was wondering if there was a way to somehow overlay a rectangle and make it transparent and just toggle its border color. How could that be done with a custom image class.
If there is a better way to accomplish this, please let us know
fabric.CustomImage = fabric.util.createClass(fabric.Image, {
type: 'named-image',
initialize: function (element, options) {
this.callSuper('initialize', element, options);
options && this.set('name', options.name);
options && this.set('rot', options.rot);
options && this.set('rawURL', options.rawURL);
options && this.set('belongsto', options.belongsto);
},
toObject: function () {
return fabric.util.object.extend(this.callSuper('toObject'),
{ name: this.name, rot: this.rot });
}
});
based on the suggestions tried the following
/////////////////////////////////////////////////////////////
//// create Custom Image class from Image class
fabric.CustomImage = fabric.util.createClass(fabric.Image, {
type: 'named-image',
initialize: function (element, options) {
this.callSuper('initialize', element, options);
options && this.set('name', options.name);
options && this.set('rot', options.rot);
options && this.set('rawURL', options.rawURL);
options && this.set('belongsto', options.belongsto);
options && this.set('myrect', true);
},
toObject: function () {
return fabric.util.object.extend(this.callSuper('toObject'),
{
name: this.name,
rot: this.rot,
myrect : ''
});
},
_render: function (ctx) {
this.callSuper('_render', ctx);
var r = new fabric.Rect();
r.left = this.left;
r.top = this.top;
r.height = this.height;
r.width = this.width;
r.borderColor = 'red';
r.strokeWidth = 2;
ctx.myrect = (r);
// ctx.add(r);
}
});
still not working, any ideas?
ok, so i am trying the following code but still cannot see a border around the image
var currentObj = ChrImages.d[j];
//closure, create a scope for specific variables
(function (obj) {
fabric.util.loadImage(obj.URL, function (img) {
var customImage = new fabric.CustomImage(img, {
name: obj.Name,
rot: obj.Rotation,
rawURL: obj.RawURL,
belongsto: obj.BelongsTo,
left: obj.PosX,
top: obj.PosY,
angle: obj.Rotation,
perPixelTargetFind: true,
});
customImage.stroke = true;
customImage.strokeWidth = 2;
customImage.lockScalingX = true;
customImage.lockScalingY = true;
customImage.filters.push(new fabric.Image.filters.RemoveWhite());
canvas.add(customImage);
groupWorkingImages.add(customImage);
});
})(currentObj);
any thoughts?

Related

Fabric.js - customized Fabric.Image class don't work at "loadFromJSON">"fromObject"

I'd like to save customized Fabric.Image class as JSON, and then later restore it with loadFromJSON.
I have looked up the following question
Fabric.js - how to save canvas on server with custom attributes
But it seems that the part of "fromObject" is not going well.
It happens when you press the 'Click' button on the following WEB page.
https://onoderayuuki.github.io/tana_motif/index.html
[subclass]
fabric.customMotif = fabric.util.createClass(fabric.Image, {
type: 'customMotif',
borderColor: '#19310B',
cornerColor: '#19310B',
cornerSize: 20,
initialize: function(src, options) {
this.callSuper('initialize', options);
options && this.set('name', options.name);
this.image = new Image();
this.top = options.top;
this.left = options.left;
this.image.src = src;
this.image.onload = (function() {
this.width = this.image.width;
this.height = this.image.height;
this.loaded = true;
this.setCoords();
this.fire('image:loaded');
}).bind(this);
},
toObject: function(options) {
return fabric.util.object.extend(this.callSuper('toObject'), {
});
},
_render: function(ctx) {
if (this.loaded) {
ctx.drawImage(this.image, -this.width / 2, -this.height / 2);
}
}
});
fabric.customMotif.fromObject = function(object, callback) {
fabric.util.loadImage(object.src, function(img) {
callback && callback(new fabric.customMotif(img, object));
});
};
[JSON]
canvasState = JSON.stringify(canvas);
canvas.loadFromJSON(canvasState);
thanks.
If you check this function you will that the first letter of the class is uppercase.
getKlass: function(type, namespace) {
// capitalize first letter only
type = fabric.util.string.camelize(type.charAt(0).toUpperCase() + type.slice(1));
return fabric.util.resolveNamespace(namespace)[type];
},
Rename the class from fabric.customMotif to fabric.CustomMotif

Highcharts: Tooltip delay before display

On my highchart i need a delay before the series tooltip is displayed.
I defined a new refresh function with a timer to realize it. If the timer is ready i check if the mouse position. If it moved not that much the tooltip should appear.
function (H) {
var timer = [];
var mousePosition = {
x: 0,
y: 0
};
window.addEventListener("mousemove", function (event) {
mousePosition.x = event.pageX;
mousePosition.y = event.pageY;
});
var getMousePositionX = function () {
return mousePosition.x;
};
var clearTimer = function () {
timer = [];
}
H.wrap(H.Tooltip.prototype, 'refresh', function (proceed) {
var mousePosX = getMousePositionX();
var delayForDisplay = this.chart.options.tooltip.delayForDisplay ? this.chart.options.tooltip.delayForDisplay : 1000;
timer[timer.length+1] = window.setTimeout(function () {
var currMousePosX = getMousePositionX();
if ((mousePosX >= currMousePosX - 5 && mousePosX <= currMousePosX + 5)) {
this.proceed.apply(this.tooltip, this.refreshArguments);
clearTimer();
}
}.bind({
refreshArguments: Array.prototype.slice.call(arguments, 1),
chart: this.chart,
tooltip: this,
clearTimer: clearTimer,
proceed: proceed
}), delayForDisplay);
});
};
The problem I have is, that the hover holos have also a delay.
Here is a sample: JSFiddle
Any solutions for this issue?
You can make new tooltip basing on your standard Highcharts tooltip and show it on your mouseover with some timeout:
load: function() {
chart = this;
this.myTooltip = new Highcharts.Tooltip(this, this.options.tooltip);
this.tooltip.label.element.remove();
}
point: {
events: {
mouseOver: function(e) {
var i = this.x;
points = [];
Highcharts.each(this.series.chart.series, function(s) {
Highcharts.each(s.data, function(p) {
if (p.x === i) {
points.push(p)
}
})
});
myTooltip = chart.myTooltip;
setTimeout(function() {
myTooltip.refresh(points, e)
}, 1000)
}, mouseOut: function() {
setTimeout(function() {
chart.myTooltip.hide();
}, 1000)
}
}
}
Here you can see an example how it can work: http://jsfiddle.net/az39das8/

Performance decrease when adding KineticJS nodes to canvas

I'm working on a floorplan editor program which is implemented in part using HTML5 Canvas and KineticJS. The user can select from a couple of tools to place nodes on the canvas (rooms, hallways, staircases, etc), and can then create edges between the nodes to link them.
I'm running into a problem when placing a specific type of node on my canvas: I can add any number of most types of nodes I've got defined without any performance decrease, but as soon as I try and add more than a handful of 'room' nodes, they take longer and longer to render and the stage tap event handler which creates nodes becomes unresponsive. This issue only occurs on the iPad and not when I run the application from a desktop.
All nodes are comprised of a Kinetic Circle with a particular image to represent the type of node and some data such as x/y position, type, id, and a list of attached edges. The only difference between room nodes and other special nodes is an extra Kinetic Rect and a couple of variables to represent the room itself. When creating a new node, all the attributes are filled by the constructor function, then the node is passed to a setup function to register event handlers and a render function to render it based on its type.
I'm still fairly new with these technologies, so any advice, no matter how specific or general, will probably be helpful. I've attached what I think will be helpful snippets of code; if you need to see more or have questions about what's here please let me know.
Thank you so much for any time and effort.
=========================================================================
EDIT:
Removing the opacity attribute of the room nodes made a huge difference; the performance decrease is almost entirely negligible now. I'll post further updates if and when I work out why that's the case.
function Node(x, y, id, type, attr) {
this.x = x;
this.y = y;
this.nX = 0;
this.nY = 0;
this.type = type;
this.bbox;
this.id = id;
this.edges = {};
this.attr = {"zPos": 0,
"name": '',
"width": default_room_width,
"height": default_room_height};
this.render(x, y, node_radius);
}
Node.prototype.render = function(x, y, r){
//Create node on canvas
this.visual = null;
var _self = this;
if(this.type == "node" || this.type == "Hallway"){
this.visual = new Kinetic.Circle({
x: x,
y: y,
radius: r,
fill: '#2B64FF',
stroke: '#000000',
strokeWidth: 2,
draggable: true
});
nodeLayer.add(this.visual);
this.x = this.visual.x();
this.y = this.visual.y();
nodeLayer.draw();
this.visual.id = this.id;
this.setupNode(x, y, node_radius);
} else {
var image = new Image();
_self.visual = new Kinetic.Image({
x: x - (node_img_size / 2),
y: y - (node_img_size / 2),
image: image,
width: node_img_size,
height: node_img_size,
draggable: true
});
image.onload = function(){
_self.setupNode(x, y, node_radius);
nodeLayer.add(_self.visual);
_self.x = _self.visual.x();
_self.y = _self.visual.y();
nodeLayer.draw();
_self.visual.id = _self.id;
}
image.src = '../../img/' + this.type + 'tool.png';
var width = this.attr["width"];
var height = this.attr["height"];
if(this.type== "room") {
this.bbox = new Kinetic.Rect({
x: x,
y: y,
strokeWidth: 2,
stroke: "black",
fill: "black",
opacity: 0.5,
listening: true,
width: width,
height: height,
offset: {x:width/2, y:height/2}
});
setupRoom(this.bbox);
this.bbox.offsetX(default_room_width/2);
this.bbox.offsetY(default_room_height/2);
roomLayer.add(this.bbox);
roomLayer.draw();
}
}
}
//Bind event handlers for rooms
function setupRoom(room) {
room.on(tap, function(e) {
if(selectedRoom == this) {
selectedRoom = null;
this.setOpacity(0.5);
roomLayer.draw();
} else {
if(selectedRoom != null)
selectedRoom.setOpacity(0.5);
selectedRoom = this;
this.setOpacity(1);
roomLayer.draw();
}
});
room.on('mouseenter', function(e) {
is_hovering = true;
}).on('mouseleave', function(e) {
is_hovering = false;
});
}
Node.prototype.setupNode = function (x, y) {
var _self = this;
var c = this.visual;
c.on('mouseenter', function(e){
is_hovering = true;
}).on('mouseleave', function(e){
is_hovering = false;
});
c.on(tap, function(e){
if(is_adding_node == true && _self.type == "node" && linkerNode != _self) {
is_adding_node = false;
nodeDrag = false;
$.each(nodes, function(key, value) {
if(value.type == "node") {
value.visual.fill("#2B64FF");
}
});
nodeLayer.batchDraw();
joinNodes(linkerNode, _self);
} else {
handleNodeTap(e, _self);
}
});
c.on(dbltap, function(e){
console.log(_self.id);
if(selectedTool != "link"){
showMenuForNode(c);
}
});
//Drag event
c.on(touchstart, function(e){
nodeDrag = true;
}).on(touchmove, function(e){
var touchPos = stage.getPointerPosition();
var newX = c.x() + offsetX * -1;
var newY = c.y() + offsetY * -1;
_self.x = newX;
_self.y = newY;
if(_self.text != null) {
_self.text.x(_self.visual.x() - node_text_offset);
_self.text.y(_self.visual.y() - node_text_offset);
}
//Move room BB if set
if (_self.bbox != undefined) {
_self.bbox.x(_self.visual.x() + _self.visual.width()/2);
_self.bbox.y(_self.visual.y() + _self.visual.height()/2);
roomLayer.batchDraw();
}
//Update positions of connected edges
for(var x in _self.edges){
edges[_self.edges[x]].setPosition();
}
nodeLayer.batchDraw();
}).on(touchend, function(e){
currentNode = _self;
nodeDrag = false;
});
};

Kinetic.js masked image with slow performance on firefox

I'm new to kinetic and I don't know about performance issues.
I made this example, you just have to click on the black and white image and drag over it, then, a colored circle apears.
The performance in chrome, safari on an Ipad, and even Opera Mobile on an android phone is quite good. In firefox it starts ok, but if you move the mouse for a while it slows down and doesn't work properly. The firebug profiler doesn't help a lot... How could I debug this issue in a better way?
In the drawing function there's an inner method onMove to do the hard work. I believe here lies the performance problem but I don't know how to achieve the same effect in a better way.
Any ideas?
function draw(images) {
var stage = new Kinetic.Stage({
container : 'container',
width : 1024,
height : 483
}), bn_layer = new Kinetic.Layer(), color_layer = new Kinetic.Layer(), circle_layer = new Kinetic.Layer(), bn = new Kinetic.Image({
x : 0,
y : 0,
image : images.bn,
width : 1024,
heigth : 483
}), tmp_circle = null, movable = false;
bn_layer.add(bn);
tmp_circle = addCircle(circle_layer, images);
var onMove = function() {
if (movable) {
var pos = getMousePosition(stage);
circle_layer.draw();
tmp_circle.remove();
tmp_circle.setPosition(pos.x, pos.y)
tmp_circle.setFillPatternImage(images.color);
tmp_circle.setFillPatternOffset(pos.x, pos.y);
circle_layer.add(tmp_circle);
}
}
stage.on("mousemove touchmove", onMove);
stage.on("mousedown touchstart", function() {
debug("activo")
circle_layer.show();
movable = true;
onMove();
circle_layer.draw();
});
stage.on("mouseup touchend", function() {
debug("inactivo")
circle_layer.draw();
tmp_circle.remove();
circle_layer.hide();
movable = false;
})
//stage.add(color_layer);
stage.add(bn_layer);
stage.add(circle_layer);
circle_layer.hide();
}
Update: Changing the mouse event for a requestAnimationFrame method controlled with a flag the performance improves a lot in firefox on windows. In firefox on Linux the performance is still crappy.
I think this might have some relation with what is commented in this topic:
Poor Canvas2D performance with Firefox on Linux
There they are talking about a possible bug in firefox related to the cairo libraries:
http://blog.mozilla.org/joe/2011/04/26/introducing-the-azure-project/
https://bugzilla.mozilla.org/show_bug.cgi?id=781731
Updated code
function Anim(layer, funcion){
var run = false;
var callback = funcion;
this.layer = layer;
function animate(){
callback();
if (!run){
return;
}
requestAnimFrame(function(){
animate();
})
};
this.start = function(){
run = true;
animate();
};
this.stop = function(){
run = false;
};
}
//draw on frames
function drawAnim(images){
var stage = new Kinetic.Stage({
container : 'container',
width : 1024,
height : 483
}), bn_layer = new Kinetic.Layer(),
hitareas_layer = new Kinetic.Layer(),
circle_layer = new Kinetic.Layer(),
bn = createImage(images.bn),
tmp_circle = null,
movable = false,
hit_areas = null,
c = 0,
colorArea = function() {
if (movable) {
var pos = getMousePosition(stage);
debug("posicion: "+pos.x+" "+pos.y+" " +c+ " " +tmp_circle.getX()+ " "+tmp_circle.getY());
if(pos.x !== tmp_circle.getX() || pos.y !== tmp_circle.getY()){
c++;
circle_layer.draw();
tmp_circle.remove();
tmp_circle.setPosition(pos.x, pos.y);
tmp_circle.setFillPatternImage(images.color);
tmp_circle.setFillPatternOffset(pos.x, pos.y);
circle_layer.add(tmp_circle);
}
}
},
anim = new Anim(null, function(){
colorArea();
}),
onPress = function() {
circle_layer.show();
//hitareas_layer.hide()
movable = true;
colorArea();
circle_layer.draw();
anim.start();
}, onRelease = function() {
anim.stop();
circle_layer.draw();
tmp_circle.remove();
circle_layer.hide();
//hitareas_layer.show()
movable = false;
c=0;
};
//hit_areas = setHitAreas(bn_layer);
bn_layer.add(bn);
tmp_circle = addCircle(100, {
x : 50,
y : 50
});
hit_areas = setHitAreas(hitareas_layer, images.color);
bn_layer.on(HitArea.HITTED, function(){
console.log("this");
})
//descomentar si queremos efecto al mover el rat�n
//stage.on("mousemove touchmove", colorArea);
stage.on("mousedown touchstart", onPress);
stage.on("mouseup touchend", onRelease);
stage.add(bn_layer);
stage.add(circle_layer);
stage.add(hitareas_layer);
circle_layer.hide();
}
Place if (movable) condition outside of onMove() function, this way you`ll not check every time for this capability:
if (movable) {
var onMove = function() {
var pos = getMousePosition(stage);
circle_layer.draw();
tmp_circle.remove();
tmp_circle.setPosition(pos.x, pos.y)
tmp_circle.setFillPatternImage(images.color);
tmp_circle.setFillPatternOffset(pos.x, pos.y);
circle_layer.add(tmp_circle);
}
}

Using LibCanvas to draw an image

Im am currently using LibCanvas to do canvas drawing in my html page, but I can't seem to figure out how to draw an image. Can anyone help me with this?
http://libcanvas.github.com/
EDIT: I am currently using this piece code, I see the image being drawn but then it dissapears;
var libcanvas = new LibCanvas('canvas', { preloadImages: false }).start();
var img = new Image();
img.src = 'images/draw.png';
img.width = 400;
img.height = 400;
libcanvas.addEvent('ready', function()
{
libcanvas.ctx.drawImage(img);
});
It depends on your aim.
If you just want to draw image - you can use plain context, without creating LibCanvas object:
var img = atom.dom.create( 'img', { src: 'images/draw.png' })
.bind( 'load', function () {
atom.dom( 'canvas' ).first
.getContext( '2d-libcanvas' )
.drawImage( img );
});
Your image dissapears because you draw it not in frame. So, second way - is to draw it in "render" part:
new LibCanvas('canvas', {
preloadImages: { foo: 'images/draw.png' }
}).start(function () {
this.ctx.drawImage( this.getImage('foo') );
});
The right way if you try to create big app - is creating special object:
LibCanvas.extract();
var ImageDrawer = atom.Class({
Extends: DrawableSprite,
initialize: function (sprite, shape) {
this.sprite = sprite;
this.shape = shape;
}
});
new LibCanvas('canvas', {
preloadImages: { foo: 'images/draw.png' }
})
.start()
.addEvent('ready', function () {
var drawTo = new Rectangle( 0, 0, 100, 100 );
var drawer = new ImageDrawer( this.getImage('foo'), drawTo );
this.addElement( drawer );
});
In such way you can easy make it, e.g. draggable:
LibCanvas.extract();
var ImageDrawer = atom.Class({
Extends: DrawableSprite,
Implements: [ Draggable ],
initialize: function (sprite, shape) {
this.sprite = sprite;
this.shape = shape;
}
});
new LibCanvas('canvas', {
preloadImages: { foo: 'images/draw.png' }
})
.start()
.addEvent('ready', function () {
var drawTo = new Rectangle( 0, 0, 100, 100 );
var drawer = new ImageDrawer( this.getImage('foo'), drawTo );
this.addElement( drawer );
drawer.draggable();
});
For answers about LibCanvas you can write to me, shocksilien#gmail.com
Look at one of the examples provided that uses images and do what it does.
http://libcanvas.github.com/games/asteroids/
For example:
this.libcanvas.ctx.drawImage({
image : engines[type].image,
center : this.somePosition,
angle : this.angle
});

Categories