If you change the size/position of the object not manually, but in the code, the controls may not be displayed correctly. For example, I created a square and moved it. If you move the mouse to its old place, the cursor will change and if you start moving the object will move but relative to its new position. If you click on its new location, nothing will happen. This is corrected if you click on a different place on the canvas.
You need to call .setCoords() on your object after changing values like position programmatically.
See the following:
http://fabricjs.com/fabric-gotchas
https://github.com/fabricjs/fabric.js/wiki/When-to-call-setCoords
This function helped me:
function Moved() {
canvas.discardActiveObject();
canvas.setActiveObject(object)
canvas.discardActiveObject();
canvas.renderAll();
}
It should be called at the end of initialization of the canvas and objects and after each change of location.
It is also better to use renderAll() instead of requestRenderAll() wherever possible.
Related
The problem: I'm trying to create a simple drawing app using p5.js. Instead of the standard cursor image, I'd like to show a circle at my cursor location that represents the size of the drawing brush.
Potential solution 1: Replace the cursor using the cursor() function native to p5.
Why it doesn't work: The p5 cursor function only takes the following parameters:
ARROW, CROSS, HAND, MOVE, TEXT, or WAIT, or path for image
As such, there's no native way to replace the cursor using the ellipse class.
Potential solution 2: Use the noCursor() function and then draw the circle at the cursor location, while also drawing the background, as such:
var brushSize = 50;
function setup() {
createCanvas(1080,720);
noCursor();
}
function draw() {
background(100);
ellipse(mouseX,mouseY,brushSize);
}
Why it doesn't work: While this solution gets the desired effect i.e. replacing the cursor with a circle the size of the brush, the constantly updating background prevents me from actually drawing to the canvas with the brush when I want to.
Is there some way I can replace the cursor without actually drawing the ellipse to the canvas? Is there any way to save and then instantly reload a canvas in p5? I couldn't find such a method searching through the API docs. Any hints are appreciated.
According to the reference, you can pass a URL into the cursor() function to set an image.
If you want to use an image that you draw, you're going to have to draw them ahead of time and save them to files, and then use those files. Something like this:
cursor('images/ellipse-15.png');
Where ellipse-15.png is an image that you generated ahead of time, to match when brushSize is 15, for example.
Btw P5.js is just setting the cursor CSS property. You can read more about it here.
If you want to go with the noCursor() approach and draw the ellipse yourself, you could draw your drawing to a buffer (the createGraphics() function is your friend) and then draw the ellipse on top of that every frame. I'd still probably use a cross cursor just because there's going to be some annoying lag if you draw it yourself.
Create a circular DIV inside the canvas container and show it on top of the actual canvas.
Iv`e created canvas pointer error. It shows a target to desired location (mouse position):
http://jsfiddle.net/conmute/rk276q3g/
The problem is in Firefox rendering, (move mouse very fast):
http://jsfiddle.net/conmute/rk276q3g/1/
rectangle acts weirdly, and i am missing smth. Can anybody point what exactly?
ctx.rect(
// start x,y pair
patternOffset.x, -p.circle.h/2 - patternOffset.y - 12,
// end x,y pair
-p.repeat.w, distance - p.circle.h - 1
);
ctx.fillStyle = pattern;
ctx.fill();
Status update
I fixed this issue:
http://jsfiddle.net/rk276q3g/2/
By commenting out ctx.save() and .restore()
and placing arrow image before drawing the rectangle.
So actually i fixed this issue, But the thing is...
Question update
... what if i need to draw after i did some ctx.translate(... and some drawings?
I need to use ctx.save() and .restore() methods, but it cause a problems. How can i do this work without commenting them? Cause when i remove them it solves problem.
I thought that i understand properly how they work, but i see that i don't.
Update
It appears that by removing all ctx.restore() and ctx.save() solves the solution, but when i add my part to be drawn after i translateed back by calculating previous position, problem appears again!
Please see:
http://jsfiddle.net/rk276q3g/5/
The save() and restore() work as follows: The <canvas> maintains a stack of its state and each item in the stack represents all the attributes of the context (lineStyle, strokeWidth, transform, …). By calling save() a new item of the state is created and pushed onto stack. By calling restore() the item is popped from the stack and the state of the canvas is set to the properties from the item on top of the stack.
Here is a good example.
To answer your question: You need to save the state of the context before you translate and rotate it, to create a new current item on the stack, which you can modify to your needs. Once you've done the heavy lifting and want to draw some things based on the default values, you need to restore the state and every paint action will be based on default values.
To not run into conflicts as you do, I always write painting methods following this pattern:
function drawSomethingFanzy (ctx) {
ctx.save();
//some really awesome drawing here;
ctx.restore();
}
Edit
I have missed to point out that save() creates a new Item on the stack, but does not restore the default settings, all settings remain unchanged, but are saved and can be restore()-ed later on.
Edit
I attached a screenshot of the result which shows up at my computer from fiddle. For me that looks correct, so could please post a picture that shows what the exact problem is, or what you would like to achieve? I honestly do not get what the problem is. Do you mean the gaps in the pattern fill?
Update
I just had a moment and created a fiddle of what I think you want here. I hope it helps!
I have a group, inside of which is an image. I've got code working so you can zoom with the mouse wheel. When the zooming stops, I reload the image, in a larger size. I add the new image to the group ( the old image is still part of the group ) and then I do this:
img.remove();
img.destroy();
imgNew.moveToTop();
imgNew.offsetX = offset.x;
imgNew.offsetY = offset.y;
At the end of this code, my old image disappears and the new one is not visible. It has a position that is sane, and it is the child of my group. It has exactly the size I expect it to have ( it's absolute position is 0,0 and it's size is bigger than my canvas ). I've turned off clipping, so if it was visible anywhere, I'd see it. I've changed the position in the debugger, and called draw() on the canvas, the layer and the group. I have dragging code and I've dragged the drag control ( which is still there ) every where I can. I've also changed my code to just add the new image to the top level layer instead of the lower down group.
I should mention in case it's relevant, the image is coming from a WebAPI RESTful service, and the size is passed in, so the image URLs are different for the two images.
I simply cannot find my image !!! What should I do next ?
This:
KineticJS - How to Change Image src on Button Click
answered my question. Short version: there's a setImage method so you can load a new image, call setImage() and it gets replaced.
I have a little tool that draws up a grid of circles(representing holes) that allows the user to add text and lines to these circles. Right now I have it set up so if the user clicks on any of the holes then wherever the hole is moved so is every other element on the Paper object. What I am trying to implement next is the ability to rotate everything as one object. I realize that for this to work that I need to know the central point of all the objects, which I can easily get.
What I want to know is should I draw everything on another object. This object will act as another Paper object of sorts, but will only serve for movement and rotation. Any click events on the holes drawn on the object will be passed on to the parent (i.e. the pseudo-paper object everything is drawn on). Is this possible? If so how would I draw everything onto say, a rectangle? And if not what would be the best way to go implementing it?
What you need is a Set. You create it, push objects to it, and then treat it as an entire group, in your case by applying transformations.
Example:
var elements = paper.set();
if (!view.text) {
view.text = App.R.text(0, 0, this.value);
view.text.attr({
'font-size': font_size,
});
elements.push(view.text);
}
elements.transform('something');
Note that you can also bind events to this entire set.
I wish to create a click-able object on a tag with javascript/jQuery.
This obviously dosen't work.
var cow = new Object();
cow = ctx.drawImage(tile,cursorH,cursorV);
$(cow).click{function(){
alert('You clicked a cow!');
});
The solution is simple, but requires some groundwork be laid. First, you'll need to keep track of the "objects" you draw on the canvas. Perhaps create your own object class that keeps track of position and size. Secondly you override the onclick event for the canvas and perform a hit test on all your visible objects. The ones that are located under your cursor at the time of the click were clicked upon.
I don't think you can do it "right out of the box." Check out Fabric.js (demo) though, I believe it has support for drawing selectable objects to the canvas.