Gecko - CSS layout border frame - Javascript transform

I am trying to get the exact formula to calculate the CSS border-radius property in the canvas. I have already tried and have an example in javascript (see below), but without success.

It seems that the browser is still adding some adaptations for border alignment. And I can’t identify them. So I checked the sources of the Gecko linking mechanism, but I'm not sure where I can find this formula (in the sources).

It may be in layout/painting/nsCSSRenderingBorders.cpp, but there is still a lot of code, and it is C ++ (I have no skills in this language)

See Gecko repository: https://github.com/mozilla/gecko-dev

So, if anyone can help me to achieve this adaptation, or give me blocks of code that compute "radius radius arcs", orientation? "(at gecko), I can do it.

Javascript / HTML fragment (current, close to a good adaptation)

(RED SHAPE = canvas shape, GREEN FORM = HTML shape)

I use this function to draw a figure: ctx.constructor.prototype.fillRoundedRect

And this function, to get closer to the adaptation of the browser: correctRadius

As you will see, I get this result when the TopLEFT, TopRight and BottomLEFT sliders are at their maximum value. The browser (green) displays it perfectly, and mine - bad (red).

See snippet below.

bad js border radius css result

// Ctx
var ctx = document.getElementById("rounded-rect").getContext("2d");

function correctRadius(r, w, h) {
  if (r.tl + r.tr > w) {
    r.tl -= (r.tl + r.tr - w) / 2;
    r.tr = w - r.tl;
  }
  if (r.bl + r.br > w) {
    r.br -= (r.br + r.bl - w) / 2;
    r.bl = w - r.br;
  }
  if (r.tl + r.bl > h) {
    r.tl -= (r.tl + r.bl - h) / 2;
    r.bl = h - r.tl;
  }
  if (r.tr + r.br > h) {
    r.tr -= (r.tr + r.br - h) / 2;
    r.br = h - r.tr;
  }
 }

//Round rect func
ctx.constructor.prototype.fillRoundedRect =
function (xx, yy, ww, hh, rad, fill, stroke) {
	correctRadius(rad, ww, hh);
	if (typeof(rad) === "undefined") rad = 5;
	this.beginPath();
	this.moveTo(xx, yy);
	this.arcTo(xx + ww, yy, xx + ww, yy + hh, rad.tr);
	this.arcTo(xx + ww, yy + hh, xx, yy + hh, rad.br);
	this.arcTo(xx, yy + hh, xx, yy, rad.bl);
	this.arcTo(xx, yy, xx + ww, yy, rad.tl);
	if (stroke) this.stroke();  // Default to no stroke
	if (fill || typeof(fill) === "undefined") this.fill();  // Default to fill
};

ctx.fillStyle = "red";
ctx.strokeStyle = "#ddf";

var copy = document.getElementById('copy');
var tl = document.getElementById('tl');
var tr = document.getElementById('tr');
var bl = document.getElementById('bl');
var br = document.getElementById('br');
var off = document.getElementById('off');

function test() {
ctx.clearRect(0, 0, 600, 500);


/* 1.Top left */
/* 2. Top right */
/* 3. Bottom right  */
/* 4. Bottom left */
var borders = [tl.value, tr.value, br.value, bl.value].join('px ') + 'px';

copy.style.borderRadius = borders;
var copyRad = borders.replace(/px/g, '').split(' ').map(function (a) {
	return parseInt(a)
});

var rad = {
	tl: copyRad[0],
	tr: copyRad[1],
	br: copyRad[2],
	bl: copyRad[3]
};
var o = +off.value;
ctx.fillRoundedRect(15 + o, 15 + o, 100, 100, rad);
}

tl.oninput = test;
tr.oninput = test;
bl.oninput = test;
br.oninput = test;
off.oninput = test;
test();
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
        html, body {
            margin: 0;
            padding: 0;
        }
    </style>
</head>
<body>


<div style="display:inline-block; position: absolute;
left:120px;top:120px; width: 100px; height: 100px; background:green;

border-radius: 10px 5px 10px 20px;" id="copy">

</div>

<canvas style="display: inline-block; position: absolute; zindex:0; left:0; top:0;" id="rounded-rect" width="600" height="500">

</canvas>


<div style="top: 300px; position:absolute; z-index: 1;">
    <label>
        Top left
        <input type="range" min="1" max="100" value="0" class="slider" id="tl"></label><br/>
    <label>
        Top right
        <input type="range" min="1" max="100" value="0" class="slider" id="tr"></label><br/>
    <label>
        Bottom left
        <input type="range" min="1" max="100" value="0" class="slider" id="bl"></label><br/>
    <label>
        Bottom right
        <input type="range" min="1" max="100" value="0" class="slider" id="br"></label><br/>
    <label>
        Offset
        <input type="range" min="1" max="200" value="0" class="slider" id="off"></label><br/>
