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.
Related
I am using Fabric JS to allow the user to have an interactive experience on my React app. Is it possible to apply a frame around a Fabric JS that is taken from an image? For instance, if the canvas is 400x400 px I can resize an image of a frame that is transparent in the middle to 410x410px and apply it on top of the canvas for the user to see? I have attached two images for reference.
Edit: This is the code I am using for zooming in
const zoomIn = useCallback(() => {
// Get original height of canvas
const canvasDimensions = getInitialCanvasSize()
let zoom = HTML5Canvas.getZoom()
zoom += 0.2
if (zoom >= 2) zoom = 2
HTML5Canvas.setZoom(zoom)
HTML5Canvas.setWidth(canvasDimensions.width * HTML5Canvas.getZoom());
HTML5Canvas.setHeight(canvasDimensions.height * HTML5Canvas.getZoom());
}, [HTML5Canvas])
There is no option for canvas's border in fabricjs canvas docs
But you can still achieve this easily using following steps.
PART 1: Creating the Illusion of border
CSS Method
First one can easily create CSS border around the canvas.
Best way to do this is to create div around canvas, as fabricjs split canvas in 2 while running.
You can create slider to control width and color/image for div's border.
This will looks like exactly your second image with customization.
OR
Another Canvas Method
Behind current canvas put this second canvas and control its width and image.
I don't recommend this one, as this will make it more complex to implement.
PART 2: Making Illusion real
If you used CSS METHOD
Now you get what your canvas looks like. You have width of border, image/color of border.
Steps:
Create new canvas (lets' call it 2nd Canvas) of 410px if canvas's width 400px with border of 5px.
Export main canvas as image and put it over 2nd Canvas. And now you can export this as final image.
For 2nd step check my answer on this stack
If you used Another Canvas Method
Directly follow above 2nd step
Export main canvas as image and put it over 2nd Canvas. And now you can export this as final image.
For 2nd step check my answer on this stack
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.
i want to draw a circle on an image in easeljs.
My actual status:
In my canvas 1200 x 800 is a bitmap 9000 x 1000.
The image got a "pressmove" function, so i can navigate from the left to the right of the panorama and from the top to the bottom.
I want to draw a circle/shape onto the image/bitmap.
For example at the coordinate of the image x:3000, y:500.
With my actual attempt to do this i created a container and added the bitmap and the shape in there. If i say bitmap.x/y = shape.x/y, the shape stays where it belongs to be but i need to tie it a specific coordinate.
The picture shows my actual view, the whole picture and the point (x,y) where i want to draw a circle onto the picture. So if i am showing this part of the picture there should be the point.
Do you guys have any idea how to do this?
Problem Solved
As #Lenny said, moving the container instead is moving both of the displayed objects. That's the result i wanted.
I have two canvases. I have made them circular using border-radius. The 2nd is positioned inside the first one (using absolute position).
I have click events on both circles. If you click on inside canvas, the color at the point of the click is loaded in the outside canvas with opacity varying from white to the picked color and finally to black. If you click on outer canvas the exact color value at that point is loaded in the text-box at the bottom
I am unable to click in red zones (as shown in figure below) of the outer canvas when using chrome. I tried z-idex, arcs but nothing is helping me. But In Firefox everything is working fine.
Note: You can drag the picker object in the outer circle. But if you leave it in red zones, you would not be able to click it again in Chrome. Clicking in green zone will get you its control again
Code in this JSFiddle
Edit
I excluded all irrelevant code to make it easy. Now there is only a container having two canvas.
Filled simply with two distinct colors. Open following fiddle link in both chrome and firefox. Click on both cirles in different zones and see difference in chrome and firefox. I want them to behave in chrome as they do in firefox
Note I will ultimately draw an image in inner canvas.
Updated Fiddle Link
-
Your problem is because canvases currently are always rectangular, even if they don't look rectangular. Border radius makes the edges except the circle transparent, but it still doesn't stop events in Chrome on the corner areas. This is why you cannot click the bottom circle in those areas
I even tried putting it inside of a container that had a border-radius instead but the click event still goes through
With that being said, you have two options. You could either change your code to only use one canvas with the same type of layout, just drawing the background circle before the other each time. Essentially you'd draw a circle, draw your black to color to white gradient, use the xor operation to combine the two into one circle, then do the same with the rainbox gradient. You must draw the background circle first because canvas paints over the old layers every time
or
You could use javascript to only detect clicks in the circular area which takes just a little bit of math (: This solution is featured in edit below
In the future, CSS Shapes may allow canvases to be non-rectangular elements to be used, I'm actually not sure, but we don't have that capability yet at least
Edit
Alright, so after going through your code a bit it seems there are some things I should cover before I offer a solution
Setup all your finite variables outside of the functions that run every time. This means you don't put them (like radiuses, offsets, etc.) in the click function or something that runs often since they don't change
Your "radius"es are actually "diameter"s. The format of .rect goes .rect(x, y, width (diameter of circle), height (diameter of circle))
Almost always when overlaying canvases like you are you want to make them equal dimensions and starting position to prevent calculation error. In the end it makes it easier, doing all relative positioning with javascript instead of mixing it with CSS. In this case, however, since you're using border-radius instead of arc to make a circle, keep it like it is but position it using javascript ....
jQuery isn't needed for something this simple. If you're worried about any load speed I'd recommend doing it in vanilla javascript, essentially just changing the .click() functions into .onclick functions, but I left jQuery for now
You can declare multiple variables in a row without declaring var each time by using the following format:
var name1 = value1,
name2 = value2;
Variables with the same value you can declare like so:
var name1 = name2 = sameValue;
When children have position:absolute and you want it to be positioned relative to the parent, the parent can have position:relative, position:fixed, or position:absolute. I would think you'd want position:relative in this case
When you don't declare var for a variable it becomes global (unlessed chained with a comma like above). For more on that read this question
Now, onto the solution.
After talking with a friend I realized I could sort do the math calculation a lot easier than I originally thought. We can just calculate the center of the circles and use their radiuses and some if statements to make sure the clicks are in the bounds.
Here's the demo
After everything is set up correctly, you can use the following to detect whether or not it's in the bounds of each
function clickHandler(e, r) {
var ex = e.pageX,
ey = e.pageY,
// Distance from click to center
l = Math.sqrt(Math.pow(cx - ex, 2) + Math.pow(cy - ey, 2));
if(l > r) { // If the distance is greater than the radius
if(r === LARGE_RADIUS) { // Outside of the large
// Do nothing
} else { // The corner area you were having a problem with
clickHandler(e, LARGE_RADIUS);
}
} else {
if(r === LARGE_RADIUS) { // Inside the large cirle
alert('Outer canvas clicked x:' + ex + ',y:' + ey);
} else { // Inside the small circle
alert('Inner canvas clicked x:' + ex + ',y:' + ey);
}
}
}
// Just call the function with the appropriate radius on click
$(img_canvas).click(function(e) { clickHandler(e, SMALL_RADIUS); });
$(wheel_canvas).click(function(e) { clickHandler(e, LARGE_RADIUS); });
Hopefully the comments above and code make enough sense, I tried to clean it up as best as I could. If you have any questions don't hesitate to ask!
Does anybody know if there is a way to 'export' an entire KineticJS layer (or group) and not just the part that is in the visible canvas?
I have a simple 'image editor' that allows the user to scale the image in the canvas, the image can deliberately be scaled larger than the visible canvas stage area allowing the user to pan the image sections into view. The problem is that when they come to export the image (currently using the toDataURL) only the section of the image in the visible canvas stage is saved, the rest of the image is discarded!
I effectively want to save the whole layer, not just what is rendered in the visible canvas stage.
Well, You need a temporary workaround.
I assume you have a function or something that does .toDataURL().
Right before you do toDataURL(), change the stage size or scale, then save, then change back.
Example:
function save(){
stage.setScale(newX,newY); //rescale to new values
stage.setWidth(newWidth);
stage.setHeight(newHeight);
stage.toDataURL(); // <----- save
stage.setScale(oldX,oldY); //rescale to old values
stage.setWidth(oldWidth);
stage.setHeight(oldHeight);
}
This will resize or rescale your stage and then put it back the way it was.