how to add only padding top and padding bottom to Konva Text - react-konva

I want to just give padding top and padding bottom to my Text in canvas but when I give padding={20}, it gives padding to all the 4 sides.
Can you plase help me to get rid of left/right paddings.

At the current version 8.3.5, Konva does't support that directly.
As a workaround, you can use Konva.Rect for bounding box and transforming target. Then you can proxy rectangle changes to text object.
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const text = new Konva.Text({
x: 100,
y: 50,
width: 100,
text: 'hello, this is a long test for testing bounding boxes'
})
layer.add(text);
const padding = 20;
const rect = new Konva.Rect({
x: text.x(),
y: text.y() - padding,
height: text.height() + padding * 2,
width: text.width(),
fill: 'rgba(0, 255, 0, 0.05)',
draggable: true
});
layer.add(rect);
const tr = new Konva.Transformer({
nodes: [rect],
rotateEnabled: false,
enabledAnchors: ['middle-left', 'middle-right']
})
layer.add(tr);
rect.on('dragmove transform', () => {
text.setAttrs({
x: rect.x(),
y: rect.y() + padding,
width: rect.width() * rect.scaleX()
});
rect.height(text.height() + padding * 2);
})
<script src="https://unpkg.com/konva#^8/konva.min.js"></script>
<div id="container"></div>

Related

How to trace a click on the stroke in Konva-react

Can you tell me how to track the click only on the stroke and not inside the body of the figure. The shapes in which you want to trace this can be anything: Star, Rect, etc. Perhaps you have encountered this.
I can't even imagine how it could be done.
You need to draw a shape with fillEnabled: false so only its stroke is visible. And only its stroke will listen to events. If you want to keep fill, then you have to use two shapes. One for fill, another for stroke and events detection.
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const shape = new Konva.Circle({
x: stage.width() / 2,
y: stage.height() / 2,
radius: 50,
stroke: 'green',
strokeWidth: 10,
fillEnabled: false
});
layer.add(shape);
shape.on('click', () => {
console.log('shape click');
})
<script src="https://unpkg.com/konva#^8/konva.min.js"></script>
<div id="container"></div>

Using Matter.js, how to position SVG paths in a compound body?

I'm trying to create a capital letter "A" as an SVG shape for use with Matter.js but the letter shape displays incorrectly.
CodePen here and duplicated here:
function percentX(percent) {
return Math.round((percent / 100) * window.innerWidth);
}
function percentY(percent) {
return Math.round((percent / 100) * window.innerHeight);
}
const Engine = Matter.Engine,
Bodies = Matter.Bodies,
Body = Matter.Body,
Svg = Matter.Svg,
Vertices = Matter.Vertices,
Composite = Matter.Composite,
Render = Matter.Render,
Runner = Matter.Runner;
// create an engine
const engine = Engine.create(),
world = engine.world;
// create a renderer
const render = Render.create({
element: document.body,
engine: engine,
options: {
wireframes: false,
showInternalEdges: false,
width: percentX(100),
height: percentY(100),
background: "transparent"
}
});
let bodies = [],
bgColor = "#0A0618";
// SVGs
let vertexSets = [],
svgLetter,
svgLetterLegOne,
svgLetterLegTwo,
svgLetterCounter;
let letterX = percentX(60);
let letterY = percentY(20);
let letterXLegOne = percentX(60) - 40;
let letterYLegOne = percentY(20) + 40;
let letterXLegTwo = percentX(60) + 40;
let letterYLegTwo = percentY(20) + 40;
let letterSize = (window.innerWidth / 1000);
// A
// silhouette test (incorrectly displaying Batman ears)
$('#svg-test').find('path').each(function(i, path) {
svgTest = Bodies.fromVertices(
percentX(30),
letterY,
Vertices.scale(Svg.pathToVertices(path, 10),
letterSize,
letterSize), {
render: {
fillStyle: "white",
strokeStyle: "white",
lineWidth: 2
}
}, true);
vertexSets.push(svgTest);
});
// letter base shape
$('#svg-3').find('path').each(function(i, path) {
svgLetter = Bodies.fromVertices(
letterX,
letterY,
Vertices.scale(Svg.pathToVertices(path, 10),
letterSize,
letterSize), {
render: {
fillStyle: "yellow",
strokeStyle: "yellow",
lineWidth: 2
}
}, true);
vertexSets.push(svgLetter);
});
// left leg
$('#svg-3-leg-1').find('path').each(function(i, path) {
svgLetterLegOne = Bodies.fromVertices(
letterXLegOne,
letterYLegOne,
Vertices.scale(Svg.pathToVertices(path, 10),
letterSize,
letterSize), {
render: {
fillStyle: "green",
strokeStyle: "green",
lineWidth: 2,
isStatic: true
}
}, true);
vertexSets.push(svgLetterLegOne);
});
// right leg
$('#svg-3-leg-2').find('path').each(function(i, path) {
svgLetterLegTwo = Bodies.fromVertices(
letterXLegTwo,
letterYLegTwo,
Vertices.scale(Svg.pathToVertices(path, 10),
letterSize,
letterSize), {
render: {
fillStyle: "blue",
strokeStyle: "blue",
lineWidth: 2,
isStatic: true
}
}, true);
vertexSets.push(svgLetterLegTwo);
});
// counter (hole in the center), no need for offset repositioning
$('#svg-3-counter').find('path').each(function(i, path) {
svgLetterCounter = Bodies.fromVertices(
letterX,
letterY,
Vertices.scale(Svg.pathToVertices(path, 10),
letterSize,
letterSize), {
render: {
fillStyle: bgColor,
strokeStyle: bgColor,
lineWidth: 2
}
}, true);
vertexSets.push(svgLetterCounter);
});
// create compound body for letter "A"
var compoundBodyA = Body.create({
parts: [svgLetter, svgLetterLegOne, svgLetterLegTwo, svgLetterCounter]
});
// add A and O compound bodies to the world
Composite.add(world, [
compoundBodyA
]);
// add all SVGs to the world
Composite.add(world, vertexSets);
// run the renderer
Render.run(render);
// create runner
const runner = Runner.create();
// run the engine
Runner.run(runner, engine);
// hold in place for testing
world.gravity.y = 0;
world.gravity.x = 0;
html {
box-sizing: border-box;
}
*, *::before, *::after {
box-sizing: inherit;
}
*:focus, *::before:focus, *::after:focus {
outline: none;
}
* {
font-family: monaco, courier;
}
body {
margin: 0;
padding: 0;
background: #0A0618;
}
svg {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/pathseg#1.2.1/pathseg.js"></script>
<script src="https://cdn.jsdelivr.net/npm/poly-decomp#0.3.0/build/decomp.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js"></script>
<svg class="svg-letter" id="svg-test">
<path class="st0" d="M59.3,0h46.4l59,141h-50.8l-7.4-18.8h-49L50.3,141h-50L59.3,0z"/>
</svg>
<svg class="svg-letter" id="svg-3">
<path d="M57.2,122.2H7.9L59,0h46.4l51.1,122.2h-50.3H57.2z"/>
</svg>
<svg class="svg-letter" id="svg-3-leg-1">
<path d="M0,141l7.9-18.8h49.3L50,141H0z"/>
</svg>
<svg class="svg-letter" id="svg-3-leg-2">
<path d="M106.2,122.2h50.3l7.9,18.8h-50.8L106.2,122.2z"/>
</svg>
<svg class="svg-letter" id="svg-3-counter">
<path d="M94.6,89L81.8,55L69,89H94.6z"/>
</svg>
I know Matter.js can't always handle SVGs with compound paths (an internal path creating a knockout) so my plan was to have two separate paths, the silhouette of the shape and the knockout, and group them as a compound body. Only the silhouette isn't even displaying correctly (the white version on the left). For some reason, the shape always has those Batman ears which I can't get rid of.
So my new plan is to break the silhouette shape into three parts, the main body (in yellow) and two legs (green and blue). That allows all three parts to have only four sides which seems to prevent the bug.
My problem is positioning those two legs so that they are always precisely butting up against the main body shape. I can adjust the position offset to accomplish this but since I've set the width and height of the render object to be proportional to the width and height of the browser, the letter breaks apart if the page is loaded in any other sized window.
Using variables (lines 45–54), I've tried setting the width and height of the legs to reference the main body shape with offsets:
svgThree.position.x - 40,
svgThree.position.y + 40,
And I've tried keeping all units and offsets proportional:
percentX(60) - percentX(2),
percentY(20) + percentX(2),
But nothing works. Without setting the letter to exact pixel dimensions, is there any way to keep these three paths touching and in precise relation to each other across different browser sizes?
Alternatively, if there's any way to build that SVG shape to avoid that bug, I'd greatly appreciate such a solution.
(Using Chrome Version 102.0.5005.115)
It's not a perfect solution but the one I eventually went with was this:
Setting the letter size to a static number (0.8), instead of a derivation of the window width, allowed me to set the width and height of the two legs of the letter to a static offset distance from the main body shape, so the change in the original CodePen above would be to lines 48–54:
let letterXLegOne = letterX - 43;
let letterYLegOne = letterY + 49;
let letterXLegTwo = letterX + 43;
let letterYLegTwo = letterY + 49;
let letterSize = 0.8;
The fix is incorporated into this CodePen as a final product.

width attribute not working with d3 style

I'm trying to display bar graphs with width of each bar representing age. but using style method in d3, i am unable to apply width attribute to the bars (as checked by doing inspect element in browser). other attributes applied using style are working fine.
function showData(clients){
let max = d3.max(clients, (d) => {
return (parseInt(d.age));
})
//console.log(max);
let scale = d3.scaleLinear().domain([0, parseInt(max)]).range([0, 100]);
//console.log(scale);
let join = container.selectAll('div').data(clients);
join.enter().append('div')
.text((d) => {
return d.name + ' : ' + scale(parseInt(d.age));
})
.style('background-color', 'blue')
.style('margin', '5px')
.style('color', 'white')
.style('width', (d) => {
return parseInt(d.age)
});
}
css width is not just a number:
width: 150px;
width: 20em;
width: 75%;
width: auto;
I believe you want:
.style('width', (d) => {
return parseInt(d.age) + 'px';
});
If you're applying width or height to svg or svg elements, use .attr() instead of .style(). In this case, you can use numbers, no px needed. If you don't specify any unit, it will be assumed to be px. If you're using width and height in style attribute, you must specify a unit, no matter what element.
const width = 400,
height = 200;
const svg = d3.select("svg")
.attr("width", width)
.attr("height", height)
.style("background", "steelblue")
.append("rect")
.attr("width", 100)
.attr("height", 200)
.attr("fill", "tomato");
<script src="https://unpkg.com/d3#6.3.1/dist/d3.min.js"></script>
<svg></svg>

change the legend.y property on browser resize

we use the highchart control with Angular and bootstrap.
To adjust the vertical space between the chart and the legend (both are rendered by highchart as svg elements), we set the y property of the legend on page load (in the controller) like this:
$scope.chartContracts = {
options: {
chart: {
type: 'pie',
marginBottom: 50
},
legend : {
layout: 'horizontal',
align: 'center',
verticalAlign: 'bottom',
x: 0,
y: 4,
Now in one of the Bootstraps layouts, the spacing (y) should be -10 in stead of 4 because for some reason an extra spacing is added.
So it should be based on media query or something similar I guess?
The question is how to do this... I tried with css but I can't seem to style SVG elements (they are called <g>)?
You can check window width and then return correct value.
var wWidth = $(window).width(),
value;
if(wWidth < 400) {
value = 5;
} else {
value = 10;
}
//scope
y: value

famo.us: Modify content in GridLayout

is it possible to modify the content of a surface (used within GridLayout) without using CSS? For example to center the text?
Basic example:
function createGrid( section, dimensions, menuData ) {
var grid = new GridLayout({
dimensions: dimensions
});
var surfaces = [];
grid.sequenceFrom(surfaces);
for(var i = 0; i < dimensions[1]; i++) {
surfaces.push(new Surface({
content: menuData[i].title,
size: [undefined, undefined],
properties: {
backgroundColor: "#ff0000",
color: "white",
textAlign: 'center',
}
}));
}
return grid;
}
I added the center property, but I also want to have the content in the middle of my surface. Do I have to use CSS or is there another way?
I tried adding another View/Surface within this Surface and added the align/origin modifier. Didn't work: I still had to adjust the origin/align values for the specific (browser) layout ...
I'm not so sure about you first question, but I can answer the second one.
text-alignhelps you horizontally center your content. So the problem is how to do it vertically.
The cleanest way to do it is to set the line-height the same as the containing div's height. In your case, there are two ways to do it:
1) calculate the height of the grid. For example, if you have a 9*9 GridLayout for the whole screen, then we will have gridHeight = window.innerHeight/9. Then you just need to add lineHeight: gridHeight to your properties object.
check http://jsfiddle.net/mrwiredancer/veLpbmmo/2/ for full exmaple
2) if you are not able to calculate the height of the grid beforehand, you can center a fixed-height(smaller than the grid's height) surface in the middle of the grid. For example, your GridLayout is contained in a dynamic view, but you're sure that your grid is no less than 20px high. Then you can do this:
var container, surface, __height = 20;
for(var i = 0; i < dimensions[1]; i++) {
container = new Container({ //View works as well
properties: {
backgroundColor: '#FF0000'
}
})
surface = new Surface({
content: menuData[i].title,
size: [undefined, __height],
properties: {
lineHeight: __height+'px', //same as surface's height
color: "white",
textAlign: 'center',
}
});
container.add(new Modifier({origin: [.5, .5]})).add(surface);
surfaces.push(container);
}
check http://jsfiddle.net/mrwiredancer/uxq30yp9/1/ for full example

Resources