簡體   English   中英

Javascript - HTML Canvas上的Gecko邊框半徑自適應(CSS border-radius)

[英]Javascript - Gecko border radius adaptation on HTML Canvas (CSS border-radius)

我試圖弄清楚如何將“border-radius”css屬性的行為重現為HTML畫布。

所以我已經在Javascript中做了一些事情,以便使用特定的半徑(每個角落)計算給定形狀的正確邊框。

如果需要,這是上一個問題: Gecko - CSS布局邊界半徑 - Javascript轉換

我已經設法接近瀏覽器改編,但仍然有一個問題,它似乎是最后一個,也是最難的部分!

我們舉一個例子來解釋你的問題

采用100px寬度和100px高度的形狀。 現在將以下半徑應用於每個角:

  • 左上角:37px
  • 右上:100px
  • 右下:1px
  • 左下:100px

所以這個css樣式將是border-radius : 37px 100px 1px 100px

現在讓我們考慮使用下面的代碼在畫布中繪制這個形狀。

通過使用函數correctRadius(r, w, h)以避免不良形狀,每個角將按如下方式計算:

=> {tl: 18.5, tr: 81.5, br: 0.5, bl: 81.5}

這是一個視覺效果:

calc問題css邊界半徑畫布

您將能夠在以下代碼段中對其進行測試

如您所見,瀏覽器形狀(綠色)與畫布形狀重疊(由於不透明度,棕色+'粉紅色')。 為了檢查壞角(粉紅色),我在它上面添加了一些不透明度。

棕色形狀不適合綠色形狀, 左上角 離開底部,左下角和右上角不適合綠色形狀。

我已經嘗試解決這個問題,但沒有成功,並且還查看了此文件中Gecko布局引擎( https://github.com/mozilla/gecko-dev )的來源: layout/painting/nsCSSRenderingBorders.cpp ,但是沒有找到任何東西+我沒有C ++的技能


如果有人能幫助我解決這個問題,或者給我一些建議,我將能夠解決這個問題並使邊界有效

 // Ctx var ctx = document.getElementById("rounded-rect").getContext("2d"); ctx.translate(0, 0); 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 last = []; setInterval(function() { /* 1.Top left */ /* 2. Top right */ /* 3. Bottom right */ /* 4. Bottom left */ var bordersCSSProps = [ "border-top-left-radius", "border-top-right-radius", "border-bottom-right-radius", "border-bottom-left-radius" ], elementBorders = [], elementStyle = getComputedStyle(copy); var changed = false; for (var i = 0; i < 4; ++i) { elementBorders[i] = elementStyle.getPropertyValue(bordersCSSProps[i]); if (elementBorders[i] !== last[i]) { changed = true; last[i] = elementBorders[i]; } } if (changed) { var borders = [].concat(elementBorders).map(function(a) { return parseInt(a) }); var rad = { tl: borders[0], tr: borders[1], br: borders[2], bl: borders[3] }; ctx.clearRect(0, 0, 600, 500); ctx.fillRoundedRect(120, 120, 100, 100, rad); } }, 1E3 / 60); function elemBordersSet() { var borders = [tl.value, tr.value, br.value, bl.value].join('px ') + 'px'; copy.style.borderRadius = borders; } tl.oninput = elemBordersSet; tr.oninput = elemBordersSet; bl.oninput = elemBordersSet; br.oninput = elemBordersSet; 
 html, body { margin: 0; padding: 0; } 
 <div style="display:inline-block; position: absolute; left:120px;top:120px; width: 100px; height: 100px; background:green; border-radius: 100px 49px 1px 1px;" id="copy"> </div> <canvas style="opacity:0.5; z-index:1; display: inline-block; position: absolute; left:0; top:0;" id="rounded-rect" width="600" height="500"> </canvas> <div style="margin-top:250px; position:absolute; z-index:5"> <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/> </div> 

找到了解決方案!

我們只需要計算scaleRatio

首先,我們得到maxRadiusWidth (r.tl + r.tr,r.bl + r.br)& maxRadiusHeight (r.tl + r.bl,r.tr + r.br)

然后widthRatio = (w / maxRadiusWidth)heightRatio = (h / maxRadiusHeight)與形狀的大小(WIDTH&HEIGHT)

然后我們取兩個變量中的較低者: Math.min(Math.min(widthRatio, heightRatio), 1)以便不脫離形狀並確保比率低於1。

最后 ,我們只需要將每個角乘以這個比例,以獲得正確的尺寸!

r.tl = tl*scaleRatio;
r.tr = tr*scaleRatio;
r.br = br*scaleRatio;
r.bl = bl*scaleRatio;

請參閱下面的代碼段;)

 // Ctx var ctx = document.getElementById("rounded-rect").getContext("2d"); ctx.translate(0, 0); function correctRadius(r, w, h) { var maxRadiusWidth = Math.max(r.tl + r.tr, r.bl + r.br), maxRadiusHeight = Math.max(r.tl + r.bl, r.tr + r.br), widthRatio = w / maxRadiusWidth, heightRatio = h / maxRadiusHeight, scaleRatio = Math.min(Math.min(widthRatio, heightRatio), 1); for (var k in r) r[k] = r[k] * scaleRatio; } //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 last = []; setInterval(function() { /* 1.Top left */ /* 2. Top right */ /* 3. Bottom right */ /* 4. Bottom left */ var bordersCSSProps = [ "border-top-left-radius", "border-top-right-radius", "border-bottom-right-radius", "border-bottom-left-radius" ], elementBorders = [], elementStyle = getComputedStyle(copy); var changed = false; for (var i = 0; i < 4; ++i) { elementBorders[i] = elementStyle.getPropertyValue(bordersCSSProps[i]); if (elementBorders[i] !== last[i]) { changed = true; last[i] = elementBorders[i]; } } if (changed) { var borders = [].concat(elementBorders).map(function(a) { return parseInt(a) }); var rad = { tl: borders[0], tr: borders[1], br: borders[2], bl: borders[3] }; ctx.clearRect(0, 0, 600, 500); ctx.fillRoundedRect(120, 120, 100, 200, rad); } }, 1E3 / 60); function elemBordersSet() { var borders = [tl.value, tr.value, br.value, bl.value].join('px ') + 'px'; copy.style.borderRadius = borders; } tl.oninput = elemBordersSet; tr.oninput = elemBordersSet; bl.oninput = elemBordersSet; br.oninput = elemBordersSet; 
 <div style="display:inline-block; position: absolute; left:120px;top:120px; width: 100px; height: 200px; background:green; border-radius: 33px 71px 40px 100px;" id="copy"> </div> <canvas style="z-index: 1;opacity:0.4;display: inline-block; position: absolute; left:0; top:0;" id="rounded-rect" width="600" height="500"> </canvas> <div style="position: absolute; z-index: 5;margin-top: 330px;"> <label> Top left <input type="range" min="1" max="500" value="0" class="slider" id="tl"></label><br/> <label> Top right <input type="range" min="1" max="500" value="0" class="slider" id="tr"></label><br/> <label> Bottom left <input type="range" min="1" max="500" value="0" class="slider" id="bl"></label><br/> <label> Bottom right <input type="range" min="1" max="500" value="0" class="slider" id="br"></label><br/> </div> 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM