In fabric.js, how do you adjust stroke thickness for the object selection box and control handles?
There's a bunch of customization options available, however it isn't clear on how you can customized stroke thickness. Is it possible, perhaps through an indirect way?
Note: The properties selectionColor, selectionBorderColor, selectionLineWidth are misleading... they have to do with the temporary selection box that appears when attempting to do a fresh drag-select across the canvas. Once your selection is made, it disappears and then you see the persistent object selection box with control handles (the thing I'm trying to customize).
See links:
http://fabricjs.com/customization
http://fabricjs.com/controls-customization
Ok here's a 2-part solution:
https://codepen.io/MarsAndBack/pen/bGExXzd
For the selection box stroke thickness:
Use fabric.Object.prototype.set to customize any object selection globally. Also, borderScaleFactor is documented, but not included in the fabric.js customization demos:
fabric.Object.prototype.set({
borderScaleFactor: 6
})
For the control handle stroke thickness:
Here we override differently, and actually draw new elements using standard HTML5 canvas properties. Through this method you could also target specific control handles and even use image icons.
fabric.Object.prototype._drawControl = controlHandles
fabric.Object.prototype.cornerSize = 20
function controlHandles (control, ctx, methodName, left, top) {
if (!this.isControlVisible(control)) {
return
}
var size = this.cornerSize
// Note 1: These are standard HTML5 canvas properties, not fabric.js.
// Note 2: Order matters, for instance putting stroke() before strokeStyle may result in undesired effects.
ctx.beginPath()
ctx.arc(left + size / 2, top + size / 2, size / 2, 0, 2 * Math.PI, false);
ctx.fillStyle = "pink"
ctx.fill()
ctx.lineWidth = 4 // This is the stroke thickness
ctx.strokeStyle = "red"
ctx.stroke()
}
SO code snippet:
const canvas = new fabric.Canvas("myCanvas")
canvas.backgroundColor="#222222"
this.box = new fabric.Rect ({
width: 240,
height: 100,
fill: '#fff28a',
myType: "box"
})
canvas.add(this.box)
this.box.center()
// Selection box border properties
// ----------------------------------------
fabric.Object.prototype.set({
borderColor: "white",
borderScaleFactor: 6
})
// Control handle properties
// ----------------------------------------
fabric.Object.prototype._drawControl = controlHandles
fabric.Object.prototype.cornerSize = 20
function controlHandles (control, ctx, methodName, left, top) {
if (!this.isControlVisible(control)) {
return
}
var size = this.cornerSize
// Note 1: These are standard HTML5 canvas properties, not fabric.js.
// Note 2: Order matters, for instance putting stroke() before strokeStyle may result in undesired effects.
ctx.beginPath()
ctx.arc(left + size/2, top + size/2, size/2, 0, 2 * Math.PI, false);
ctx.fillStyle = "pink"
ctx.fill()
ctx.lineWidth = 4
ctx.strokeStyle = "red"
ctx.stroke()
}
<script src="https://pagecdn.io/lib/fabric/3.6.3/fabric.min.js"></script>
<canvas id="myCanvas" width="700" height="400"></canvas>
Related
What is the workaround to create 3 buttons with the same design but different texts (short text, medium length text, long text):
a) do I need 3 different images as a background ? What if i don’t know the length of the text ?
b) or I can use a single image and shrink/stretch it horizontally/vertically somehow when a text needs more/less space to fit in?
I hope i don’t need hundreds of images for each button :)
I’m just a beginner. What is the best practice ?
Here is how I create a responsive button and text:
buttonSprite2 = game.add.sprite(352, 76,'button');
buttonSprite2.position.set(200, 0);
buttonSprite2.inputEnabled = true;
buttonSprite2.events.onInputDown.add(listener2, this);
var style = { font: "32px Arial", fill: "#ffffff", wordWrap: true, align: "center", backgroundImage:'button'};
buttonText2 = game.add.text(0, 0, "stop text", style);
buttonText2.wordWrapWidth = game.world.width - 400;
// trying to center the text within the button
buttonText2.position.set(buttonSprite2.x + 100, buttonSprite2.y+15);
// trying to make the button responsive
if(buttonText2.width < game.world.width - 400){
buttonSprite2.width = buttonText2.width + 200;
}
else{
buttonSprite2.width = game.world.width - 300;
buttonSprite2.height = buttonText2.height + 30;
}
You need to load(in preload function) image only once. But you need to add(in create function) 3 images for 3 buttons.
For changing size of this button images according to text size. First you add image then add text. Now, after adding text, check for height or width of that text object and change size of the button image according to the height and width of text.
You may define seperate function to do this. So, you can get rid of redundant code if you have too many buttons.
I have an issue regarding a kendo dialog window including an upload.
When I upload multiple elements the list gets longer and longer and at some point gets bigger than my browserwindow and runs out of bounds at the bottom of the screen.
The insert button then is below the bottom bound of the browser but the browser doesn't allow me to scroll (firefox + chrome).
How can I limit my window to the browser screen and not exceed it?
#(Html.Kendo().Dialog()
.Name("ImageBrowser")
.Content( Html.Partial("ImageBrowserContent").ToString())
.MinWidth(400)
.MinHeight(800)
.MaxHeight(1000)
.MaxWidth(800)
.Modal(true)
.Visible(false)
)
#(Html.Kendo().Upload()
.Name("imageUpload")
.Messages(mess => mess.Select("Upload"))
.Async(a => a
.Save("Upload", "Image")
.Remove("RemoveUpload", "Image")
.AutoUpload(true)
)
)
I have provided a dojo here for you which hopefully is what you are after.
https://dojo.telerik.com/eqaZibIL
It uses the javascript version but you can just apply the function I have created to the dialog initial event. like so:
#Html.Kendo().Dialog().Events(e => e.InitOpen("dialog_resize"))
(This is some code I have modified for handling kendoWindow's so could be optimized more I think)
function dialog_resize() {
//THIS GETS THE POPUP DIALOG
var popUpDialog = $('#dialog').data("kendoDialog");
//THIS GETS THE CONTENT AREA FOR THE ENTIRE DIALOG
var contentArea = $(".k-widget.k-window.k-dialog");
//THIS GETS THE ACTUAL CONTENT AREA OF THE DIALOG IE.WHAT YOU WANT DISPLAYED
var innerForm = $("#dialog");
var windowHeight = $(window).innerHeight();
var windowWidth = $(window).innerWidth();
//CALCULATE THE WIDTH OF THE DIALOG AND SET IT TO 80%
//OF SCREEN REALESTATE TO STRECH OUT.
//NOT REQUIRED IF YOU ARE SETTING THE WIDTH MANUALLY.
contentArea.width(windowWidth * 0.8)
//CENTER THE DIALOG ON THE SCREEN.
popUpDialog.center();
//GET 97% OF THE AVAILABLE DIALOG CONTENT AREA TO SHOW Y-SCROLL BAR
var fixedHeight = (contentArea.height() * 0.97);
var fixedWidth = contentArea.width() * 0.965;
//SET THE HEIGHT, WIDTH AND SCROLL BAR OF THE CONTENT AREA
//SHOWING BLUE BORDER TO SHOW THE ITEM IS POSITIONING CORRECTLY IN DIALOG
innerForm.height(fixedHeight).width(fixedWidth).css({
maxHeight: fixedHeight + 'px !important',
maxWidth: fixedWidth + 'px !important',
overflowY: 'scroll',
overflowX: 'hidden',
border: '1px solid blue'
});
}
Hopefully you can follow what is going off here.
As long as you have set the maxHeight for the dialog on initialization all this seems to work correctly. I have applied a blue border to the content area just for demo purposes so you can see it in action.
I want to add a label that doesn't always face the camera. Instead, I want it to follow a defined path. Similar to how street names follow the direction of their streets in google maps (they aren't always horizontal).
I can think of 2 possible implementations for rotating text but haven't had any luck.
That Label() or label : have a rotation property I haven't found. IE something like this:
viewer.entities.add({
position : Cesium.Cartesian3.fromDegrees(-75.1641667, 39.9522222),
label : {
text : 'Philadelphia'
//rotation : Cesium.Math.toRadians(-45)
}
});
or this
var labels = scene.primitives.add(new Cesium.LabelCollection());
var l = labels.add({
position : Cesium.Cartesian3.fromRadians(longitude, latitude, height),
text : 'Hello World',
font : '24px Helvetica'
//rotation: Cesium.Math.toRadians(-45)
});
Create Pictures of each label in photoshop and import them as an image, then rotate the image (or use it as a material and rotate the entity). Very labor intensive if you have a lot of labels (like street names).
Or perhaps there is a way for cesiumjs to recognize text as a fixed position 2D object and skew it appropriately as the view angle changes.
Any ideas?
If your text doesn't change, You can use an image, and load it by Cesium.SingleTileImageryProvider .
If your text does change, you can use a billboard.image, set an HTML canvas to it, and rotate the canvas like so:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.font = "20px Georgia";
ctx.fillText("Hello World!", 10, 50);
ctx.font = "30px Verdana";
// Create gradient
var gradient = ctx.createLinearGradient(0, 0, c.width, 0);
gradient.addColorStop("0", "magenta");
gradient.addColorStop("0.5", "blue");
gradient.addColorStop("1.0", "red");
// Fill with gradient
ctx.rotate(20*Math.PI/180);
ctx.fillStyle = gradient;
ctx.fillText("Big smile!", 10, 90);
billboard.image = ctx;
The only way I know how to rotate a label is like #Henri Aloni said with canvas.
Cesium has already a function called: writeTextToCanvas.
example in typescript :
viewer.entities.add({
position: Cartesain3.fromDegrees(34, 32, 0),
billboard: {
image: writeTextToCanvas('baruch', {
font: '30px sans-serif'
}),
rotation: 45
}
});
My application involves drawing of custom shapes, in which the details of the shapes are retrieved from the database. Shapes will be Lines, Arcs.
I will calling DrawArc and DrawLine function to draw the custom shapes.
My Problem is I need to fill this custom shape with a background color.
I am using the following namespaces.
using System.Drawing.Drawing2D;
using System.Drawing;
Things that I tried out:
Graphics.FillRegion Method
private void OnPaint(object sender, PaintEventArgs e)
{
Pen redPen = new Pen(Color.Red, 2);
// Create a graphics path
GraphicsPath path = new GraphicsPath();
// Add two lines, a rectangle and an ellipse
Graphics g = e.Graphics;
path.StartFigure();
path.AddLine(20, 400, 20, 20); // left
path.AddLine(20, 20, 400, 20); //top
path.AddLine(400, 20, 400, 400); // right
path.AddLine(400, 400, 20, 400); // bottom
path.CloseFigure();
g.DrawPath(redPen, path);
Region reg = new Region(path);
g.FillRegion(Brushes.Green, reg);
reg.Dispose();
g.Dispose();
}
Using this method it is easy to fill a simple shape, where the start and end points are clearly defined.
But my application involves complex shapes, which is difficult to track the points. If I use this method, filling of background color is not proper.
Pls suggest me some way of filling custom shapes.
thanks for your help.
I need to 'scale' text to fill an antire HTML5 canvas. It appears that the scale() method does not affect text. I've seen approximation methods with iterative loops on the measureText() method but this doesn't get me exactly what I need. Ideally I'd like to fill both horizontally and vertically without conserving the aspect ratio. Would SVG possibly be able to help with this?
My bad - Scale DOES apply to text. I've come up with a solution:
context.font = "20px 'Segoe UI'";
var metrics = context.measureText("Testing!");
var textWidth = metrics.width;
var scalex = (canvas.width / textWidth);
var scaley = (canvas.height / 23);
var ypos = (canvas.height / (scaley * 1.25));
context.scale(scalex, scaley);
context.fillText("Testing!", 0, ypos);
Scale does affect text. Try this:
var can = document.getElementById('test');
var ctx = can.getContext('2d');
ctx.fillText("test", 10, 10); // not scaled text
ctx.scale(3,3);
ctx.fillText("test", 10, 10); // scaled text
See it in action here:
http://jsfiddle.net/3zeBk/8/