Fabric.js: Image controls behind an overlay image - javascript

I'm building a canvas user interface with jquery and fabric.js library and I set an overlay png image with a transparent section using the following code
var bgImgSrc = bgImg.attr('src');
canvas.setOverlayImage(bgImgSrc, function(img){
canvas.renderAll();
});
I also added an image behind the overlay and resized to fit the container using the following code
var photoImg = $('#img-photo');
var photoImgSrc = photoImg.attr('src');
fabric.Image.fromURL(photoImgSrc, function(img) {
var photoImgWidth = photoImg.width();
var photoImgHeight = photoImg.height();
var hRatio = 380/photoImgWidth;
var vRatio = 300/photoImgHeight;
var ratio = Math.min(hRatio, vRatio);
pImg = img.set({left: 380/2, top: 300/2, angle: 0})
img.scale(ratio).setCoords();
canvas.add(pImg);
canvas.sendToBack(pImg);
canvas.renderAll();
});
And it works as expected, however, when I click on the image to scale/resize it, I don't see the controls, except through the transparent space of the overlay image. Controls are behind the overlay image, is there a way to force show the image controls without having to put the entire image in front of the overlay?

Just set the boolean controlsAboveOverlay:
canvas.controlsAboveOverlay = true;

The problem here is that overlay image is rendered on top of objects' controls. This is expected behavior. Take a look at this wiki article, which shows the z-index of various things on fabric canvas and in which order they're rendered.
There are plans to add support for object controls that are always on top, as you can see from this ticket, but I can't tell you when that's going to happen.
You can also try using "after:render" callback and draw image manually onto canvas.

canvas.controlsAboveOverlay = true; //did the job... but:
I don't want to render controls above the overlay image (as overlay means overlay to me). I just want to render the stack exactly as described in Wiki (by default).
I just wonder...as described in the Wiki rendering-stack, the controls should be rendered on top of all objects (always visible by default), but they do not (look at kitchensink demo).
IMHO this behaviour should be set by a flag like canvas.controlsInStack = true.
By the way ... that thing is really beautiful!

Related

Apply frame image on top of Fabric JS Canvas

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

Image on HTML5 canvas only appearing for one frame

Long time lurker but never made an account. Just wanted to preface that I'm by no means a dev and just tinkering and experimenting for fun, so I apologise in advance if I seem really dumb.
I'm working on a dynamic overlay for Twitch streaming and was previously using AS3 but I've switched over to HTML5 now. I'm trying to load an image onto the canvas (which will eventually be a profile picture fetched using Twitch API... but one step at a time). I'm using Adobe Animate and I have the following so far applied in Actions on the first frame of the layer:
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d');
show_image();
function show_image() {
source_image = new Image();
source_image.src = 'https://cdn.sstatic.net/stackexchange/img/logos/so/so-icon.png';
source_image.onload = function () {
context.drawImage(source_image, 100, 100);
}
}
When I hit Ctrl+Enter and see it in Chrome, the image appears for the first frame then disappears. I'm not sure how I'm supposed to get it to stay indefinitely. I need to be able to animate it later, and it'll change depending on the latest follow/donation/sub, etc.
I tried extending the frame itself in the timeline, however, this just changed long how it took to loop and didn't make the image itself stay longer. I'm probably missing something really simple!
Any help would be appreciated. Thanks!
Your code is okay if your approach is using a canvas with HTML and JS, without any libraries involved. However, this is not the case, as you are using Animate, and the way to draw graphics with it is different than using default canvas methods like drawImage().
Animate includes the CreateJS suite, which includes the EaselJS library ,and this allows you to use another tools to draw to your canvas. Two or them are the Stage object, the visual container of your animate project, and the Bitmap object, who represents an image, canvas or video. For effects of this question, only both objects are required.
Note that the code below is only for the first frame:
/* It is not necessary to declare the canvas or stage element,
as both are already declared. At this point the stage is ready to be drawn */
show_image();
function show_image() {
var source_image = new Image();
source_image.src = 'https://cdn.sstatic.net/stackexchange/img/logos/so/so-icon.png';
source_image.onload = function(event) {
/* A new Bitmap object is created using your image element */
var bmp = new createjs.Bitmap(event.currentTarget);
/* The Bitmap is added to the stage */
stage.addChild(bmp);
}
}

Responsive Absolute Positioning

I am trying to add a hover effect to each speech bubble in the image below that link to a different page when clicked on.
Currently, I am using an image map and whenever an area of is hovered over jQuery changes the whole image to be the appropriate image that has the speech bubble filled in. It kind of works but IE flickers every time a hover happens and the website is responsive so the image map does not scale when the screen size is changed. I've tried using https://github.com/stowball/jQuery-rwdImageMaps too but it doesn't seem to work on a mobile device.
Ideally I'd like to be able to have the speech bubbles be separate images that are positioned correctly when scaled so I can manipulate the speech bubbles easier.
Screenshot
You could go with approach which uses scaled coordinates.
Here's the basic idea:
var coordManager = {
"imageBaseHeight":800,
"imageBaseWidth":800,
"imageID":"myImg",
"baseCoordinateActions" = [
{"x":10,"y":10,"h":100,"w":100, "text": "Mousing over first option"}, // the square you want the mouseover to cover for a given action
{"x":200,"y":200,"h":100,"w":100, "text": "Mousing over first option"}
],
"scaledCoordinateActions" : [], // this should contain the baseCoordinateActions with the scaled values
"init" : function(id, baseHeight, baseWidth)
{
var self=this;
this.imageID=id;
this.imageBaseHeight=baseHeight;
this.imageBaseWith=baseWidth;
var img = document.getElementById(id);
img.onresize =function()
{
// regenerate the scaled coordinates based on the difference between the imageBaseHeight and the current height;
// usually you can get the scale by dividing the imageBaseHeight by the actual height
};
image.onmousemove = function(event)
{
// check the mouse location based on scaled coordinates if it's within the scaled coordinates of any of the scaledCoordinateAction items
// display that scaleCoordinateAction item's text using the current eventX and Y, or do it relative to the coordinateACtion X and Y.
// make sure to check if the bubble is already showing before changing the display: property to avoid flicker.
}
}
};
coordManager.init("imageID", 800,800);

Reloading an image after zooming in KineticJS

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.

Javascript Loading Image

I've created a html5 web app to display data, and to mimmic swipe gestures I've used A Jquery plugin Called Wipetouch. When a wipe gesture is triggered, all I do is redraw all of my data with new numbers via a javascript function. I've realized that this isn't the optimal solution as the images are static, and are currently being loaded every time I swipe. Any ideas would be great.
edit
var img01 = new Image();
enter code here
img01.onload = function () {
ctx.drawImage(img01, x, y, img01.width * 2, img01.height * 2);
ctx.fillStyle = "white";
//draw text
ctx.font = "bold 28pt Calibri";
ctx.fillText(monthname[date.getMonth()], x+51, y+135);
ctx.fillText(d[1], x+47, y+37);
}
img01.src = 'images/retail_car.png';
I apologize for not making this clear earlier. I'm drawing the images on my canvas, and this code is triggered each time the wipetouch plugin registers a swipe. I'd like to make everything stay in the canvas, so the CSS fix that was mentioned won't work in my case.
You could put the images in your html and give them a class in css that has display:none;. Then when you call the function you could change the class of the displayed image to one with display: block; or however you need them displayed. Just be sure to change the class back after you swipe so that the new image appears and the old image is no longer visible. That way they are not being generated over again each time you call the swipe.

Categories