</div>

</body>
</html>
Run codeHide result
+1
source share
1 answer

correctRadius. , , .

TopLeft, TopRight BottomLeft, (100px):

1) , :

TopLeft = 50px | TopRight = 50px ( )

2) , TL + TB (100px + 50px) > h (100px), :

TopLeft = 25px | BottomLeft 75px ( , 50 )

, , .


, . 3 :

TL = 100px | TR = 90px | BL = 100px | BR = 0px

TL TR BL:

  • TR, 190px > 100px , , TL = 45px
  • BL, 200px > 100px TL = 50px

, 50px. .

:

// Ctx
var ctx = document.getElementById("rounded-rect").getContext("2d");

function correctRadius(r, w, h) {
  var tl=r.tl;
  var tr=r.tr;
  var br=r.br;
  var bl=r.bl;

  r.tl -= Math.max(Math.max((tl + tr - w)/2,0),
                   Math.max((tl + bl - h)/2,0));
                   
  r.tr -= Math.max(Math.max((tr + tl - w)/2,0),
                   Math.max((tr + br - h)/2,0));
                    
  r.br -= Math.max(Math.max((br + bl - w)/2,0),
                   Math.max((br + tr - h)/2,0));
                   
  r.bl -= Math.max(Math.max((bl + br - w)/2,0),
                   Math.max((bl + tl - h)/2,0));

 }

//Round rect func
ctx.constructor.prototype.fillRoundedRect =
function (xx, yy, ww, hh, rad, fill, stroke) {
	correctRadius(rad, ww, hh);
	if (typeof(rad) === "undefined") rad = 5;
	this.beginPath();
	this.moveTo(xx, yy);
	this.arcTo(xx + ww, yy, xx + ww, yy + hh, rad.tr);
	this.arcTo(xx + ww, yy + hh, xx, yy + hh, rad.br);
	this.arcTo(xx, yy + hh, xx, yy, rad.bl);
	this.arcTo(xx, yy, xx + ww, yy, rad.tl);
	if (stroke) this.stroke();  // Default to no stroke
	if (fill || typeof(fill) === "undefined") this.fill();  // Default to fill
};

ctx.fillStyle = "red";
ctx.strokeStyle = "#ddf";

var copy = document.getElementById('copy');
var tl = document.getElementById('tl');
var tr = document.getElementById('tr');
var bl = document.getElementById('bl');
var br = document.getElementById('br');
var off = document.getElementById('off');

function test() {
ctx.clearRect(0, 0, 600, 500);


/* 1.Top left */
/* 2. Top right */
/* 3. Bottom right  */
/* 4. Bottom left */
var borders = [tl.value, tr.value, br.value, bl.value].join('px ') + 'px';

copy.style.borderRadius = borders;
var copyRad = borders.replace(/px/g, '').split(' ').map(function (a) {
	return parseInt(a)
});

var rad = {
	tl: copyRad[0],
	tr: copyRad[1],
	br: copyRad[2],
	bl: copyRad[3]
};
var o = +off.value;
ctx.fillRoundedRect(15 + o, 15 + o, 100, 100, rad);
}

tl.oninput = test;
tr.oninput = test;
bl.oninput = test;
br.oninput = test;
off.oninput = test;
test();
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
        html, body {
            margin: 0;
            padding: 0;
        }
    </style>
</head>
<body>


<div style="display:inline-block; position: absolute;
left:120px;top:120px; width: 100px; height: 100px; background:green;

border-radius: 10px 5px 10px 20px;" id="copy">

</div>

<canvas style="display: inline-block; position: absolute; zindex:0; left:0; top:0;" id="rounded-rect" width="600" height="500">

</canvas>


<div style="top: 300px; position:absolute; z-index: 1;">
    <label>
        Top left
        <input type="range" min="1" max="100" value="0" class="slider" id="tl"></label><br/>
    <label>
        Top right
        <input type="range" min="1" max="100" value="0" class="slider" id="tr"></label><br/>
    <label>
        Bottom left
        <input type="range" min="1" max="100" value="0" class="slider" id="bl"></label><br/>
    <label>
        Bottom right
        <input type="range" min="1" max="100" value="0" class="slider" id="br"></label><br/>
    <label>
        Offset
        <input type="range" min="1" max="200" value="0" class="slider" id="off"></label><br/>
</div>

</body>
</html>
Hide result
+1

Source: https://habr.com/ru/post/1692816/


All Articles